add: 多源音乐 优化日志相关功能

This commit is contained in:
sqj
2025-08-22 15:11:35 +08:00
parent 0e6dba8249
commit 77c586d65e
144 changed files with 20519 additions and 503 deletions

View File

@@ -2,3 +2,4 @@ singleQuote: true
semi: false
printWidth: 100
trailingComma: none
endOfLine: "auto"

View File

@@ -10,10 +10,20 @@ import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
plugins: [externalizeDepsPlugin()],
resolve: {
alias: {
'@common': resolve('src/common')
}
}
},
preload: {
plugins: [externalizeDepsPlugin()]
plugins: [externalizeDepsPlugin()],
resolve: {
alias: {
'@common': resolve('src/common')
}
}
},
renderer: {
plugins: [
@@ -44,7 +54,8 @@ export default defineConfig({
'@components': resolve('src/renderer/src/components'),
'@services': resolve('src/renderer/src/services'),
'@types': resolve('src/renderer/src/types'),
'@store': resolve('src/renderer/src/store')
'@store': resolve('src/renderer/src/store'),
'@common': resolve('src/common')
}
}
}

View File

@@ -1,50 +1,98 @@
import tseslint from '@electron-toolkit/eslint-config-ts'
import eslintConfigPrettier from '@electron-toolkit/eslint-config-prettier'
import eslintPluginVue from 'eslint-plugin-vue'
import vueParser from 'vue-eslint-parser'
export default tseslint.config(
{
ignores: [
'**/node_modules',
'**/dist',
'**/out',
'**/src/renderer/src/assets/icon_font/iconfont.js'
]
},
tseslint.configs.recommended,
eslintPluginVue.configs['flat/recommended'],
{
files: ['**/*.vue'],
languageOptions: {
parser: vueParser,
parserOptions: {
ecmaFeatures: {
jsx: true
},
extraFileExtensions: ['.vue'],
parser: tseslint.parser
const baseRule = {
'no-new': 'off',
camelcase: 'off',
'no-return-assign': 'off',
'space-before-function-paren': ['error', 'never'],
'no-var': 'error',
'no-fallthrough': 'off',
eqeqeq: 'off',
'require-atomic-updates': ['error', { allowProperties: true }],
'no-multiple-empty-lines': [1, { max: 2 }],
'comma-dangle': [2, 'always-multiline'],
'standard/no-callback-literal': 'off',
'prefer-const': 'off',
'no-labels': 'off',
'node/no-callback-literal': 'off',
'multiline-ternary': 'off',
}
}
},
{
files: ['**/*.{ts,mts,tsx,vue}'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'no-empty-pattern': 'off',
'vue/require-default-prop': 'off',
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-unsafe-function-return-type': 'off',
const typescriptRule = {
...baseRule,
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'vue/block-lang': [
'@typescript-eslint/space-before-function-paren': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/restrict-template-expressions': [1, {
allowBoolean: true,
allowAny: true,
}],
'@typescript-eslint/restrict-plus-operands': [1, {
allowBoolean: true,
allowAny: true,
}],
'@typescript-eslint/no-misused-promises': [
'error',
{
script: {
lang: 'ts'
}
}
]
}
checksVoidReturn: {
arguments: false,
attributes: false,
},
eslintConfigPrettier
)
},
],
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/comma-dangle': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
}
const vueRule = {
...typescriptRule,
'vue/multi-word-component-names': 'off',
'vue/max-attributes-per-line': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/use-v-on-exact': 'off',
}
exports.base = {
extends: ['standard'],
rules: baseRule,
parser: '@babel/eslint-parser',
}
exports.html = {
files: ['*.html'],
plugins: ['html'],
}
exports.typescript = {
files: ['*.ts'],
rules: typescriptRule,
parser: '@typescript-eslint/parser',
extends: [
'standard-with-typescript',
],
}
exports.vue = {
files: ['*.vue'],
rules: vueRule,
parser: 'vue-eslint-parser',
extends: [
// 'plugin:vue/vue3-essential',
'plugin:vue/base',
'plugin:vue/vue3-recommended',
'plugin:vue-pug/vue3-recommended',
// "plugin:vue/strongly-recommended"
'standard-with-typescript',
],
parserOptions: {
sourceType: 'module',
parser: {
// Script parser for `<script>`
js: '@typescript-eslint/parser',
// Script parser for `<script lang="ts">`
ts: '@typescript-eslint/parser',
},
extraFileExtensions: ['.vue'],
},
}

View File

@@ -40,20 +40,25 @@
"@pixi/filter-bulge-pinch": "^5.1.1",
"@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3",
"@types/needle": "^3.3.0",
"NeteaseCloudMusicApi": "^4.27.0",
"axios": "^1.11.0",
"color-extraction": "^1.0.8",
"crypto-js": "^4.2.0",
"dompurify": "^3.2.6",
"electron-log": "^5.4.3",
"electron-updater": "^6.3.9",
"iconv-lite": "^0.7.0",
"jss": "^10.10.0",
"jss-preset-default": "^10.10.0",
"marked": "^16.1.2",
"mitt": "^3.0.1",
"NeteaseCloudMusicApi": "^4.27.0",
"needle": "^3.3.1",
"node-fetch": "2",
"pinia": "^3.0.3",
"tdesign-vue-next": "^1.15.2",
"vue-router": "^4.5.1"
"vue-router": "^4.5.1",
"zlib": "^1.0.5"
},
"devDependencies": {
"@babel/plugin-transform-arrow-functions": "^7.27.1",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 请求响应状态: 200"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
error "[ceru 音乐插件] Error: 歌曲不存在"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2645500113,\"name\":\"跳楼机\",\"artist\":\"LBI利比\",\"album\":\"跳楼机\",\"pic_id\":\"109951170507596121\",\"url_id\":2645500113,\"lyric_id\":2645500113,\"source\":\"netease\",\"lyric\":\"[00:00.00] 作..."log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/1135545898"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/1135545898"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/1139605559"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/1139605559"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2003594562"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2003594562"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[CeruMusic] 响应不是JSON格式内容: ..."
error "[ceru 音乐插件] Error: 歌曲不存在"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: ..."
log "[CeruMusic] 请求响应内容: [object Object]"
error "[ceru 音乐插件] Error: 歌曲不存在"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2645500113,\"name\":\"跳楼机\",\"artist\":\"LBI利比\",\"album\":\"跳楼机\",\"pic_id\":\"109951170507596121\",\"url_id\":2645500113,\"lyric_id\":2645500113,\"source\":\"netease\",\"lyric\":\"[00:00.00] 作..."
error "[ceru 音乐插件] Error: 歌曲不存在"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2645500113,\"name\":\"跳楼机\",\"artist\":\"LBI利比\",\"album\":\"跳楼机\",\"pic_id\":\"109951170507596121\",\"url_id\":2645500113,\"lyric_id\":2645500113,\"source\":\"netease\",\"lyric\":\"[00:00.00] 作..."
log "[CeruMusic] 请求响应内容: [object Object]"
error "[ceru 音乐插件] Error: 歌曲不存在"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2149456401,\"name\":\"alone.\",\"artist\":\"DLSS\",\"album\":\"alone.\",\"pic_id\":\"109951169530908047\",\"url_id\":2149456401,\"lyric_id\":2149456401,\"source\":\"netease\",\"lyric\":\"[00:00...."
log "[CeruMusic] 请求响应内容: [object Object]"
error "[ceru 音乐插件] Error: 歌曲不存在"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2003594562,\"name\":\"天晴\",\"artist\":\"是二智呀\",\"album\":\"天晴\",\"pic_id\":\"109951168114308737\",\"url_id\":2003594562,\"lyric_id\":2003594562,\"source\":\"netease\",\"lyric\":\"[00:00.06]作词:陈澈..."
error "[ceru 音乐插件] Error: 歌曲不存在"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[ceru 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2645500113"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/254574
log [CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/254574
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"status":true,"song_data":{"id":254574,"name":"后来","artist":"刘若英","album":"我等你","pic_id":"109951163351825356","url_id":254574,"lyric_id":254574,"source":"netease","lyric":"[00:00.000] 作词 : 施人诚\n[00:01.000] 作曲 : 玉城千春\n[00:02.000] 编曲 : 王继康\n[00:03.000] 制作人 : 光良\n[00:12.571]后来 我总算学会了如何去爱\n[00:19.303]可惜你 早已远去 消失在人海\n[00:25.238]后来 终于在眼泪中明白\n[00:32.064]有些人 一旦错过就不在\n[00:39.800]栀子花 白花瓣\n[00:46.027]落在我蓝色百褶裙上\n[00:51.763]爱你 你轻声说\n[00:58.844]我低下头 闻见一阵芬芳\n[01:05.371]那个永恒的夜晚\n[01:09.341]十七岁仲夏\n[01:12.104]你吻我的那个夜晚\n[01:18.232]让我往后的时光\n[01:21.749]每当有感叹\n[01:24.862]总想起当天的星光\n[01:30.992]那时候的爱情\n[01:37.422]为什么就能那样简单\n[01:42.493]而又是为什么 人年少时\n[01:50.179]一定要让深爱的人受伤\n[01:56.301]在这相似的深夜里\n[02:00.566]你是否一样 也在静静追悔感伤\n[02:09.350]如果当时我们能\n[02:13.015]不那么倔强\n[02:16.079]现在也 不那么遗憾\n[02:21.053]你都如何回忆我\n[02:24.014]带着笑或是很沉默\n[02:27.231]这些年来\n[02:28.837]有没有人能让你不寂寞\n[02:33.455]后来 我总算学会了如何去爱\n[02:40.238]可惜你 早已远去 消失在人海\n[02:46.318]后来 终于在眼泪中明白\n[02:53.042]有些人 一旦错过就不在\n[03:25.379]你都如何回忆我\n[03:28.132]带着笑或是很沉默\n[03:31.296]这些年来\n[03:32.953]有没有人能让你不寂寞\n[03:37.523]后来\n[03:39.530]我总算学会了如何去爱\n[03:44.301]可惜你 早已远去 消失在人海\n[03:50.174]后来 终于在眼泪中明白\n[03:57.104]有些人 一旦错过就不在\n[04:03.136]后来 我总算学会了如何去爱\n[02:59.120]\n[04:09.965]可惜你 早已远去 消失在人海\n[04:15.937]后来 终于在眼泪中明白\n[04:22.771]有些人 一旦错过就不在\n[04:29.598]永远不会再重来\n[04:35.510]有一个男孩爱着那个女孩\n[05:02.681]录音工程师 : 周建平 & Peter Chong (MAL)\n[05:03.781]混音工程师 : 王晋溢\n[05:04.881]录音室 : 捷奏 & Synchrosound (MAL)\n[05:05.981]混音室 : 白金录音室\n[05:06.111]吉他 : Jamie Wilson\n[05:07.222]和声编写 : 光良\n[05:08.333]和声 : 梁静茹\n[05:09.444]OP : Victor Music Publishing Co., Ltd.\n[05:10.555]ISRC TW-A45-99-62502\n","pic":"https://p3.music.126.net/eBF7bHnJYBUfOFrJ_7SUfw==/109951163351825356.jpg?param=300y300","url":"https://m701.music.126.net/20250822145154/db9d934a5c833fe5bbe68e21bfd4720f/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/14783185971/f717/93d1/d743/037c337c4123b835758717a3e2e9286b.mp3","size":13658950,"br":320}}
log [ceru 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/254574
log [CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/254574
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"status":true,"song_data":{"id":254574,"name":"后来","artist":"刘若英","album":"我等你","pic_id":"109951163351825356","url_id":254574,"lyric_id":254574,"source":"netease","lyric":"[00:00.000] 作词 : 施人诚\n[00:01.000] 作曲 : 玉城千春\n[00:02.000] 编曲 : 王继康\n[00:03.000] 制作人 : 光良\n[00:12.571]后来 我总算学会了如何去爱\n[00:19.303]可惜你 早已远去 消失在人海\n[00:25.238]后来 终于在眼泪中明白\n[00:32.064]有些人 一旦错过就不在\n[00:39.800]栀子花 白花瓣\n[00:46.027]落在我蓝色百褶裙上\n[00:51.763]爱你 你轻声说\n[00:58.844]我低下头 闻见一阵芬芳\n[01:05.371]那个永恒的夜晚\n[01:09.341]十七岁仲夏\n[01:12.104]你吻我的那个夜晚\n[01:18.232]让我往后的时光\n[01:21.749]每当有感叹\n[01:24.862]总想起当天的星光\n[01:30.992]那时候的爱情\n[01:37.422]为什么就能那样简单\n[01:42.493]而又是为什么 人年少时\n[01:50.179]一定要让深爱的人受伤\n[01:56.301]在这相似的深夜里\n[02:00.566]你是否一样 也在静静追悔感伤\n[02:09.350]如果当时我们能\n[02:13.015]不那么倔强\n[02:16.079]现在也 不那么遗憾\n[02:21.053]你都如何回忆我\n[02:24.014]带着笑或是很沉默\n[02:27.231]这些年来\n[02:28.837]有没有人能让你不寂寞\n[02:33.455]后来 我总算学会了如何去爱\n[02:40.238]可惜你 早已远去 消失在人海\n[02:46.318]后来 终于在眼泪中明白\n[02:53.042]有些人 一旦错过就不在\n[03:25.379]你都如何回忆我\n[03:28.132]带着笑或是很沉默\n[03:31.296]这些年来\n[03:32.953]有没有人能让你不寂寞\n[03:37.523]后来\n[03:39.530]我总算学会了如何去爱\n[03:44.301]可惜你 早已远去 消失在人海\n[03:50.174]后来 终于在眼泪中明白\n[03:57.104]有些人 一旦错过就不在\n[04:03.136]后来 我总算学会了如何去爱\n[02:59.120]\n[04:09.965]可惜你 早已远去 消失在人海\n[04:15.937]后来 终于在眼泪中明白\n[04:22.771]有些人 一旦错过就不在\n[04:29.598]永远不会再重来\n[04:35.510]有一个男孩爱着那个女孩\n[05:02.681]录音工程师 : 周建平 & Peter Chong (MAL)\n[05:03.781]混音工程师 : 王晋溢\n[05:04.881]录音室 : 捷奏 & Synchrosound (MAL)\n[05:05.981]混音室 : 白金录音室\n[05:06.111]吉他 : Jamie Wilson\n[05:07.222]和声编写 : 光良\n[05:08.333]和声 : 梁静茹\n[05:09.444]OP : Victor Music Publishing Co., Ltd.\n[05:10.555]ISRC TW-A45-99-62502\n","pic":"https://p3.music.126.net/eBF7bHnJYBUfOFrJ_7SUfw==/109951163351825356.jpg?param=300y300","url":"https://m701.music.126.net/20250822145155/4f1e01f33ad960791480b07624819f2a/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/14783185971/f717/93d1/d743/037c337c4123b835758717a3e2e9286b.mp3","size":13658950,"br":320}}
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.
log [CeruMusic] Plugin "ceru 音乐插件" loaded successfully.

View File

@@ -0,0 +1,100 @@
log [CeruMusic] Plugin "未知插件" loaded successfully.log [插件] 注册事件监听器: request
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/urlinfo/1.0.0
log [CeruMusic] 事件驱动插件初始化成功
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 503
log [CeruMusic] 请求响应内容: [object Object]
log [插件] 音源注册完成: kw,wy,mg,tx,kg
log [插件] 发送事件: inited [object Object]
error [CeruMusic] Request failed: Expected JSON response but got: text/html
log [CeruMusic] 响应不是JSON格式内容: <html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.26.1</center>
</body>
</html>
...
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应状态: 404
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应状态: 404
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/254574/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 kw 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/kw/2061973302/320k
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 请求响应内容: [object Object]

View File

@@ -0,0 +1,28 @@
log "[CeruMusic] Plugin \"LiHouse 音乐插件\" loaded successfully."log "[LiHouse 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/1135545898"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/1135545898"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[LiHouse 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2003594562"
log "[LiHouse 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2003594562"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: ..."
log "[CeruMusic] 请求响应内容: [object Object]"
error "[LiHouse 音乐插件] 请求失败: 歌曲不存在"
error "[LiHouse 音乐插件] Error: 歌曲不存在"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2149456401,\"name\":\"alone.\",\"artist\":\"DLSS\",\"album\":\"alone.\",\"pic_id\":\"109951169530908047\",\"url_id\":2149456401,\"lyric_id\":2149456401,\"source\":\"netease\",\"lyric\":\"[00:00...."
error "[LiHouse 音乐插件] Error: 歌曲不存在"
error "[LiHouse 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[LiHouse 音乐插件] 请求失败: 歌曲不存在"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2003594562,\"name\":\"天晴\",\"artist\":\"是二智呀\",\"album\":\"天晴\",\"pic_id\":\"109951168114308737\",\"url_id\":2003594562,\"lyric_id\":2003594562,\"source\":\"netease\",\"lyric\":\"[00:00.06]作词:陈澈..."
error "[LiHouse 音乐插件] Error: 歌曲不存在"
log "[CeruMusic] 发起请求: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[LiHouse 音乐插件] 请求音乐链接: https://www.lihouse.xyz/coco_widget/music_resource/id/2149456401"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 响应不是JSON格式内容: {\"status\":true,\"song_data\":{\"id\":2149456401,\"name\":\"alone.\",\"artist\":\"DLSS\",\"album\":\"alone.\",\"pic_id\":\"109951169530908047\",\"url_id\":2149456401,\"lyric_id\":2149456401,\"source\":\"netease\",\"lyric\":\"[00:00...."
log "[CeruMusic] 请求响应内容: [object Object]"
error "[LiHouse 音乐插件] Error: 歌曲不存在"
error "[LiHouse 音乐插件] 请求失败: 歌曲不存在"

View File

@@ -0,0 +1,200 @@
end
log handleGetMusicUrl(kg_178240, hires) failed: Key失效/鉴权失败
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
start Handle Action(musicUrl)
log musicInfo {"name":"后来","singer":"刘若英","source":"kw","songmid":"96765035","albumId":"13825074","interval":"05:09","albumName":"2020 刘若英陪你 献上录音专辑","lrc":null,"img":"http://img1.kwcdn.kuwo.cn/star/albumcover/500/s4s68/34/3776980601.jpg","otherSource":null,"types":[{"type":"128k","size":"4.72Mb"},{"type":"320k","size":"11.80Mb"},{"type":"flac","size":"60.23Mb"},{"type":"flac24bit","size":"60.23Mb"}],"_types":{"flac24bit":{"size":"60.23MB"},"flac":{"size":"60.23MB"},"320k":{"size":"11.80MB"},"128k":{"size":"4.72MB"}},"typeUrl":{},"url":"http://cu.sycdn.kuwo.cn/226d5ab974a1d2ca5b5643e2354e7696/68a73db3/resource/s2/87/81/730236710.flac"}
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log quality hires
log source kw
log --- start --- https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log [CeruMusic] 请求响应状态: 200
log API Response: {"body":{"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":false,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:30:35 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["583"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.12571569811552763"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":false,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
end
log handleGetMusicUrl(kw_96765035, hires) success, URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac
log [ikun音源] Got URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flaclog 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 注册事件监听器: request
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:32:16 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0007874071598052979"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log checkUpdate success
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
start Handle Action(musicUrl)
log quality hires
log musicInfo {"name":"后来","singer":"刘若英","source":"kw","songmid":"96765035","albumId":"13825074","interval":"05:09","albumName":"2020 刘若英陪你 献上录音专辑","lrc":null,"img":"http://img1.kwcdn.kuwo.cn/star/albumcover/500/s4s68/34/3776980601.jpg","otherSource":null,"types":[{"type":"128k","size":"4.72Mb"},{"type":"320k","size":"11.80Mb"},{"type":"flac","size":"60.23Mb"},{"type":"flac24bit","size":"60.23Mb"}],"_types":{"flac24bit":{"size":"60.23MB"},"flac":{"size":"60.23MB"},"320k":{"size":"11.80MB"},"128k":{"size":"4.72MB"}},"typeUrl":{},"url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac"}
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log source kw
log --- start --- https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:32:17 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["582"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0030085640028119087"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
end
log handleGetMusicUrl(kw_96765035, hires) success, URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac
log [ikun音源] Got URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log [ikun音源 by Ceru插件] 注册事件监听器: request
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [CeruMusic] 事件驱动插件初始化成功
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:32:58 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0008850144222378731"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log checkUpdate success
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
error [ikun音源] Error: args.map is not a function
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [ikun音源 by Ceru插件] 注册事件监听器: request
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:33:02 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0008127372711896896"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log checkUpdate success
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
error [ikun音源] Error: args.map is not a function
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 注册事件监听器: request
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:33:30 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0008348245173692703"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log checkUpdate success
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
error [ikun音源] Error: args.map is not a function
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [ikun音源 by Ceru插件] 注册事件监听器: request
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 请求响应状态: 200
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:34:27 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0008898386731743813"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log checkUpdate success
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
error [ikun音源] Error: args.map is not a functionlog [ikun音源] 使用事件驱动方式获取 kw 音源链接
error [ikun音源] Error: args.map is not a function
error [ikun音源] Error: args.map is not a function
log [ikun音源] 使用事件驱动方式获取 kg 音源链接
log [ikun音源] 使用事件驱动方式获取 kg 音源链接
error [ikun音源] Error: args.map is not a function
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 注册事件监听器: request
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","data":{"updateMsg":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:35:51 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["370"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0008251480758190155"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log [ikun音源 by Ceru插件] 发送事件: updateAlert {"log":"⚠️ 重要更新\n更新地址:\nhttps://api.ikunshare.com/script\n日志\n更换域名\nQQ群: https://qm.qq.com/q/okMS3ubs0E","updateUrl":"https://api.ikunshare.com/script"}
log checkUpdate success
log [ikun音源] 使用事件驱动方式获取 kg 音源链接
log quality hires
start groupStart--------- Handle Action(musicUrl)
log musicInfo {"singer":"周杰伦","name":"半岛铁盒","albumName":"八度空间","albumId":"961807","songmid":178240,"source":"kg","interval":"05:19","_interval":319,"img":"http://imge.kugou.com/stdmusic/480/20250221/20250221180703846658.jpg","lrc":null,"otherSource":null,"hash":"67BA8A4A0681F2078BC423CB13B904B7","types":[{"type":"128k","size":"4.88 MB","hash":"67BA8A4A0681F2078BC423CB13B904B7"},{"type":"320k","size":"12.19 MB","hash":"68DEA6329FB28534271943B3F97599DC"},{"type":"flac","size":"34.93 MB","hash":"9F0F140F8A9C3A0E639A9BF8680E80E9"},{"type":"flac24bit","size":"61.72 MB","hash":"2CCC7683F50BE90A0C407D6FF0F973D9"}],"_types":{"128k":{"size":"4.88 MB","hash":"67BA8A4A0681F2078BC423CB13B904B7"},"320k":{"size":"12.19 MB","hash":"68DEA6329FB28534271943B3F97599DC"},"flac":{"size":"34.93 MB","hash":"9F0F140F8A9C3A0E639A9BF8680E80E9"},"flac24bit":{"size":"61.72 MB","hash":"2CCC7683F50BE90A0C407D6FF0F973D9"}},"typeUrl":{}}
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=kg&songId=67BA8A4A0681F2078BC423CB13B904B7&quality=hires
log --- start --- https://api.ikunshare.com/url?source=kg&songId=67BA8A4A0681F2078BC423CB13B904B7&quality=hires
log source kg
log [CeruMusic] 请求响应状态: 403
log API Response: {"body":{"code":403,"message":"你还没给我上供","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":403,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:35:51 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["138"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.00015995558351278305"]}}
log [CeruMusic] 请求响应内容: {"code":403,"message":"你还没给我上供","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log handleGetMusicUrl(kg_178240, hires) failed: Key失效/鉴权失败
error [ikun音源] Error: Key失效/鉴权失败
end log [ikun音源] 使用事件驱动方式获取 kg 音源链接
start groupStart--------- Handle Action(musicUrl)
log source kg
log musicInfo {"singer":"周杰伦","name":"半岛铁盒","albumName":"八度空间","albumId":"961807","songmid":178240,"source":"kg","interval":"05:19","_interval":319,"img":"http://imge.kugou.com/stdmusic/480/20250221/20250221180703846658.jpg","lrc":null,"otherSource":null,"hash":"67BA8A4A0681F2078BC423CB13B904B7","types":[{"type":"128k","size":"4.88 MB","hash":"67BA8A4A0681F2078BC423CB13B904B7"},{"type":"320k","size":"12.19 MB","hash":"68DEA6329FB28534271943B3F97599DC"},{"type":"flac","size":"34.93 MB","hash":"9F0F140F8A9C3A0E639A9BF8680E80E9"},{"type":"flac24bit","size":"61.72 MB","hash":"2CCC7683F50BE90A0C407D6FF0F973D9"}],"_types":{"128k":{"size":"4.88 MB","hash":"67BA8A4A0681F2078BC423CB13B904B7"},"320k":{"size":"12.19 MB","hash":"68DEA6329FB28534271943B3F97599DC"},"flac":{"size":"34.93 MB","hash":"9F0F140F8A9C3A0E639A9BF8680E80E9"},"flac24bit":{"size":"61.72 MB","hash":"2CCC7683F50BE90A0C407D6FF0F973D9"}},"typeUrl":{}}
log --- start --- https://api.ikunshare.com/url?source=kg&songId=67BA8A4A0681F2078BC423CB13B904B7&quality=hires
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=kg&songId=67BA8A4A0681F2078BC423CB13B904B7&quality=hires
log quality hires
log [CeruMusic] 请求响应状态: 403
log API Response: {"body":{"code":403,"message":"你还没给我上供","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":403,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:36:55 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["138"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["8.029583841562271e-05"]}}
log handleGetMusicUrl(kg_178240, hires) failed: Key失效/鉴权失败
error [ikun音源] Error: Key失效/鉴权失败
end
log [CeruMusic] 请求响应内容: {"code":403,"message":"你还没给我上供","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log [ikun音源] 使用事件驱动方式获取 kw 音源链接
log source kw
log musicInfo {"name":"后来","singer":"刘若英","source":"kw","songmid":"96765035","albumId":"13825074","interval":"05:09","albumName":"2020 刘若英陪你 献上录音专辑","lrc":null,"img":"http://img1.kwcdn.kuwo.cn/star/albumcover/500/s4s68/34/3776980601.jpg","otherSource":null,"types":[{"type":"128k","size":"4.72Mb"},{"type":"320k","size":"11.80Mb"},{"type":"flac","size":"60.23Mb"},{"type":"flac24bit","size":"60.23Mb"}],"_types":{"flac24bit":{"size":"60.23MB"},"flac":{"size":"60.23MB"},"320k":{"size":"11.80MB"},"128k":{"size":"4.72MB"}},"typeUrl":{},"url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac"}
log --- start --- https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=kw&songId=96765035&quality=hires
log quality hires
start groupStart--------- Handle Action(musicUrl)
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","url":"http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac","info":{"id":"96765035","name":"后来","album":"2020 刘若英陪你 献上录音专辑","artist":"刘若英"},"ekey":null,"cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 12:15:35","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:36:56 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["582"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.002598297782242298"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log handleGetMusicUrl(kw_96765035, hires) success, URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac
end
log [ikun音源] Got URL: http://cu.sycdn.kuwo.cn/f9678d8cdc643bcd8ff61bc61099540d/68a7e45b/resource/s2/87/81/730236710.flac
log [ikun音源] 使用事件驱动方式获取 mg 音源链接
log source mg
start groupStart--------- Handle Action(musicUrl)
log musicInfo {"singer":"音阙诗听","name":"红昭愿 (伴奏)","albumName":"诗","albumId":"1140686463","songmid":"1135545898","copyrightId":"69069801208","source":"mg","interval":"02:53","img":"http://d.musicapp.migu.cn/data/oss/resource/00/4g/u6/5fa3dbc0f6584fa88ba2c073f52d7330.webp","lrc":null,"lrcUrl":"https://d.musicapp.migu.cn/data/oss/resource/00/4s/oi/6972a559ab2d4162b649ac12646c9678","types":[{"type":"128k","size":"2.62 MB"},{"type":"320k","size":"6.56 MB"}],"_types":{"128k":{"size":"2.62 MB"},"320k":{"size":"6.56 MB"}},"typeUrl":{},"url":"http://freetyst.nf.migu.cn/public/product9th/product47/2025/03/1216/2025%E5%B9%B401%E6%9C%8820%E6%97%A514%E7%82%B940%E5%88%86%E7%B4%A7%E6%80%A5%E5%86%85%E5%AE%B9%E5%87%86%E5%85%A5%E6%88%90%E9%83%BD%E8%B0%A6%E4%BF%AE%E6%88%90%E6%9C%AC502%E9%A6%96015401/%E6%A0%87%E6%B8%85%E9%AB%98%E6%B8%85/MP3_128_16_Stero/69069801208160124.mp3"}
log quality hires
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=mg&songId=1135545898&quality=hires
log --- start --- https://api.ikunshare.com/url?source=mg&songId=1135545898&quality=hires
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","url":"http://freetyst.nf.migu.cn/public/product9th/product47/2025/03/1216/2025%E5%B9%B401%E6%9C%8820%E6%97%A514%E7%82%B940%E5%88%86%E7%B4%A7%E6%80%A5%E5%86%85%E5%AE%B9%E5%87%86%E5%85%A5%E6%88%90%E9%83%BD%E8%B0%A6%E4%BF%AE%E6%88%90%E6%9C%AC502%E9%A6%96015401/%E6%A0%87%E6%B8%85%E9%AB%98%E6%B8%85/MP3_128_16_Stero/69069801208160124.mp3","info":{"id":"1135545898","name":"红昭愿 (伴奏)","album":"诗","artist":"音阙诗听"},"ekey":"","cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":null,"canExpire":false},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","url":"http://freetyst.nf.migu.cn/public/product9th/product47/2025/03/1216/2025%E5%B9%B401%E6%9C%8820%E6%97%A514%E7%82%B940%E5%88%86%E7%B4%A7%E6%80%A5%E5%86%85%E5%AE%B9%E5%87%86%E5%85%A5%E6%88%90%E9%83%BD%E8%B0%A6%E4%BF%AE%E6%88%90%E6%9C%AC502%E9%A6%96015401/%E6%A0%87%E6%B8%85%E9%AB%98%E6%B8%85/MP3_128_16_Stero/69069801208160124.mp3","info":{"id":"1135545898","name":"红昭愿 (伴奏)","album":"诗","artist":"音阙诗听"},"ekey":"","cache":true,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":null,"canExpire":false},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:37:05 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["761"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.004381708800792694"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
end
log [ikun音源] Got URL: http://freetyst.nf.migu.cn/public/product9th/product47/2025/03/1216/2025%E5%B9%B401%E6%9C%8820%E6%97%A514%E7%82%B940%E5%88%86%E7%B4%A7%E6%80%A5%E5%86%85%E5%AE%B9%E5%87%86%E5%85%A5%E6%88%90%E9%83%BD%E8%B0%A6%E4%BF%AE%E6%88%90%E6%9C%AC502%E9%A6%96015401/%E6%A0%87%E6%B8%85%E9%AB%98%E6%B8%85/MP3_128_16_Stero/69069801208160124.mp3
log handleGetMusicUrl(mg_1135545898, hires) success, URL: http://freetyst.nf.migu.cn/public/product9th/product47/2025/03/1216/2025%E5%B9%B401%E6%9C%8820%E6%97%A514%E7%82%B940%E5%88%86%E7%B4%A7%E6%80%A5%E5%86%85%E5%AE%B9%E5%87%86%E5%85%A5%E6%88%90%E9%83%BD%E8%B0%A6%E4%BF%AE%E6%88%90%E6%9C%AC502%E9%A6%96015401/%E6%A0%87%E6%B8%85%E9%AB%98%E6%B8%85/MP3_128_16_Stero/69069801208160124.mp3
log [ikun音源] 使用事件驱动方式获取 wy 音源链接
start groupStart--------- Handle Action(musicUrl)
log musicInfo {"singer":"DLSS","name":"alone.","albumName":"alone.","albumId":193463408,"source":"wy","interval":"02:25","songmid":2149456401,"img":"http://p2.music.126.net/I-pKOLxJENNTkZp1nbY2VA==/109951169530908047.jpg","lrc":null,"types":[{"type":"128k","size":"2.21 MB"},{"type":"320k","size":"5.54 MB"},{"type":"flac","size":"16.91 MB"},{"type":"flac24bit","size":"30.14 MB"}],"_types":{"flac24bit":{"size":"30.14 MB"},"flac":{"size":"16.91 MB"},"320k":{"size":"5.54 MB"},"128k":{"size":"2.21 MB"}},"typeUrl":{},"url":"http://m701.music.126.net/20250822111746/88562bac7e992f3433e465b0d8b57883/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506990299/de4f/d537/7094/a86a0c894b20f627e58f4e037d4c395d.flac"}
log source wy
log --- start --- https://api.ikunshare.com/url?source=wy&songId=2149456401&quality=hires
log quality hires
log [CeruMusic] 发起请求: https://api.ikunshare.com/url?source=wy&songId=2149456401&quality=hires
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","url":"http://m701.music.126.net/20250822120208/2d51054583249a7c26ad5fb98b6cd10c/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506990299/de4f/d537/7094/a86a0c894b20f627e58f4e037d4c395d.flac","info":{"id":"2149456401","name":"无","album":"无","artist":"无"},"ekey":"","cache":false,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 11:46:08","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","url":"http://m701.music.126.net/20250822120208/2d51054583249a7c26ad5fb98b6cd10c/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506990299/de4f/d537/7094/a86a0c894b20f627e58f4e037d4c395d.flac","info":{"id":"2149456401","name":"无","album":"无","artist":"无"},"ekey":"","cache":false,"quality":{"target":"无损音质 24Bit","result":"无损音质 24Bit"},"expire":{"ExpireAt":"2025-08-22 11:46:08","canExpire":true},"yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:37:08 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["573"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.05814129579812288"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log handleGetMusicUrl(wy_2149456401, hires) success, URL: http://m701.music.126.net/20250822120208/2d51054583249a7c26ad5fb98b6cd10c/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506990299/de4f/d537/7094/a86a0c894b20f627e58f4e037d4c395d.flac
end
log [ikun音源] Got URL: http://m701.music.126.net/20250822120208/2d51054583249a7c26ad5fb98b6cd10c/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506990299/de4f/d537/7094/a86a0c894b20f627e58f4e037d4c395d.flac

View File

@@ -0,0 +1,611 @@
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 wy 音源链接"
log "[CeruMusic] 发起请求: https://api.cenguigui.cn/api/netease/music_v1.php?id=2149456401&type=json&level=standard"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[聚合API接口 (by lerd)] Got URL: https://m801.music.126.net/20250822114001/b2afa538a9bd71a275090423ddb61b8b/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506991358/9262/9bfa/0e3d/8c4de2e46cb45b5913ac027c12634562.mp3?vuutv=W9gkRY5/Xlj5zcDzyIP7t0xycVF+bFPqM0yESnLJxymoNNC8Qe5OOEOlRo53aqLkZnFkBT8oxO2ITbyyaDwgkp+EtrWk+EReVTFmLEMt874=&cdntag=bWFyaz1vc193ZWIscXVhbGl0eV9zdGFuZGFyZA"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[CeruMusic] 请求响应状态: 200"log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 wy 音源链接"
log "[CeruMusic] 发起请求: https://api.cenguigui.cn/api/netease/music_v1.php?id=2149456401&type=json&level=standard"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[聚合API接口 (by lerd)] Got URL: https://m701.music.126.net/20250822114056/2d8aa51da3c2074cb50a36f4bf348786/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/35506991358/9262/9bfa/0e3d/8c4de2e46cb45b5913ac027c12634562.mp3?vuutv=AsMvTRDjrKksRSVj9wEKkCeAiu88u1zgQsfaYBeFQEw2J4cS1rQojrSdnZMXbu7OdwCJztt32zRefQ6ptoNL0+xYMJarqVgOT1jJWCTrJgc=&cdntag=bWFyaz1vc193ZWIscXVhbGl0eV9zdGFuZGFyZA"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[CeruMusic] 事件驱动插件初始化成功"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[CeruMusic] 请求响应状态: 200"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request"
log "[聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]"
log "[聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mg"
log "[CeruMusic] 事件驱动插件初始化成功"
log "[CeruMusic] Plugin \"聚合API接口 (by lerd)\" loaded successfully."
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
error "[聚合API接口 (by lerd)] Error: get url error"
log "[CeruMusic] 请求响应内容: [object Object]"
log "[聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接"
log "[CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128"
log "[CeruMusic] 请求响应状态: 200"
log "[CeruMusic] 请求响应内容: [object Object]"
error "[聚合API接口 (by lerd)] Error: get url error"log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接
log [CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":-2,"msg":"酷狗hash解析暂未公开开放,如需请付费使用或购买源码使用"}
error [聚合API接口 (by lerd)] Error: get url error
log [聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接
log [CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":-2,"msg":"酷狗hash解析暂未公开开放,如需请付费使用或购买源码使用"}
error [聚合API接口 (by lerd)] Error: get url errorlog [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接
log [CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":-2,"msg":"酷狗hash解析暂未公开开放,如需请付费使用或购买源码使用"}
error [聚合API接口 (by lerd)] Error: get url error
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd)] 使用事件驱动方式获取 kg 音源链接
log [CeruMusic] 发起请求: https://www.hhlqilongzhu.cn/api/dg_kugouSQ.php?type=json&hash=67BA8A4A0681F2078BC423CB13B904B7&quality=128
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":-2,"msg":"酷狗hash解析暂未公开开放,如需请付费使用或购买源码使用"}
error [聚合API接口 (by lerd)] Error: get url error
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: request
log [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"tx":{"name":"tx","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kg":{"name":"kg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit"]},"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["320k","flac"]}}}
log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac"]},"kg":{"name":"酷狗音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"tx":{"name":"QQ音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["320k","flac"]}}
log [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.
log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: ["tx","wy","kg","kw","mg"]
log [CeruMusic] 事件驱动插件初始化成功

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
log [未知插件 by Ceru插件] 注册事件监听器: request
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/urlinfo/1.0.0
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "未知插件" loaded successfully.
log [CeruMusic] 请求响应状态: 503
log [CeruMusic] 响应不是JSON格式内容: <html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.26.1</center>
</body>
</html>
...
log [CeruMusic] 请求响应内容: [object Object]
error [CeruMusic] Request failed: Expected JSON response but got: text/html
log [未知插件 by Ceru插件] 发送事件: inited [object Object]
log [未知插件 by Ceru插件] 动态音源信息已更新: [object Object]
log [未知插件 by Ceru插件] 音源注册完成: kw,wy,mg,tx,kglog [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1895330088/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/38576323/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1808492017/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/502043537/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1887139866/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1365898499/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1363948882/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 请求响应内容: [object Object]
log [CeruMusic] 响应不是JSON格式内容: Not Found...
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8
log [未知插件 by Ceru插件] 注册事件监听器: request
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/urlinfo/1.0.0
log [CeruMusic] Plugin "未知插件" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] 请求响应状态: 503
log [CeruMusic] 响应不是JSON格式内容: <html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.26.1</center>
</body>
</html>
...
log [未知插件 by Ceru插件] 发送事件: inited [object Object]
error [CeruMusic] Request failed: Expected JSON response but got: text/html
log [未知插件 by Ceru插件] 动态音源信息已更新: [object Object]
log [未知插件 by Ceru插件] 音源注册完成: kw,wy,mg,tx,kg
log [CeruMusic] 请求响应内容: [object Object]
log [未知插件] 使用事件驱动方式获取 tx 音源链接
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/url/tx/1363948882/master
log [CeruMusic] 请求响应状态: 404
log [CeruMusic] 响应不是JSON格式内容: Not Found...
log [CeruMusic] 请求响应内容: [object Object]
error [未知插件] Error: Expected JSON response but got: text/plain; charset=utf-8

52
plugin/logs/temp.txt Normal file
View File

@@ -0,0 +1,52 @@
log [CeruMusic] Plugin "ikun音源" loaded successfully.log [聚合API接口 (by lerd) by Ceru插件] 注册事件监听器: requestlog [聚合API接口 (by lerd) by Ceru插件] 发送事件: inited [object Object]log [聚合API接口 (by lerd) by Ceru插件] 动态音源信息已更新: [object Object]log [聚合API接口 (by lerd) by Ceru插件] 音源注册完成: tx,wy,kg,kw,mglog [CeruMusic] Plugin "聚合API接口 (by lerd)" loaded successfully.log [CeruMusic] 事件驱动插件初始化成功log [未知插件 by Ceru插件] 注册事件监听器: request
log [CeruMusic] 发起请求: http://flower.tempmusics.tk/v1/urlinfo/1.0.0
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] Plugin "未知插件" loaded successfully.
log [CeruMusic] 请求响应状态: 503
log [CeruMusic] 响应不是JSON格式内容: <html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.26.1</center>
</body>
</html>
...
log [未知插件 by Ceru插件] 发送事件: inited [object Object]
log [未知插件 by Ceru插件] 音源注册完成: kw,wy,mg,tx,kg
log [未知插件 by Ceru插件] 动态音源信息已更新: [object Object]
log [CeruMusic] 请求响应内容: [object Object]
error [CeruMusic] Request failed: Expected JSON response but got: text/html
log [CeruMusic] Plugin "未知插件" loaded successfully.
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: [object Object]
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log 解析后的 MUSIC_QUALITY 数据: [object Object]
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=6c72d109ed7db5cf037b3bbf1dfccb53
log [ikun音源 by Ceru插件] 注册事件监听器: request
log [ikun音源 by Ceru插件] 发送事件: inited [object Object]
log [ikun音源 by Ceru插件] 动态音源信息已更新: [object Object]
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [ikun音源 by Ceru插件] 音源注册完成: kw,wy,mg
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: [object Object]
log checkUpdate success
log API Response: [object Object]
log [ikun音源 by Ceru插件] 发送事件: updateAlert [object Object]
log "[CeruMusic] Plugin \"LiHouse 音乐插件\" loaded successfully."
log "[CeruMusic] Plugin \"ceru 音乐插件\" loaded successfully."
log 解析后的 MUSIC_QUALITY 数据: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取到的 MUSIC_QUALITY 字符串: {"kw":["128k","320k","flac","flac24bit","hires"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"mg":["128k","320k","flac","flac24bit","hires"]}
log 提取的音源配置: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log --- start --- https://api.ikunshare.com/script?key=&checkUpdate=5917eb1e565bf906fb0433bb1a3b00b5
log [ikun音源 by Ceru插件] 注册事件监听器: request
log [CeruMusic] 发起请求: https://api.ikunshare.com/script?key=&checkUpdate=5917eb1e565bf906fb0433bb1a3b00b5
log [ikun音源 by Ceru插件] 发送事件: inited {"status":true,"openDevTools":false,"sources":{"kw":{"name":"kw","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"wy","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"mg","type":"music","actions":["musicUrl"],"qualitys":["128k","320k","flac","flac24bit","hires"]}}}
log [ikun音源 by Ceru插件] 音源注册完成: ["kw","wy","mg"]
log [ikun音源 by Ceru插件] 动态音源信息已更新: {"kw":{"name":"酷我音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]},"wy":{"name":"网易云音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires","atmos","master"]},"mg":{"name":"咪咕音乐","type":"music","qualitys":["128k","320k","flac","flac24bit","hires"]}}
log [CeruMusic] Plugin "ikun音源" loaded successfully.
log [CeruMusic] 事件驱动插件初始化成功
log [CeruMusic] 请求响应状态: 200
log [CeruMusic] 请求响应内容: {"code":200,"message":"成功","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}}
log API Response: {"body":{"code":200,"message":"成功","yourinfo":{"ip":"183.251.97.29","ua":"lx-music-nodejs/1.0.0"}},"statusCode":200,"headers":{"server":["openresty"],"date":["Fri, 22 Aug 2025 03:44:41 GMT"],"content-type":["application/json; charset=utf-8"],"content-length":["108"],"connection":["keep-alive"],"access-control-allow-origin":["*"],"x-process-time":["0.0007938602939248085"],"cache-control":["no-cache"],"alt-svc":["h3=\":443\"; ma=2592000"]}}
log checkUpdate success

View File

@@ -0,0 +1,75 @@
/**
* LiHouse 音乐插件
* @author CodeBuddy
* @version 1.0.0
*/
// 基础 URL
const baseTwoUrl = 'https://www.lihouse.xyz/coco_widget';
// 1. 插件信息
const pluginInfo = {
name: 'ceru 音乐插件',
version: '1.0.0',
author: '时迁酱',
description: '提供 网易云 音乐资源的访问'
}
// 2. 支持的音源配置
const sources = {
wy: {
name: '网易云',
type: 'music',
qualitys: ['320k'] // 假设支持这些音质
}
}
// 3. 获取音乐URL的核心函数
async function musicUrl(source, musicInfo, quality) {
// 从 cerumusic 对象获取 API
const { request, env, version } = cerumusic
// 构建请求参数
const songId = musicInfo.songmid || musicInfo.hash;
if (!songId) {
throw new Error('无效的歌曲ID');
}
const apiUrl = `${baseTwoUrl}/music_resource/id/${songId}`;
console.log(`[${pluginInfo.name}] 请求音乐链接: ${apiUrl}`);
try {
// 发起网络请求
const { body, statusCode } = await request(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'User-Agent': `cerumusic-${env}/${version}`
}
});
// 处理响应
if (statusCode !== 200 || !body.status) {
const errorMessage = body.message || '歌曲不存在';
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
// 根据请求的音质选择对应的链接
// 假设 song_data 中包含不同音质的链接
const songData = body.song_data;
return songData.url
throw new Error('无效的响应格式');
} catch (error) {
console.error(`[${pluginInfo.name}] 请求失败:`, error.message);
throw new Error(`获取音乐链接失败: ${error.message}`);
}
}
// 导出插件
module.exports = {
pluginInfo,
sources,
musicUrl,
}

View File

@@ -0,0 +1,284 @@
/**
* 由 CeruMusic 插件转换器转换 - @author sqj
* @name ikun音源
* @author ikunshare
* @version v14
* @description 反馈群951962664
*/
const pluginInfo = {
name: "ikun音源",
version: "v14",
author: "ikunshare",
description: "反馈群951962664"
};
// 原始插件代码
const originalPluginCode = "/*!\n * @name ikun音源\n * @description 反馈群951962664\n * @version v14\n * @author ikunshare\n */\n\nconst DEV_ENABLE = false\nconst UPDATE_ENABLE = true\nconst API_URL = \"https://api.ikunshare.com\"\nconst API_KEY = ``\nconst MUSIC_QUALITY = JSON.parse('{\"kw\":[\"128k\",\"320k\",\"flac\",\"flac24bit\",\"hires\"],\"wy\":[\"128k\",\"320k\",\"flac\",\"flac24bit\",\"hires\",\"atmos\",\"master\"],\"mg\":[\"128k\",\"320k\",\"flac\",\"flac24bit\",\"hires\"]}');\nconst MUSIC_SOURCE = Object.keys(MUSIC_QUALITY);\n\nconst { EVENT_NAMES, request, on, send, utils, env, version } = globalThis.lx;\n\nconst SCRIPT_MD5 = \"5917eb1e565bf906fb0433bb1a3b00b5\";\n\nconst httpFetch = (url, options = { method: \"GET\" }) => {\n return new Promise((resolve, reject) => {\n console.log(\"--- start --- \" + url);\n request(url, options, (err, resp) => {\n if (err) return reject(err);\n console.log(\"API Response: \", resp);\n resolve(resp);\n });\n });\n};\n\nconst handleGetMusicUrl = async (source, musicInfo, quality) => {\n const songId = musicInfo.hash ?? musicInfo.songmid;\n const request = await httpFetch(\n `${API_URL}/url?source=${source}&songId=${songId}&quality=${quality}`,\n {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `${\n env ? `lx-music-${env}/${version}` : `lx-music-request/${version}`\n }`,\n \"X-Request-Key\": API_KEY,\n },\n follow_max: 5,\n }\n );\n const { body } = request;\n if (!body || isNaN(Number(body.code))) throw new Error(\"unknow error\");\n if (env != \"mobile\") console.groupEnd();\n switch (body.code) {\n case 200:\n console.log(\n `handleGetMusicUrl(${source}_${musicInfo.songmid}, ${quality}) success, URL: ${body.url}`\n );\n return body.url;\n case 403:\n console.log(\n `handleGetMusicUrl(${source}_${musicInfo.songmid}, ${quality}) failed: Key失效/鉴权失败`\n );\n throw new Error(\"Key失效/鉴权失败\");\n case 500:\n console.log(\n `handleGetMusicUrl(${source}_${musicInfo.songmid}, ${quality}) failed, ${body.msg}`\n );\n throw new Error(`获取URL失败, ${body.msg ?? \"未知错误\"}`);\n case 429:\n console.log(\n `handleGetMusicUrl(${source}_${musicInfo.songmid}, ${quality}) failed, 请求过于频繁,请休息一下吧`\n );\n throw new Error(\"请求过速\");\n default:\n console.log(\n `handleGetMusicUrl(${source}_${\n musicInfo.songmid\n }, ${quality}) failed, ${body.msg ? body.msg : \"未知错误\"}`\n );\n throw new Error(body.msg ?? \"未知错误\");\n }\n};\n\nconst checkUpdate = async () => {\n const request = await httpFetch(\n `${API_URL}/script?key=${API_KEY}&checkUpdate=${SCRIPT_MD5}`,\n {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `${\n env ? `lx-music-${env}/${version}` : `lx-music-request/${version}`\n }`,\n },\n }\n );\n const { body } = request;\n\n if (!body || body.code !== 200) console.log(\"checkUpdate failed\");\n else {\n console.log(\"checkUpdate success\");\n if (body.data != null) {\n globalThis.lx.send(lx.EVENT_NAMES.updateAlert, {\n log: body.data.updateMsg,\n updateUrl: body.data.updateUrl,\n });\n }\n }\n};\n\nconst musicSources = {};\nMUSIC_SOURCE.forEach((item) => {\n musicSources[item] = {\n name: item,\n type: \"music\",\n actions: [\"musicUrl\"],\n qualitys: MUSIC_QUALITY[item],\n };\n});\n\non(EVENT_NAMES.request, ({ action, source, info }) => {\n switch (action) {\n case \"musicUrl\":\n if (env != \"mobile\") {\n console.group(`Handle Action(musicUrl)`);\n console.log(\"source\", source);\n console.log(\"quality\", info.type);\n console.log(\"musicInfo\", info.musicInfo);\n } else {\n console.log(`Handle Action(musicUrl)`);\n console.log(\"source\", source);\n console.log(\"quality\", info.type);\n console.log(\"musicInfo\", info.musicInfo);\n }\n return handleGetMusicUrl(source, info.musicInfo, info.type)\n .then((data) => Promise.resolve(data))\n .catch((err) => Promise.reject(err));\n default:\n console.error(`action(${action}) not support`);\n return Promise.reject(\"action not support\");\n }\n});\n\nif (UPDATE_ENABLE) checkUpdate();\n\nsend(EVENT_NAMES.inited, {\n status: true,\n openDevTools: DEV_ENABLE,\n sources: musicSources,\n});\n";
// 音源信息将通过插件的 send 调用动态获取
let sources = {};
function getSourceName(sourceId) {
const nameMap = {
'kw': '酷我音乐',
'kg': '酷狗音乐',
'tx': 'QQ音乐',
'wy': '网易云音乐',
'mg': '咪咕音乐'
};
return nameMap[sourceId] || sourceId.toUpperCase() + '音乐';
}
// 提取默认音源配置作为备用
function extractDefaultSources() {
// 尝试从 MUSIC_QUALITY 常量中提取音源信息
const qualityMatch = originalPluginCode.match(/const\s+MUSIC_QUALITY\s*=\s*JSON\.parse\(([^)]+)\)/);
if (qualityMatch) {
try {
// 处理字符串,移除外层引号并正确解析
let qualityStr = qualityMatch[1].trim();
if (qualityStr.startsWith("'") && qualityStr.endsWith("'")) {
qualityStr = qualityStr.slice(1, -1);
} else if (qualityStr.startsWith('"') && qualityStr.endsWith('"')) {
qualityStr = qualityStr.slice(1, -1);
}
console.log('提取到的 MUSIC_QUALITY 字符串:', qualityStr);
const qualityData = JSON.parse(qualityStr);
console.log('解析后的 MUSIC_QUALITY 数据:', qualityData);
const extractedSources = {};
Object.keys(qualityData).forEach(sourceId => {
extractedSources[sourceId] = {
name: getSourceName(sourceId),
type: 'music',
qualitys: qualityData[sourceId] || ['128k', '320k']
};
});
console.log('提取的音源配置:', extractedSources);
return extractedSources;
} catch (e) {
console.log('解析 MUSIC_QUALITY 失败:', e.message);
}
}
// 默认音源配置
return {
kw: { name: "酷我音乐", type: "music", qualitys: ['128k', '320k', 'flac', 'flac24bit', 'hires', 'atmos', 'master'] },
kg: { name: "酷狗音乐", type: "music", qualitys: ['128k', '320k', 'flac', 'flac24bit', 'hires', 'atmos', 'master'] },
tx: { name: "QQ音乐", type: "music", qualitys: ['128k', '320k', 'flac', 'flac24bit', 'hires', 'atmos', 'master'] },
wy: { name: "网易云音乐", type: "music", qualitys: ['128k', '320k', 'flac', 'flac24bit', 'hires', 'atmos', 'master'] },
mg: { name: "咪咕音乐", type: "music", qualitys: ['128k', '320k', 'flac', 'flac24bit', 'hires', 'atmos', 'master'] }
};
}
// 初始化默认音源
sources = extractDefaultSources();
// 插件状态
let isInitialized = false;
let pluginSources = {};
let requestHandler = null;
initializePlugin()
function initializePlugin() {
if (isInitialized) return;
const { request, utils } = cerumusic;
// 创建完整的 lx 模拟环境
const mockLx = {
EVENT_NAMES: {
request: 'request',
inited: 'inited',
updateAlert: 'updateAlert'
},
on: (event, handler) => {
console.log(`[ikun音源 by Ceru插件] 注册事件监听器: ${event}`);
if (event === 'request') {
requestHandler = handler;
}
},
send: (event, data) => {
console.log(`[ikun音源 by Ceru插件] 发送事件: ${event}`, data);
if (event === 'inited' && data.sources) {
// 动态更新音源信息,保持原始的音质配置
pluginSources = data.sources;
// 将插件发送的音源信息转换为正确格式并同步到导出的 sources
Object.keys(pluginSources).forEach(sourceId => {
const sourceInfo = pluginSources[sourceId];
// 保留原始音质配置,如果存在的话
const originalQualitys = sources[sourceId] && sources[sourceId].qualitys;
sources[sourceId] = {
name: getSourceName(sourceId),
type: sourceInfo.type || 'music',
// 优先使用插件发送的音质配置,其次使用原始解析的配置,最后使用默认配置
qualitys: sourceInfo.qualitys || originalQualitys || ['128k', '320k']
};
});
console.log('[ikun音源 by Ceru插件] 音源注册完成:', Object.keys(pluginSources));
console.log('[ikun音源 by Ceru插件] 动态音源信息已更新:', sources);
}
},
request: request,
utils: {
buffer: utils.buffer,
crypto: {
aesEncrypt: (data, mode, key, iv) => {
// 简化的 AES 加密实现
try {
return utils.crypto ? utils.crypto.aesEncrypt(data, mode, key, iv) : data;
} catch (e) {
return data;
}
},
md5: (str) => {
try {
return utils.crypto ? utils.crypto.md5(str) : str;
} catch (e) {
return str;
}
},
randomBytes: (size) => {
try {
return utils.crypto ? utils.crypto.randomBytes(size) : Buffer.alloc(size);
} catch (e) {
return Buffer.alloc(size);
}
},
rsaEncrypt: (data, key) => {
try {
return utils.crypto ? utils.crypto.rsaEncrypt(data, key) : data;
} catch (e) {
return data;
}
}
}
},
version: '1.0.0',
currentScriptInfo: {
rawScript: originalPluginCode,
name: 'ikun音源',
version: 'v14',
author: 'ikunshare',
description: '反馈群951962664'
},
env: 'nodejs' // 添加环境信息
};
// 创建全局环境
const globalThis = {
lx: mockLx
};
// 创建沙箱环境
const sandbox = {
globalThis: globalThis,
lx: mockLx,
console: console,
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval,
Buffer: Buffer,
JSON: JSON,
require: () => ({}),
module: { exports: {} },
exports: {},
process: { env: { NODE_ENV: 'production' } }
};
try {
// 使用 Function 构造器执行插件代码
const pluginFunction = new Function(
'globalThis', 'lx', 'console', 'setTimeout', 'clearTimeout',
'setInterval', 'clearInterval', 'Buffer', 'JSON', 'require',
'module', 'exports', 'process',
originalPluginCode
);
pluginFunction(
globalThis, mockLx, console, setTimeout, clearTimeout,
setInterval, clearInterval, Buffer, JSON, () => ({}),
{ exports: {} }, {}, { env: { NODE_ENV: 'production' } }
);
isInitialized = true;
console.log(`[CeruMusic] 事件驱动插件初始化成功`);
} catch (error) {
console.log(`[CeruMusic] 插件初始化完成: ${error.message}`);
isInitialized = true;
}
}
async function musicUrl(source, musicInfo, quality) {
// 确保插件已初始化
initializePlugin();
// 等待一小段时间让插件完全初始化
await new Promise(resolve => setTimeout(resolve, 100));
if (!requestHandler) {
const errorMessage = '插件请求处理器未初始化';
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
console.log(`[${pluginInfo.name}] 使用事件驱动方式获取 ${source} 音源链接`);
try {
// 调用插件的请求处理器
const result = await requestHandler({
source: source,
action: 'musicUrl',
info: {
musicInfo: musicInfo,
type: quality
}
});
// 检查结果是否有效
if (!result) {
const errorMessage = `获取 ${source} 音源链接失败: 返回结果为空`;
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
// 如果结果是对象且包含错误信息
if (typeof result === 'object' && result.error) {
const errorMessage = result.error || `获取 ${source} 音源链接失败`;
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
// 如果结果是对象且包含状态码
if (typeof result === 'object' && result.code && result.code !== 200) {
const errorMessage = result.msg || `接口错误 (Code: ${result.code})`;
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
console.log(`[${pluginInfo.name}] Got URL: ${typeof result === 'string' ? result : result.url || result}`);
return result;
} catch (error) {
// 确保错误信息格式与 example-plugin.js 一致
const errorMessage = error.message || `获取 ${source} 音源链接时发生未知错误`;
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
}
module.exports = {
pluginInfo,
sources,
musicUrl
};

View File

@@ -1,81 +0,0 @@
/**
* ikun 音源插件 for CeruMusic
* @author ikunshare
* @version v1.0
*/
// 插件元信息
const pluginInfo = {
name: 'ikun音源',
version: '1.0.0',
author: 'ikunshare',
description: '基于 ikunshare API 的音源插件',
};
// API 配置
const API_URL = "https://api.ikunshare.com";
const API_KEY = ``; // 如果需要请填入你的API Key
// 支持的音源和音质
const sources = {
kw: {
name: '酷我音乐',
type: 'music',
qualitys: ["128k", "320k", "flac", "flac24bit", "hires"],
},
wy: {
name: '网易云音乐',
type: 'music',
qualitys: ["128k", "320k", "flac", "flac24bit", "hires", "atmos", "master"],
},
mg: {
name: '咪咕音乐',
type: 'music',
qualitys: ["128k", "320k", "flac", "flac24bit", "hires"],
},
};
/**
* 获取音乐URL的核心函数
* @param {string} source - 音源标识 (e.g., "kw", "wy")
* @param {object} musicInfo - 歌曲信息
* @param {string} quality - 音质
* @returns {Promise<string>} 歌曲的URL
*/
async function musicUrl(source, musicInfo, quality) {
// cerumusic 对象由插件宿主提供
const { request, env, version } = cerumusic;
const songId = musicInfo.hash ?? musicInfo.songmid;
const url = `${API_URL}/url?source=${source}&songId=${songId}&quality=${quality}`;
console.log(`[${pluginInfo.name}] Requesting URL: ${url}`);
const { body, statusCode } = await request(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'User-Agent': `cerumusic-${env}/${version}`,
'X-Request-Key': API_KEY,
},
});
if (statusCode !== 200 || body.code !== 200) {
const errorMessage = body.msg || `接口错误 (HTTP: ${statusCode}, Body: ${body.code})`;
console.error(`[${pluginInfo.name}] Error: ${errorMessage}`);
throw new Error(errorMessage);
}
console.log(`[${pluginInfo.name}] Got URL: ${body.url}`);
return body.url;
}
// 导出插件模块
module.exports = {
pluginInfo,
sources,
musicUrl,
// 如果需要,可以继续实现 getPic, getLyric 等方法
// getPic: async function(source, musicInfo) { ... },
// getLyric: async function(source, musicInfo) { ... },
};

42
src/common/index.ts Normal file
View File

@@ -0,0 +1,42 @@
import log from 'electron-log/node'
export const isLinux = process.platform == 'linux'
export const isWin = process.platform == 'win32'
export const isMac = process.platform == 'darwin'
export const isProd = process.env.NODE_ENV == 'production'
export const getPlatform = (platform: NodeJS.Platform = process.platform) => {
switch (platform) {
case 'win32':
return 'windows'
case 'darwin':
return 'mac'
default:
return 'linux'
}
}
// https://stackoverflow.com/a/53387532
export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {
// treat non-numerical characters as lower version
// replacing them with a negative number based on charcode of each character
const fix = (s: string) => `.${s.toLowerCase().charCodeAt(0) - 2147483647}.`
const currentVerArr: Array<string | number> = ('' + currentVer)
.replace(/[^0-9.]/g, fix)
.split('.')
const targetVerArr: Array<string | number> = ('' + targetVer).replace(/[^0-9.]/g, fix).split('.')
let c = Math.max(currentVerArr.length, targetVerArr.length)
for (let i = 0; i < c; i++) {
// convert to integer the most efficient way
currentVerArr[i] = ~~currentVerArr[i]
targetVerArr[i] = ~~targetVerArr[i]
if (currentVerArr[i] > targetVerArr[i]) return 1
else if (currentVerArr[i] < targetVerArr[i]) return -1
}
return 0
}
export { log }
export * from './utils/common'

239
src/common/utils/common.ts Normal file
View File

@@ -0,0 +1,239 @@
// 非业务工具方法
/**
* 获取两个数之间的随机整数大于等于min小于max
* @param {*} min
* @param {*} max
*/
export const getRandom = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min)) + min
export const sizeFormate = (size: number): string => {
// https://gist.github.com/thomseddon/3511330
if (!size) return '0 B'
const units = ['B', 'KB', 'MB', 'GB', 'TB']
const number = Math.floor(Math.log(size) / Math.log(1024))
return `${(size / Math.pow(1024, Math.floor(number))).toFixed(2)} ${units[number]}`
}
/**
* 将字符串、时间戳等格式转成时间对象
* @param date 时间
* @returns 时间对象或空字符串
*/
export const toDateObj = (date: any): Date | '' => {
// console.log(date)
if (!date) return ''
switch (typeof date) {
case 'string':
if (!date.includes('T')) date = date.split('.')[0].replace(/-/g, '/')
// eslint-disable-next-line no-fallthrough
case 'number':
date = new Date(date)
// eslint-disable-next-line no-fallthrough
case 'object':
break
default:
return ''
}
return date
}
const numFix = (n: number): string => (n < 10 ? `0${n}` : n.toString())
/**
* 时间格式化
* @param _date 时间
* @param format Y-M-D h:m:s Y年 M月 D日 h时 m分 s秒
*/
export const dateFormat = (_date: any, format = 'Y-M-D h:m:s') => {
// console.log(date)
const date = toDateObj(_date)
if (!date) return ''
return format
.replace('Y', date.getFullYear().toString())
.replace('M', numFix(date.getMonth() + 1))
.replace('D', numFix(date.getDate()))
.replace('h', numFix(date.getHours()))
.replace('m', numFix(date.getMinutes()))
.replace('s', numFix(date.getSeconds()))
}
export const formatPlayTime = (time: number) => {
const m = Math.trunc(time / 60)
const s = Math.trunc(time % 60)
return m == 0 && s == 0 ? '--/--' : numFix(m) + ':' + numFix(s)
}
export const formatPlayTime2 = (time: number) => {
const m = Math.trunc(time / 60)
const s = Math.trunc(time % 60)
return numFix(m) + ':' + numFix(s)
}
export const isUrl = (path: string) => /https?:\/\//.test(path)
// 解析URL参数为对象
export const parseUrlParams = (str: string): Record<string, string> => {
const params: Record<string, string> = {}
if (typeof str !== 'string') return params
const paramsArr = str.split('&')
for (const param of paramsArr) {
const [key, value] = param.split('=')
params[key] = value
}
return params
}
/**
* 生成节流函数
* @param fn 回调
* @param delay 延迟
* @returns
*/
export function throttle<Args extends any[]>(
fn: (...args: Args) => void | Promise<void>,
delay = 100
) {
let timer: NodeJS.Timeout | null = null
let _args: Args
return (...args: Args) => {
_args = args
if (timer) return
timer = setTimeout(() => {
timer = null
void fn(..._args)
}, delay)
}
}
/**
* 生成防抖函数
* @param fn 回调
* @param delay 延迟
* @returns
*/
export function debounce<Args extends any[]>(
fn: (...args: Args) => void | Promise<void>,
delay = 100
) {
let timer: NodeJS.Timeout | null = null
let _args: Args
return (...args: Args) => {
_args = args
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
void fn(..._args)
}, delay)
}
}
const fileNameRxp = /[\\/:*?#"<>|]/g
export const filterFileName = (name: string): string => name.replace(fileNameRxp, '')
// https://blog.csdn.net/xcxy2015/article/details/77164126#comments
/**
*
* @param a
* @param b
*/
export const similar = (a: string, b: string) => {
if (!a || !b) return 0
if (a.length > b.length) {
// 保证 a <= b
const t = b
b = a
a = t
}
const al = a.length
const bl = b.length
const mp: number[] = [] // 一个表
let i, j, ai, lt, tmp // ai字符串a的第i个字符。 lt左上角的值。 tmp暂存新的值。
for (i = 0; i <= bl; i++) mp[i] = i
for (i = 1; i <= al; i++) {
ai = a.charAt(i - 1)
lt = mp[0]
mp[0] = mp[0] + 1
for (j = 1; j <= bl; j++) {
tmp = Math.min(mp[j] + 1, mp[j - 1] + 1, lt + (ai == b.charAt(j - 1) ? 0 : 1))
lt = mp[j]
mp[j] = tmp
}
}
return 1 - mp[bl] / bl
}
/**
* 排序字符串
* @param arr
* @param data
*/
export const sortInsert = <T>(
arr: Array<{ num: number; data: T }>,
data: { num: number; data: T }
) => {
const key = data.num
let left = 0
let right = arr.length - 1
while (left <= right) {
const middle = Math.trunc((left + right) / 2)
if (key == arr[middle].num) {
left = middle
break
} else if (key < arr[middle].num) {
right = middle - 1
} else {
left = middle + 1
}
}
while (left > 0) {
if (arr[left - 1].num != key) break
left--
}
arr.splice(left, 0, data)
}
export const encodePath = (path: string) => {
return encodeURI(path.replaceAll('\\', '/'))
}
export const arrPush = <T>(list: T[], newList: T[]) => {
for (let i = 0; i * 1000 < newList.length; i++) {
list.push(...newList.slice(i * 1000, (i + 1) * 1000))
}
return list
}
export const arrUnshift = <T>(list: T[], newList: T[]) => {
for (let i = 0; i * 1000 < newList.length; i++) {
list.splice(i * 1000, 0, ...newList.slice(i * 1000, (i + 1) * 1000))
}
return list
}
export const arrPushByPosition = <T>(list: T[], newList: T[], position: number) => {
for (let i = 0; i * 1000 < newList.length; i++) {
list.splice(position + i * 1000, 0, ...newList.slice(i * 1000, (i + 1) * 1000))
}
return list
}
// https://stackoverflow.com/a/2450976
export const arrShuffle = <T>(array: T[]) => {
let currentIndex = array.length
let randomIndex
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex)
currentIndex--
// And swap it with the current element.
;[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]
}
return array
}

View File

@@ -0,0 +1,82 @@
import { inflate } from 'zlib'
import { decodeName } from './util'
// https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784
const enc_key = Buffer.from(
[0x40, 0x47, 0x61, 0x77, 0x5e, 0x32, 0x74, 0x47, 0x51, 0x36, 0x31, 0x2d, 0xce, 0xd2, 0x6e, 0x69],
'binary'
)
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const decodeLyric = (str) =>
new Promise((resolve, reject) => {
if (!str.length) return
const buf_str = Buffer.from(str, 'base64').subarray(4)
for (let i = 0, len = buf_str.length; i < len; i++) {
buf_str[i] = buf_str[i] ^ enc_key[i % 16]
}
inflate(buf_str, (err, result) => {
if (err) return reject(err)
resolve(result.toString())
})
})
const headExp = /^.*\[id:\$\w+\]\n/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const parseLyric = (str) => {
str = str.replace(/\r/g, '')
if (headExp.test(str)) str = str.replace(headExp, '')
let trans = str.match(/\[language:([\w=\\/+]+)\]/)
let lyric
let rlyric
let tlyric
if (trans) {
str = str.replace(/\[language:[\w=\\/+]+\]\n/, '')
let json = JSON.parse(Buffer.from(trans[1], 'base64').toString())
for (const item of json.content) {
switch (item.type) {
case 0:
rlyric = item.lyricContent
break
case 1:
tlyric = item.lyricContent
break
}
}
}
let i = 0
let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, (str) => {
let result = str.match(/\[((\d+),\d+)\].*/)
let time = parseInt(result[2])
let ms = time % 1000
time /= 1000
let m = parseInt(time / 60)
.toString()
.padStart(2, '0')
time %= 60
let s = parseInt(time).toString().padStart(2, '0')
time = `${m}:${s}.${ms}`
if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}`
if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}`
i++
return str.replace(result[1], time)
})
rlyric = rlyric ? rlyric.join('\n') : ''
tlyric = tlyric ? tlyric.join('\n') : ''
lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>')
lxlyric = decodeName(lxlyric)
lyric = lxlyric.replace(/<\d+,\d+>/g, '')
rlyric = decodeName(rlyric)
tlyric = decodeName(tlyric)
return {
lyric,
tlyric,
rlyric,
lxlyric
}
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const decodeKrc = async (data) => {
return decodeLyric(data).then(parseLyric)
}

View File

@@ -0,0 +1,18 @@
const encodeNames = {
'&nbsp;': ' ',
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&apos;': "'",
'&#039;': "'"
} as const
export const decodeName = (str: string | null = '') => {
return (
str?.replace(
/(?:&amp;|&lt;|&gt;|&quot;|&apos;|&#039;|&nbsp;)/gm,
(s: string) => encodeNames[s as keyof typeof encodeNames]
) ?? ''
)
}

187
src/common/utils/nodejs.ts Normal file
View File

@@ -0,0 +1,187 @@
import fs from 'node:fs'
import crypto from 'node:crypto'
import { gzip, gunzip } from 'node:zlib'
import path from 'node:path'
import { networkInterfaces } from 'node:os'
export const joinPath = (...paths: string[]): string => path.join(...paths)
export const extname = (p: string): string => path.extname(p)
export const basename = (p: string, ext?: string): string => path.basename(p, ext)
export const dirname = (p: string): string => path.dirname(p)
/**
* 检查路径是否存在
* @param {*} path 路径
*/
export const checkPath = async (path: string): Promise<boolean> => {
return new Promise((resolve) => {
if (!path) {
resolve(false)
return
}
fs.access(path, fs.constants.F_OK, (err) => {
if (err) {
resolve(false)
return
}
resolve(true)
})
})
}
/**
* 检查路径并创建目录
* @param path
* @returns
*/
export const checkAndCreateDir = async (path: string) => {
return fs.promises
.access(path, fs.constants.F_OK | fs.constants.W_OK)
.catch(async (err: NodeJS.ErrnoException) => {
if (err.code != 'ENOENT') throw err as Error
return fs.promises.mkdir(path, { recursive: true })
})
.then(() => true)
.catch((err) => {
console.error(err)
return false
})
}
export const getFileStats = async (path: string): Promise<fs.Stats | null> => {
return new Promise((resolve) => {
if (!path) {
resolve(null)
return
}
fs.stat(path, (err, stats) => {
if (err) {
resolve(null)
return
}
resolve(stats)
})
})
}
/**
* 检查路径并创建目录
* @param path
* @returns
*/
export const createDir = async (path: string) =>
new Promise<void>((resolve, reject) => {
fs.access(path, fs.constants.F_OK | fs.constants.W_OK, (err) => {
if (err) {
if (err.code === 'ENOENT') {
fs.mkdir(path, { recursive: true }, (err) => {
if (err) {
reject(err)
return
}
resolve()
})
return
}
reject(err)
return
}
resolve()
})
})
export const removeFile = async (path: string) =>
new Promise<void>((resolve, reject) => {
fs.access(path, fs.constants.F_OK, (err) => {
if (err) {
err.code == 'ENOENT' ? resolve() : reject(err)
return
}
fs.unlink(path, (err) => {
if (err) {
reject(err)
return
}
resolve()
})
})
})
export const readFile = async (path: string) => fs.promises.readFile(path)
/**
* 创建 MD5 hash
* @param {*} str
*/
export const toMD5 = (str: string) => crypto.createHash('md5').update(str).digest('hex')
export const gzipData = async (str: string): Promise<Buffer> => {
return new Promise((resolve, reject) => {
gzip(str, (err, result) => {
if (err) {
reject(err)
return
}
resolve(result)
})
})
}
export const gunzipData = async (buf: Buffer): Promise<string> => {
return new Promise((resolve, reject) => {
gunzip(buf, (err, result) => {
if (err) {
reject(err)
return
}
resolve(result.toString())
})
})
}
export const saveStrToFile = async (path: string, str: string | Buffer): Promise<void> => {
await new Promise<void>((resolve, reject) => {
fs.writeFile(path, str, (err) => {
if (err) {
reject(err)
return
}
resolve()
})
})
}
export const b64DecodeUnicode = (str: string): string => {
// Going backwards: from bytestream, to percent-encoding, to original string.
// return decodeURIComponent(window.atob(str).split('').map(function(c) {
// return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
// }).join(''))
return Buffer.from(str, 'base64').toString()
}
export const copyFile = async (sourcePath: string, distPath: string) => {
return fs.promises.copyFile(sourcePath, distPath)
}
export const moveFile = async (sourcePath: string, distPath: string) => {
return fs.promises.rename(sourcePath, distPath)
}
export const getAddress = (): string[] => {
const nets = networkInterfaces()
const results: string[] = []
// console.log(nets)
for (const interfaceInfos of Object.values(nets)) {
if (!interfaceInfos) continue
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
for (const interfaceInfo of interfaceInfos) {
if (interfaceInfo.family === 'IPv4' && !interfaceInfo.internal) {
results.push(interfaceInfo.address)
}
}
}
return results
}

View File

@@ -0,0 +1,378 @@
const easeInOutQuad = (t: number, b: number, c: number, d: number): number => {
t /= d / 2
if (t < 1) return (c / 2) * t * t + b
t--
return (-c / 2) * (t * (t - 2) - 1) + b
}
type Noop = () => void
const noop: Noop = () => {}
type ScrollElement<T> = {
lx_scrollLockKey?: number
lx_scrollNextParams?: [ScrollElement<HTMLElement>, number, number, Noop]
lx_scrollTimeout?: number
lx_scrollDelayTimeout?: number
} & T
const handleScrollY = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = noop
): Noop => {
if (!element) {
fn()
return noop
}
const clean = () => {
element.lx_scrollLockKey = undefined
element.lx_scrollNextParams = undefined
if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout)
element.lx_scrollTimeout = undefined
}
if (element.lx_scrollLockKey) {
element.lx_scrollNextParams = [element, to, duration, fn]
element.lx_scrollLockKey = -1
return clean
}
// @ts-expect-error
const start = element.scrollTop ?? element.scrollY ?? 0
if (to > start) {
let maxScrollTop = element.scrollHeight - element.clientHeight
if (to > maxScrollTop) to = maxScrollTop
} else if (to < start) {
if (to < 0) to = 0
} else {
fn()
return noop
}
const change = to - start
const increment = 10
if (!change) {
fn()
return noop
}
let currentTime = 0
let val: number
let key = Math.random()
const animateScroll = () => {
element.lx_scrollTimeout = undefined
// if (element.lx_scrollLockKey != key) {
if (element.lx_scrollNextParams && currentTime > duration * 0.75) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
return
}
currentTime += increment
val = Math.trunc(easeInOutQuad(currentTime, start, change, duration))
if (element.scrollTo) {
element.scrollTo(0, val)
} else {
element.scrollTop = val
}
if (currentTime < duration) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
}
}
}
element.lx_scrollLockKey = key
animateScroll()
return clean
}
/**
* 设置滚动条位置
* @param {*} element 要设置滚动的容器 dom
* @param {*} to 滚动的目标位置
* @param {*} duration 滚动完成时间 ms
* @param {*} fn 滚动完成后的回调
* @param {*} delay 延迟执行时间
*/
export const scrollTo = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = () => {},
delay = 0
): (() => void) => {
let cancelFn: () => void
if (element.lx_scrollDelayTimeout != null) {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
if (delay) {
let scrollCancelFn: Noop
cancelFn = () => {
if (element.lx_scrollDelayTimeout == null) {
scrollCancelFn?.()
} else {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
}
element.lx_scrollDelayTimeout = window.setTimeout(() => {
element.lx_scrollDelayTimeout = undefined
scrollCancelFn = handleScrollY(element, to, duration, fn)
}, delay)
} else {
cancelFn = handleScrollY(element, to, duration, fn) ?? noop
}
return cancelFn
}
const handleScrollX = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = () => {}
): (() => void) => {
if (!element) {
fn()
return noop
}
const clean = () => {
element.lx_scrollLockKey = undefined
element.lx_scrollNextParams = undefined
if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout)
element.lx_scrollTimeout = undefined
}
if (element.lx_scrollLockKey) {
element.lx_scrollNextParams = [element, to, duration, fn]
element.lx_scrollLockKey = -1
return clean
}
// @ts-expect-error
const start = element.scrollLeft || element.scrollX || 0
if (to > start) {
let maxScrollLeft = element.scrollWidth - element.clientWidth
if (to > maxScrollLeft) to = maxScrollLeft
} else if (to < start) {
if (to < 0) to = 0
} else {
fn()
return noop
}
const change = to - start
const increment = 10
if (!change) {
fn()
return noop
}
let currentTime = 0
let val: number
let key = Math.random()
const animateScroll = () => {
element.lx_scrollTimeout = undefined
if (element.lx_scrollNextParams && currentTime > duration * 0.75) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
return
}
currentTime += increment
val = Math.trunc(easeInOutQuad(currentTime, start, change, duration))
if (element.scrollTo) {
element.scrollTo(val, 0)
} else {
element.scrollLeft = val
}
if (currentTime < duration) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
}
}
}
element.lx_scrollLockKey = key
animateScroll()
return clean
}
/**
* 设置滚动条位置
* @param {*} element 要设置滚动的容器 dom
* @param {*} to 滚动的目标位置
* @param {*} duration 滚动完成时间 ms
* @param {*} fn 滚动完成后的回调
* @param {*} delay 延迟执行时间
*/
export const scrollXTo = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = () => {},
delay = 0
): (() => void) => {
let cancelFn: Noop
if (element.lx_scrollDelayTimeout != null) {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
if (delay) {
let scrollCancelFn: Noop
cancelFn = () => {
if (element.lx_scrollDelayTimeout == null) {
scrollCancelFn?.()
} else {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
}
element.lx_scrollDelayTimeout = window.setTimeout(() => {
element.lx_scrollDelayTimeout = undefined
scrollCancelFn = handleScrollX(element, to, duration, fn)
}, delay)
} else {
cancelFn = handleScrollX(element, to, duration, fn)
}
return cancelFn
}
const handleScrollXR = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = () => {}
): (() => void) => {
if (!element) {
fn()
return noop
}
const clean = () => {
element.lx_scrollLockKey = undefined
element.lx_scrollNextParams = undefined
if (element.lx_scrollTimeout) window.clearTimeout(element.lx_scrollTimeout)
element.lx_scrollTimeout = undefined
}
if (element.lx_scrollLockKey) {
element.lx_scrollNextParams = [element, to, duration, fn]
element.lx_scrollLockKey = -1
return clean
}
// @ts-expect-error
const start = element.scrollLeft || (element.scrollX as number) || 0
if (to < start) {
let maxScrollLeft = -element.scrollWidth + element.clientWidth
if (to < maxScrollLeft) to = maxScrollLeft
} else if (to > start) {
if (to > 0) to = 0
} else {
fn()
return noop
}
const change = to - start
const increment = 10
if (!change) {
fn()
return noop
}
let currentTime = 0
let val: number
let key = Math.random()
const animateScroll = () => {
element.lx_scrollTimeout = undefined
if (element.lx_scrollNextParams && currentTime > duration * 0.75) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
return
}
currentTime += increment
val = Math.trunc(easeInOutQuad(currentTime, start, change, duration))
if (element.scrollTo) {
element.scrollTo(val, 0)
} else {
element.scrollLeft = val
}
if (currentTime < duration) {
element.lx_scrollTimeout = window.setTimeout(animateScroll, increment)
} else {
if (element.lx_scrollNextParams) {
const [_element, to, duration, fn] = element.lx_scrollNextParams
clean()
handleScrollY(_element, to, duration, fn)
} else {
clean()
fn()
}
}
}
element.lx_scrollLockKey = key
animateScroll()
return clean
}
/**
* 设置滚动条位置 writing-mode: vertical-rl 专用)
* @param element 要设置滚动的容器 dom
* @param to 滚动的目标位置
* @param duration 滚动完成时间 ms
* @param fn 滚动完成后的回调
* @param delay 延迟执行时间
*/
export const scrollXRTo = (
element: ScrollElement<HTMLElement>,
to: number,
duration = 300,
fn = () => {},
delay = 0
): (() => void) => {
let cancelFn: Noop
if (element.lx_scrollDelayTimeout != null) {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
if (delay) {
let scrollCancelFn: Noop
cancelFn = () => {
if (element.lx_scrollDelayTimeout == null) {
scrollCancelFn?.()
} else {
window.clearTimeout(element.lx_scrollDelayTimeout)
element.lx_scrollDelayTimeout = undefined
}
}
element.lx_scrollDelayTimeout = window.setTimeout(() => {
element.lx_scrollDelayTimeout = undefined
scrollCancelFn = handleScrollXR(element, to, duration, fn)
}, delay)
} else {
cancelFn = handleScrollXR(element, to, duration, fn)
}
return cancelFn
}
/**
* 设置标题
*/
let dom_title = document.getElementsByTagName('title')[0]
export const setTitle = (title: string | null) => {
title ||= 'LX Music'
dom_title.innerText = title
}

148
src/common/utils/tools.ts Normal file
View File

@@ -0,0 +1,148 @@
// 业务工具方法
export const toNewMusicInfo = (oldMusicInfo: any): LX.Music.MusicInfo => {
const meta: Record<string, any> = {
songId: oldMusicInfo.songmid, // 歌曲IDlocal为文件路径
albumName: oldMusicInfo.albumName, // 歌曲专辑名称
picUrl: oldMusicInfo.img // 歌曲图片链接
}
const newInfo = {
id: `${oldMusicInfo.source}_${oldMusicInfo.songmid}`,
name: oldMusicInfo.name,
singer: oldMusicInfo.singer,
source: oldMusicInfo.source,
interval: oldMusicInfo.interval,
meta: meta as LX.Music.MusicInfoOnline['meta']
}
if (oldMusicInfo.source == 'local') {
meta.filePath = oldMusicInfo.filePath ?? oldMusicInfo.songmid ?? ''
meta.ext = oldMusicInfo.ext ?? /\.(\w+)$/.exec(meta.filePath)?.[1] ?? ''
} else {
meta.qualitys = oldMusicInfo.types
meta._qualitys = oldMusicInfo._types
meta.albumId = oldMusicInfo.albumId
if (meta._qualitys.flac32bit && !meta._qualitys.flac24bit) {
meta._qualitys.flac24bit = meta._qualitys.flac32bit
delete meta._qualitys.flac32bit
meta.qualitys = (meta.qualitys as any[]).map((quality) => {
if (quality.type == 'flac32bit') quality.type = 'flac24bit'
return quality
})
}
switch (oldMusicInfo.source) {
case 'kg':
meta.hash = oldMusicInfo.hash
newInfo.id = oldMusicInfo.songmid + '_' + oldMusicInfo.hash
break
case 'tx':
meta.strMediaMid = oldMusicInfo.strMediaMid
meta.id = oldMusicInfo.songId
meta.albumMid = oldMusicInfo.albumMid
break
case 'mg':
meta.copyrightId = oldMusicInfo.copyrightId
meta.lrcUrl = oldMusicInfo.lrcUrl
meta.mrcUrl = oldMusicInfo.mrcUrl
meta.trcUrl = oldMusicInfo.trcUrl
break
}
}
return newInfo
}
export const toOldMusicInfo = (minfo: LX.Music.MusicInfo) => {
const oInfo: Record<string, any> = {
name: minfo.name,
singer: minfo.singer,
source: minfo.source,
songmid: minfo.meta.songId,
interval: minfo.interval,
albumName: minfo.meta.albumName,
img: minfo.meta.picUrl ?? '',
typeUrl: {}
}
if (minfo.source == 'local') {
oInfo.filePath = minfo.meta.filePath
oInfo.ext = minfo.meta.ext
oInfo.albumId = ''
oInfo.types = []
oInfo._types = {}
} else {
oInfo.albumId = minfo.meta.albumId
oInfo.types = minfo.meta.qualitys
oInfo._types = minfo.meta._qualitys
switch (minfo.source) {
case 'kg':
oInfo.hash = minfo.meta.hash
break
case 'tx':
oInfo.strMediaMid = minfo.meta.strMediaMid
oInfo.albumMid = minfo.meta.albumMid
oInfo.songId = minfo.meta.id
break
case 'mg':
oInfo.copyrightId = minfo.meta.copyrightId
oInfo.lrcUrl = minfo.meta.lrcUrl
oInfo.mrcUrl = minfo.meta.mrcUrl
oInfo.trcUrl = minfo.meta.trcUrl
break
}
}
return oInfo
}
/**
* 修复2.0.0-dev.8之前的新列表数据音质
* @param musicInfo
*/
export const fixNewMusicInfoQuality = (musicInfo: LX.Music.MusicInfo) => {
if (musicInfo.source == 'local') return musicInfo
// @ts-expect-error
if (musicInfo.meta._qualitys.flac32bit && !musicInfo.meta._qualitys.flac24bit) {
// @ts-expect-error
musicInfo.meta._qualitys.flac24bit = musicInfo.meta._qualitys.flac32bit
// @ts-expect-error
delete musicInfo.meta._qualitys.flac32bit
musicInfo.meta.qualitys = musicInfo.meta.qualitys.map((quality) => {
// @ts-expect-error
if (quality.type == 'flac32bit') quality.type = 'flac24bit'
return quality
})
}
return musicInfo
}
export const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] => {
const ids = new Set<string>()
return list.filter((s) => {
if (!s.id || ids.has(s.id) || !s.name) return false
if (s.singer == null) s.singer = ''
ids.add(s.id)
return true
})
}
const MAX_NAME_LENGTH = 80
const MAX_FILE_NAME_LENGTH = 150
export const clipNameLength = (name: string) => {
if (name.length <= MAX_NAME_LENGTH || !name.includes('、')) return name
const names = name.split('、')
let newName = names.shift()!
for (const name of names) {
if (newName.length + name.length > MAX_NAME_LENGTH) break
newName = newName + '、' + name
}
return newName
}
export const clipFileNameLength = (name: string) => {
return name.length > MAX_FILE_NAME_LENGTH ? name.substring(0, MAX_FILE_NAME_LENGTH) : name
}

View File

@@ -6,6 +6,16 @@ import path from 'node:path'
import musicService from './services/music'
import pluginService from './services/plugin'
import aiEvents from './events/ai'
import './services/musicSdk/index'
// import wy from './utils/musicSdk/wy/index'
// import kg from './utils/musicSdk/kg/index'
// wy.hotSearch.getList().then((res) => {
// console.log(res)
// })
// kg.hotSearch.getList().then((res) => {
// console.log(res)
// })
let tray: Tray | null = null
let mainWindow: BrowserWindow | null = null
let isQuitting = false
@@ -60,6 +70,7 @@ function createTray(): void {
}
function createWindow(): void {
// return
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1100,
@@ -179,6 +190,7 @@ ipcMain.handle('service-music-request', async (_, api, args) => {
return await musicService.request(api, args)
})
aiEvents(mainWindow)
// This method will be called when Electron has finished
@@ -186,6 +198,7 @@ aiEvents(mainWindow)
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.cerulean.music')
// 初始化插件系统

View File

@@ -1,5 +1,4 @@
import { MusicServiceBase, ServiceNamesType, ServiceArgsType } from './service-base'
import {
GetToplistArgs,
SearchArgs,
@@ -10,7 +9,6 @@ import {
GetListSongsArgs,
DownloadSingleSongArgs
} from './service-base'
import { netEaseService } from './net-ease-service'
import { AxiosError } from 'axios'
@@ -68,3 +66,12 @@ async function request(api: ServiceNamesType, args: ServiceArgsType): Promise<an
}
export default { request }
// netEaseService
// .search({
// keyword: '稻香',
// type: 1,
// limit: 25
// })
// .then((res) => {
// console.log(res)
// })

View File

@@ -5,6 +5,8 @@ import fsPromise from 'fs/promises'
import NeteaseCloudMusicApi from 'NeteaseCloudMusicApi'
import { axiosClient, MusicServiceBase } from './service-base'
import { pipeline } from 'node:stream/promises'
import pluginService from '../plugin'
import musicSdk from '../../utils/musicSdk'
import {
SearchArgs,
@@ -22,13 +24,110 @@ import { SongDetailResponse, SongResponse } from './service-base'
import { fieldsSelector } from '../../utils/object'
import { getAppDirPath } from '../../utils/path'
// 音乐源映射
const MUSIC_SOURCES = {
kg: 'kg', // 酷狗音乐
wy: 'wy', // 网易云音乐
tx: 'tx', // QQ音乐
kw: 'kw', // 酷我音乐
mg: 'mg' // 咪咕音乐
}
// 扩展搜索参数接口
interface ExtendedSearchArgs extends SearchArgs {
source?: string // 音乐源参数 kg|wy|tx|kw|mg
}
// 扩展歌曲详情参数接口
interface ExtendedGetSongDetailArgs extends GetSongDetailArgs {
source?: string
}
// 扩展歌词参数接口
interface ExtendedGetLyricArgs extends GetLyricArgs {
source?: string
}
const baseUrl: string = 'https://music.163.com'
const baseTwoUrl: string = 'https://www.lihouse.xyz/coco_widget'
const fileLock: Record<string, boolean> = {}
/**
* 获取支持的音乐源列表
*/
export const getSupportedSources = () => {
return Object.keys(MUSIC_SOURCES).map((key) => ({
id: key,
name: getSourceName(key),
available: !!musicSdk[key]
}))
}
/**
* 获取音乐源名称
*/
const getSourceName = (source: string): string => {
const sourceNames = {
kg: '酷狗音乐',
wy: '网易云音乐',
tx: 'QQ音乐',
kw: '酷我音乐',
mg: '咪咕音乐'
}
return sourceNames[source] || source
}
/**
* 智能音乐匹配使用musicSdk的findMusic功能
*/
export const findMusic = async (musicInfo: {
name: string
singer?: string
albumName?: string
interval?: string
source?: string
}) => {
try {
return await musicSdk.findMusic(musicInfo)
} catch (error) {
console.error('智能音乐匹配失败:', error)
return []
}
}
export const netEaseService: MusicServiceBase = {
async search({ type, keyword, offset, limit }: SearchArgs): Promise<SongResponse> {
async search({
type,
keyword,
offset,
limit,
source
}: ExtendedSearchArgs): Promise<SongResponse> {
// 如果指定了音乐源且不是网易云使用对应的musicSdk
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
try {
const sourceModule = musicSdk[source]
if (sourceModule && sourceModule.musicSearch) {
const page = Math.floor((offset || 0) / (limit || 25)) + 1
const result = await sourceModule.musicSearch.search(keyword, page, limit || 25)
// 转换为统一格式
return {
songs: result.list || [],
songCount: result.total || result.list?.length || 0
}
} else {
throw new Error(`不支持的音乐源: ${source}`)
}
} catch (error: any) {
console.error(`${source}音乐源搜索失败:`, error)
// 如果指定源失败,回退到网易云
console.log('回退到网易云音乐搜索')
}
}
// 默认使用网易云音乐搜索
return await axiosClient
.get(`${baseUrl}/api/search/get/web`, {
params: {
@@ -46,7 +145,27 @@ export const netEaseService: MusicServiceBase = {
return data.result
})
},
async getSongDetail({ ids }: GetSongDetailArgs): Promise<SongDetailResponse> {
async getSongDetail({ ids, source }: ExtendedGetSongDetailArgs): Promise<SongDetailResponse> {
// 如果指定了音乐源且不是网易云使用对应的musicSdk
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
try {
const sourceModule = musicSdk[source]
if (sourceModule && sourceModule.musicInfo) {
// 对于多个ID并行获取详情
const promises = ids.map((id) => sourceModule.musicInfo.getMusicInfo(id))
const results = await Promise.all(promises)
return results.filter((result :any) => result) // 过滤掉失败的结果
} else {
throw new Error(`不支持的音乐源: ${source}`)
}
} catch (error: any) {
console.error(`${source}音乐源获取歌曲详情失败:`, error)
// 如果指定源失败,回退到网易云
console.log('回退到网易云音乐获取歌曲详情')
}
}
// 默认使用网易云音乐
return await axiosClient
.get(`${baseUrl}/api/song/detail?ids=[${ids.join(',')}]`)
.then(({ data }) => {
@@ -58,7 +177,48 @@ export const netEaseService: MusicServiceBase = {
return data.songs
})
},
async getSongUrl({ id }: GetSongUrlArgs): Promise<any> {
async getSongUrl({ id, pluginId, quality, source }: GetSongUrlArgs): Promise<any> {
// 如果提供了插件ID、音质和音乐源则使用插件获取音乐URL
if (pluginId && (quality || source)) {
try {
// 获取插件实例
const plugin = pluginService.getPluginById(pluginId)
if (!plugin) {
throw new Error(`未找到ID为 ${pluginId} 的插件`)
}
// 准备音乐信息对象确保符合MusicInfo类型要求
const musicInfo = {
songmid: id as unknown as number,
singer: '',
name: '',
albumName: '',
albumId: 0,
source: source || 'wy',
interval: '',
img: '',
lrc: null,
types: [],
_types: {},
typeUrl: {}
}
// 调用插件的getMusicUrl方法获取音乐URL
const url: string = await plugin.getMusicUrl(
source || 'wy',
musicInfo,
quality || 'standard'
)
// 构建返回对象
return { url }
} catch (error: any) {
console.error('通过插件获取音乐URL失败:', error)
throw new Error(`插件获取音乐URL失败: ${error.message}`)
}
}
// 如果没有提供插件信息或插件调用失败,则使用默认方法获取
return await axiosClient.get(`${baseTwoUrl}/music_resource/id/${id}`).then(({ data }) => {
if (!data.status) {
throw new Error('歌曲不存在')
@@ -67,8 +227,33 @@ export const netEaseService: MusicServiceBase = {
return data.song_data
})
},
async getLyric({ id, lv, yv, tv }: GetLyricArgs): Promise<any> {
// 默认什么都不获取
async getLyric({ id, lv, yv, tv, source }: ExtendedGetLyricArgs): Promise<any> {
// 如果指定了音乐源且不是网易云使用对应的musicSdk
if (source && source !== 'wy' && MUSIC_SOURCES[source]) {
try {
const sourceModule = musicSdk[source]
if (sourceModule && sourceModule.getLyric) {
// 构建歌曲信息对象,不同源可能需要不同的参数
const songInfo = { id, songmid: id, hash: id }
const result = await sourceModule.getLyric(songInfo)
// 转换为统一格式
return {
lrc: { lyric: result.lyric || '' },
tlyric: { lyric: result.tlyric || '' },
yrc: { lyric: result.yrc || '' }
}
} else {
throw new Error(`不支持的音乐源: ${source}`)
}
} catch (error: any) {
console.error(`${source}音乐源获取歌词失败:`, error)
// 如果指定源失败,回退到网易云
console.log('回退到网易云音乐获取歌词')
}
}
// 默认使用网易云音乐
const optionalParams: any = {}
if (lv) {
optionalParams.lv = -1
@@ -137,14 +322,42 @@ export const netEaseService: MusicServiceBase = {
throw err.body?.msg ?? err
})
},
async downloadSingleSong({ id }: DownloadSingleSongArgs) {
const songDownloadDetail = await this.getSongUrl({ id })
async downloadSingleSong({
id,
name,
artist,
pluginId,
source,
quality
}: DownloadSingleSongArgs) {
const { url } = await this.getSongUrl({ id, pluginId, source, quality })
// 从URL中提取文件扩展名如果没有则默认为mp3
const getFileExtension = (url: string): string => {
try {
const urlObj = new URL(url)
const pathname = urlObj.pathname
const lastDotIndex = pathname.lastIndexOf('.')
if (lastDotIndex !== -1 && lastDotIndex < pathname.length - 1) {
const extension = pathname.substring(lastDotIndex + 1).toLowerCase()
// 验证是否为常见的音频格式
const validExtensions = ['mp3', 'flac', 'wav', 'aac', 'm4a', 'ogg', 'wma']
if (validExtensions.includes(extension)) {
return extension
}
}
} catch (error) {
console.warn('解析URL失败使用默认扩展名:', error)
}
return 'mp3' // 默认扩展名
}
const fileExtension = getFileExtension(url)
const songPath = path.join(
getAppDirPath(),
'download',
'songs',
`${songDownloadDetail.name}-${songDownloadDetail.artist}-${songDownloadDetail.id}.mp3`
`${name}-${artist}-${id}.${fileExtension}`
.replace(/[/\\:*?"<>|]/g, '')
.replace(/^\.+/, '')
.replace(/\.+$/, '')
@@ -168,7 +381,7 @@ export const netEaseService: MusicServiceBase = {
const songDataRes = await axiosClient({
method: 'GET',
url: songDownloadDetail.url,
url: url,
responseType: 'stream'
})

View File

@@ -16,6 +16,7 @@ type SearchArgs = {
keyword: string
offset?: number
limit: number
source?: string
}
type GetSongDetailArgs = {
@@ -24,6 +25,9 @@ type GetSongDetailArgs = {
type GetSongUrlArgs = {
id: string
pluginId?: string // 插件ID
quality?: string // 音质
source?: string // 音乐源wy, tx等
}
type GetLyricArgs = {
@@ -45,6 +49,11 @@ type GetListSongsArgs = {
type DownloadSingleSongArgs = {
id: string
name: string
artist: string
pluginId?: string
quality?: string
source?: string
}
type ServiceNamesType =
@@ -243,7 +252,7 @@ interface SongUrlResponse {
interface MusicServiceBase {
search({ type, keyword, offset, limit }: SearchArgs): Promise<SongResponse>
getSongDetail({ ids }: GetSongDetailArgs): Promise<SongDetailResponse>
getSongUrl({ id }: GetSongUrlArgs): Promise<SongUrlResponse>
getSongUrl({ id, pluginId, quality, source }: GetSongUrlArgs): Promise<SongUrlResponse>
getLyric({ id, lv, yv, tv }: GetLyricArgs): Promise<any>
getToplist({}: GetToplistArgs): Promise<any>
getToplistDetail({}: GetToplistDetailArgs): Promise<any>

View File

@@ -0,0 +1,20 @@
import main from './service'
import { ipcMain } from 'electron';
export type MainApi = ReturnType<typeof main>;
export type MethodParams<T extends keyof MainApi> =
MainApi[T] extends (...args: any[]) => any
? Parameters<MainApi[T]>[0]
: never;
export function request<T extends keyof MainApi>(_: any, method: T,
options: {
source: any;
} & (MethodParams<T> extends object ? MethodParams<T> : { [key: string]: any })): ReturnType<MainApi[T]> {
const { source, ...args } = options;
if (!source) throw new Error('请配置音源')
const Api = main(source);
if (Api.hasOwnProperty(method)) {
return (Api[method] as (args: any) => any)(args);
}
throw new Error(`未知的方法: ${method}`);
}
ipcMain.handle('service-music-sdk-request', request)

View File

@@ -0,0 +1,35 @@
import { SearchSongArg, SearchResult, GetMusicUrlArg, GetMusicPicArg } from './type'
import pluginService from '../plugin/index'
import musicSdk from '../../utils/musicSdk/index'
function main(source: string) {
const Api = musicSdk[source]
Api.songList
return {
async search({ keyword, page = 1, limit = 30 }: SearchSongArg) {
return await Api.musicSearch.search(keyword, page, limit) as Promise<SearchResult>
},
async getMusicUrl({ pluginId, songInfo, quality }: GetMusicUrlArg) {
try {
const usePlugin = pluginService.getPluginById(pluginId)
if (!pluginId || !usePlugin) return { error: '请配置音源来播放歌曲' }
return await usePlugin.getMusicUrl(source, songInfo , quality)
} catch (e: any) {
return {
error: '获取歌曲失败 ' + e.error || e
}
}
},
async getPic({songInfo}: GetMusicPicArg) {
try {
return await Api.getPic(songInfo)
} catch (e: any) {
return {
error: '获取歌曲失败 ' + e.error || e
}
}
}
}
}
export default main

View File

@@ -0,0 +1,51 @@
export interface sdkArg {
source: string
[key:string]: any
}
export interface SearchSongArg {
keyword: string
page: number
limit: number
}
// 可以添加到 src/main/services/music/service-base.ts 文件中
// 单首歌曲的类型定义
export interface MusicItem {
hash?: string
singer: string
name: string
albumName: string
albumId: number
source: string
interval: string
songmid: number
img: string
lrc: null | string
types: string[]
_types: Record<string, any>
typeUrl: Record<string, any>
}
// 搜索结果的类型定义
export interface SearchResult {
list: MusicItem[]
allPage: number
limit: number
total: number
source: string
}
export interface GetMusicUrlArg {
pluginId: string
songInfo: MusicItem
quality: string
}
export interface GetMusicPicArg {
songInfo: MusicItem
}
export interface GetLyricArg {
songInfo: MusicItem
}

View File

@@ -101,7 +101,7 @@ const pluginService = {
}
},
getPluginById(pluginId: string) {
getPluginById(pluginId: string): CeruMusicPluginHost | null{
if (!Object.hasOwn(loadedPlugins, pluginId)) {
return null
}

View File

@@ -66,19 +66,27 @@ class Logger {
}
log(...args: any[]): void {
this.write(`log ${args.join(' ')}`)
this.write(`log ${parseArgs(args)}\n`)
}
info(...args: any[]): void {
this.write(`info ${args.join(' ')}`)
this.write(`info ${parseArgs(args)}\n`)
}
warn(...args: any[]): void {
this.write(`warn ${args.join(' ')}`)
this.write(`warn ${parseArgs(args)}\n`)
}
error(...args: any[]): void {
this.write(`error ${args.join(' ')}`)
this.write(`error ${parseArgs(args)}\n`)
}
group(...args: any[]): void {
args.unshift('groupStart---------')
this.write(`start ${parseArgs(args)}\n`)
}
groupEnd(...args: any[]): void {
this.write(`end ${parseArgs(args)}\n`)
}
private write(msg: string): void {
@@ -86,6 +94,16 @@ class Logger {
}
}
function parseArgs(args){
return args.map(arg=>{
if(typeof arg==='object'){
return JSON.stringify(arg)
}
return arg
}).join(' ')
}
async function getLog(pluginId: string) {
const logFilePath: string = getLogPath(pluginId)
return await add_promise(

View File

@@ -1,6 +1,7 @@
import * as vm from 'vm'
import fetch from 'node-fetch'
import * as fs from 'fs'
import {MusicItem} from '../../musicSdk/type'
// 定义插件结构接口
export interface PluginInfo {
@@ -25,9 +26,8 @@ interface CeruMusicPlugin {
getLyric?: (source: string, musicInfo: MusicInfo) => Promise<string>
}
interface MusicInfo {
id: string
[key: string]: any
interface MusicInfo extends MusicItem{
id?: string
}
interface RequestResult {
@@ -155,21 +155,8 @@ class CeruMusicPluginHost {
// 尝试解析JSON如果失败则返回文本
let body: any
const contentType = response.headers.get('content-type')
try {
if (contentType && contentType.includes('application/json')) {
body = await response.json()
} else {
const text = await response.text()
console.log(`[CeruMusic] 响应不是JSON格式内容: ${text.substring(0, 200)}...`)
// 对于非JSON响应创建一个错误状态的body
body = {
code: response.status,
msg: `Expected JSON response but got: ${contentType || 'unknown content type'}`,
data: text
}
}
} catch (parseError: any) {
console.error(`[CeruMusic] 解析响应失败: ${parseError.message}`)
// 解析失败时创建错误body

60
src/main/utils/index.ts Normal file
View File

@@ -0,0 +1,60 @@
// 导入通用工具函数
import { dateFormat } from '../../common/utils/common'
// 导出通用工具函数
export * from '../../common/utils/nodejs'
export * from '../../common/utils/common'
export * from '../../common/utils/tools'
/**
* 格式化播放数量
* @param {*} num 数字
*/
export const formatPlayCount = (num: number): string => {
if (num > 100000000) return `${Math.trunc(num / 10000000) / 10}亿`
if (num > 10000) return `${Math.trunc(num / 1000) / 10}`
return String(num)
}
/**
* 时间格式化 - 主进程版本
*/
export const dateFormat2 = (time: number): string => {
const differ = Math.trunc((Date.now() - time) / 1000)
if (differ < 60) {
return `${differ}秒前`
} else if (differ < 3600) {
return `${Math.trunc(differ / 60)}分钟前`
} else if (differ < 86400) {
return `${Math.trunc(differ / 3600)}小时前`
} else {
return dateFormat(time)
}
}
// 定义简单的音乐信息接口
interface MusicInfo {
id: string
[key: string]: any
}
export const deduplicationList = <T extends MusicInfo>(list: T[]): T[] => {
const ids = new Set<string>()
return list.filter((s) => {
if (ids.has(s.id)) return false
ids.add(s.id)
return true
})
}
// 主进程中的字符串解码函数
export const decodeName = (str: string | null = '') => {
if (!str) return ''
// 在 Node.js 中使用简单的 HTML 实体解码
return str
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
}

View File

@@ -0,0 +1,32 @@
// Support qualitys: 128k 320k flac wav
const sources: Array<{
id: string
name: string
disabled: boolean
supportQualitys: any
}> = [
// {
// id: 'test',
// name: '测试接口',
// disabled: false,
// supportQualitys: {
// kw: ['128k'],
// kg: ['128k'],
// tx: ['128k'],
// wy: ['128k'],
// mg: ['128k'],
// // bd: ['128k'],
// },
// },
// {
// id: 'temp',
// name: '临时接口',
// disabled: false,
// supportQualitys: {
// kw: ['128k'],
// },
// },
]
export default sources

View File

@@ -0,0 +1,207 @@
import kw from './kw/index'
import kg from './kg/index'
import tx from './tx/index'
import wy from './wy/index'
import mg from './mg/index'
// 导入工具函数
export {
decodeName,
formatPlayTime,
sizeFormate,
dateFormat,
formatPlayCount,
dateFormat2
} from '../utils'
const sources = {
sources: [
{
name: '酷我音乐',
id: 'kw'
},
{
name: '酷狗音乐',
id: 'kg'
},
{
name: 'QQ音乐',
id: 'tx'
},
{
name: '网易音乐',
id: 'wy'
},
{
name: '咪咕音乐',
id: 'mg'
},
{
name: '虾米音乐',
id: 'xm'
}
],
kw,
kg,
tx,
wy,
mg
}
export default {
...sources,
init() {
const tasks = []
for (let source of sources.sources) {
let sm = sources[source.id]
sm && sm.init && tasks.push(sm.init())
}
return Promise.all(tasks)
},
async searchMusic({ name, singer, source: s, limit = 25 }) {
const trimStr = (str) => (typeof str == 'string' ? str.trim() : str)
const musicName = trimStr(name)
const tasks = []
const excludeSource = ['xm']
for (const source of sources.sources) {
if (!sources[source.id].musicSearch || source.id == s || excludeSource.includes(source.id))
continue
tasks.push(
sources[source.id].musicSearch
.search(`${musicName} ${singer || ''}`.trim(), 1, limit)
.catch((_) => null)
)
}
return (await Promise.all(tasks)).filter((s) => s)
},
async findMusic({ name, singer, albumName, interval, source: s }) {
const lists = await this.searchMusic({ name, singer, source: s, limit: 25 })
// console.log(lists)
// console.log({ name, singer, albumName, interval, source: s })
const singersRxp = /、|&|;||\/|,||\|/
const sortSingle = (singer) =>
singersRxp.test(singer)
? singer
.split(singersRxp)
.sort((a, b) => a.localeCompare(b))
.join('、')
: singer || ''
const sortMusic = (arr, callback) => {
const tempResult = []
for (let i = arr.length - 1; i > -1; i--) {
const item = arr[i]
if (callback(item)) {
delete item.fSinger
delete item.fMusicName
delete item.fAlbumName
delete item.fInterval
tempResult.push(item)
arr.splice(i, 1)
}
}
tempResult.reverse()
return tempResult
}
const getIntv = (interval) => {
if (!interval) return 0
// if (musicInfo._interval) return musicInfo._interval
let intvArr = interval.split(':')
let intv = 0
let unit = 1
while (intvArr.length) {
intv += parseInt(intvArr.pop()) * unit
unit *= 60
}
return intv
}
const trimStr = (str) => (typeof str == 'string' ? str.trim() : str || '')
const filterStr = (str) =>
typeof str == 'string'
? str.replace(/\s|'|\.|,||&|"|、|\(|\)|||`|~|-|<|>|\||\/|\]|\[|!|/g, '')
: String(str || '')
const fMusicName = filterStr(name).toLowerCase()
const fSinger = filterStr(sortSingle(singer)).toLowerCase()
const fAlbumName = filterStr(albumName).toLowerCase()
const fInterval = getIntv(interval)
const isEqualsInterval = (intv) => Math.abs((fInterval || intv) - (intv || fInterval)) < 5
const isIncludesName = (name) => fMusicName.includes(name) || name.includes(fMusicName)
const isIncludesSinger = (singer) =>
fSinger ? fSinger.includes(singer) || singer.includes(fSinger) : true
const isEqualsAlbum = (album) => (fAlbumName ? fAlbumName == album : true)
const result = lists
.map((source) => {
for (const item of source.list) {
item.name = trimStr(item.name)
item.singer = trimStr(item.singer)
item.fSinger = filterStr(sortSingle(item.singer).toLowerCase())
item.fMusicName = filterStr(String(item.name ?? '').toLowerCase())
item.fAlbumName = filterStr(String(item.albumName ?? '').toLowerCase())
item.fInterval = getIntv(item.interval)
// console.log(fMusicName, item.fMusicName, item.source)
if (!isEqualsInterval(item.fInterval)) {
item.name = null
continue
}
if (item.fMusicName == fMusicName && isIncludesSinger(item.fSinger)) return item
}
for (const item of source.list) {
if (item.name == null) continue
if (item.fSinger == fSinger && isIncludesName(item.fMusicName)) return item
}
for (const item of source.list) {
if (item.name == null) continue
if (
isEqualsAlbum(item.fAlbumName) &&
isIncludesSinger(item.fSinger) &&
isIncludesName(item.fMusicName)
)
return item
}
return null
})
.filter((s) => s)
const newResult = []
if (result.length) {
newResult.push(
...sortMusic(
result,
(item) =>
item.fSinger == fSinger && item.fMusicName == fMusicName && item.interval == interval
)
)
newResult.push(
...sortMusic(
result,
(item) =>
item.fMusicName == fMusicName &&
item.fSinger == fSinger &&
item.fAlbumName == fAlbumName
)
)
newResult.push(
...sortMusic(result, (item) => item.fSinger == fSinger && item.fMusicName == fMusicName)
)
newResult.push(
...sortMusic(result, (item) => item.fMusicName == fMusicName && item.interval == interval)
)
newResult.push(
...sortMusic(result, (item) => item.fSinger == fSinger && item.interval == interval)
)
newResult.push(...sortMusic(result, (item) => item.interval == interval))
newResult.push(...sortMusic(result, (item) => item.fMusicName == fMusicName))
newResult.push(...sortMusic(result, (item) => item.fSinger == fSinger))
newResult.push(...sortMusic(result, (item) => item.fAlbumName == fAlbumName))
for (const item of result) {
delete item.fSinger
delete item.fMusicName
delete item.fAlbumName
delete item.fInterval
}
newResult.push(...result)
}
// console.log(newResult)
return newResult
}
}

View File

@@ -0,0 +1,69 @@
import { getMusicInfosByList } from './musicInfo'
import { createHttpFetch } from './util'
export default {
/**
* 通过AlbumId获取专辑信息
* @param {*} id
*/
async getAlbumInfo(id) {
const albumInfoRequest = await createHttpFetch(
'http://kmrserviceretry.kugou.com/container/v1/album?dfid=1tT5He3kxrNC4D29ad1MMb6F&mid=22945702112173152889429073101964063697&userid=0&appid=1005&clientver=11589',
{
method: 'POST',
body: {
appid: 1005,
clienttime: 1681833686,
clientver: 11589,
data: [{ album_id: id }],
fields:
'language,grade_count,intro,mix_intro,heat,category,sizable_cover,cover,album_name,type,quality,publish_company,grade,special_tag,author_name,publish_date,language_id,album_id,exclusive,is_publish,trans_param,authors,album_tag',
isBuy: 0,
key: 'e6f3306ff7e2afb494e89fbbda0becbf',
mid: '22945702112173152889429073101964063697',
show_album_tag: 0
}
}
)
if (!albumInfoRequest) return Promise.reject(new Error('get album info failed.'))
const albumInfo = albumInfoRequest[0]
return {
name: albumInfo.album_name,
image: albumInfo.sizable_cover.replace('{size}', 240),
desc: albumInfo.intro,
authorName: albumInfo.author_name
// play_count: this.formatPlayCount(info.count),
}
},
/**
* 通过AlbumId获取专辑
* @param {*} id
* @param {*} page
*/
async getAlbumDetail(id, page = 1, limit = 200) {
const albumList = await createHttpFetch(
`http://mobiles.kugou.com/api/v3/album/song?version=9108&albumid=${id}&plat=0&pagesize=${limit}&area_code=0&page=${page}&with_res_tag=0`
)
if (!albumList.info) return Promise.reject(new Error('Get album list failed.'))
let result = await getMusicInfosByList(albumList.info)
const info = await this.getAlbumInfo(id)
return {
list: result || [],
page,
limit,
total: albumList.total,
source: 'kg',
info: {
name: info.name,
img: info.image,
desc: info.desc,
author: info.authorName
// play_count: this.formatPlayCount(info.count),
}
}
}
}

View File

@@ -0,0 +1,59 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_test = {
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(
`http://ts.tempmusics.tk/url/kg/${songInfo._types[type].hash}/${type}`,
{
method: 'get',
timeout,
headers,
lookup: dnsLookup,
family: 4
}
)
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(requestMsg.fail))
}
})
return requestObj
},
getPic(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/pic/kg/${songInfo.hash}`, {
method: 'get',
timeout,
headers,
family: 4
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 0
? Promise.resolve(body.data)
: Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getLyric(songInfo) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/lrc/kg/${songInfo.hash}`, {
method: 'get',
timeout,
headers,
family: 4
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 0
? Promise.resolve(body.data)
: Promise.reject(new Error(requestMsg.fail))
})
return requestObj
}
}
export default api_test

View File

@@ -0,0 +1,129 @@
import { httpFetch } from '../../request'
import { decodeName, dateFormat2 } from '../index'
import { signatureParams } from './util'
// import { getMusicInfoRaw } from './musicInfo'
export default {
_requestObj: null,
_requestObj2: null,
async getComment({ hash }, page = 1, limit = 20) {
if (this._requestObj) this._requestObj.cancelHttp()
// const res_id = (await getMusicInfoRaw(hash)).classification?.[0]?.res_id
// if (!res_id) throw new Error('获取评论失败')
let timestamp = Date.now()
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
// const params = `appid=1005&clienttime=${timestamp}&clienttoken=0&clientver=11409&code=fc4be23b4e972707f36b8a828a93ba8a&dfid=0&extdata=${hash}&kugouid=0&mid=16249512204336365674023395779019&mixsongid=${res_id}&p=${page}&pagesize=${limit}&uuid=0&ver=10`
const _requestObj = httpFetch(
`http://m.comment.service.kugou.com/r/v1/rank/newest?${params}&signature=${signatureParams(params)}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.24'
}
}
)
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取评论失败')
const total = body.count ?? 0
return {
source: 'kg',
comments: this.filterComment(body.list || []),
total,
page,
limit,
maxPage: Math.ceil(total / limit) || 1
}
},
async getHotComment({ hash }, page = 1, limit = 20) {
// console.log(songmid)
if (this._requestObj2) this._requestObj2.cancelHttp()
let timestamp = Date.now()
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
// https://github.com/GitHub-ZC/wp_MusicApi/blob/bf9307dd138dc8ac6c4f7de29361209d4f5b665f/routes/v1/kugou/comment.js#L53
const _requestObj2 = httpFetch(
`http://m.comment.service.kugou.com/r/v1/rank/topliked?${params}&signature=${signatureParams(params)}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.24'
}
}
)
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取热门评论失败')
const total = body.count ?? 0
return {
source: 'kg',
comments: this.filterComment(body.list || []),
total,
page,
limit,
maxPage: Math.ceil(total / limit) || 1
}
},
async getReplyComment({ songmid, audioId }, replyId, page = 1, limit = 100) {
if (this._requestObj2) this._requestObj2.cancelHttp()
songmid =
songmid.length == 32 // 修复歌曲ID存储变更导致图片获取失败的问题
? audioId.split('_')[0]
: songmid
const _requestObj2 = httpFetch(
`http://comment.service.kugou.com/index.php?r=commentsv2/getReplyWithLike&code=fc4be23b4e972707f36b8a828a93ba8a&p=${page}&pagesize=${limit}&ver=1.01&clientver=8373&kugouid=687373022&need_show_image=1&appid=1001&childrenid=${songmid}&tid=${replyId}`,
{
headers: {
'User-Agent': 'Android712-AndroidPhone-8983-18-0-COMMENT-wifi'
}
}
)
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.err_code !== 0) throw new Error('获取回复评论失败')
return { source: 'kg', comments: this.filterComment(body.list || []) }
},
replaceAt(raw, atList) {
atList.forEach((atobj) => {
raw = raw.replaceAll(`[at=${atobj.id}]`, `@${atobj.name} `)
})
return raw
},
filterComment(rawList) {
return rawList.map((item) => {
let data = {
id: item.id,
text: decodeName(
(item.atlist ? this.replaceAt(item.content, item.atlist) : item.content) || ''
),
images: item.images ? item.images.map((i) => i.url) : [],
location: item.location,
time: item.addtime,
timeStr: dateFormat2(new Date(item.addtime).getTime()),
userName: item.user_name,
avatar: item.user_pic,
userId: item.user_id,
likedCount: item.like.likenum,
replyNum: item.reply_num,
reply: []
}
return item.pcontent
? {
id: item.id,
text: decodeName(item.pcontent),
time: null,
userName: item.puser,
avatar: null,
userId: item.puser_id,
likedCount: null,
replyNum: null,
reply: [data]
}
: data
})
}
}

View File

@@ -0,0 +1,36 @@
import { httpFetch } from '../../request'
import { decodeName } from '../index'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = httpFetch(
'http://gateway.kugou.com/api/v3/search/hot_tab?signature=ee44edb9d7155821412d220bcaf509dd&appid=1005&clientver=10026&plat=0',
{
method: 'get',
headers: {
dfid: '1ssiv93oVqMp27cirf2CvoF1',
mid: '156798703528610303473757548878786007104',
clienttime: 1584257267,
'x-router': 'msearch.kugou.com',
'user-agent': 'Android9-AndroidPhone-10020-130-0-searchrecommendprotocol-wifi',
'kg-rc': 1
}
}
)
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.errcode !== 0) throw new Error('获取热搜词失败')
// console.log(body, statusCode)
return { source: 'kg', list: this.filterList(body.data.list) }
},
filterList(rawList) {
const list = []
rawList.forEach((item) => {
item.keywords.map((k) => list.push(decodeName(k.keyword)))
})
return list
}
}

View File

@@ -0,0 +1,28 @@
import leaderboard from './leaderboard'
import songList from './songList'
import musicSearch from './musicSearch'
import pic from './pic'
import lyric from './lyric'
import hotSearch from './hotSearch'
import comment from './comment'
// import tipSearch from './tipSearch'
const kg = {
// tipSearch,
leaderboard,
songList,
musicSearch,
hotSearch,
comment,
getLyric(songInfo) {
return lyric.getLyric(songInfo)
},
getPic(songInfo) {
return pic.getPic(songInfo)
},
getMusicDetailPageUrl(songInfo) {
return `https://www.kugou.com/song/#hash=${songInfo.hash}&album_id=${songInfo.albumId}`
}
}
export default kg

View File

@@ -0,0 +1,262 @@
import { httpFetch } from '../../request'
import { decodeName, formatPlayTime, sizeFormate } from '../index'
import { formatSingerName } from '../utils'
let boardList = [
{ id: 'kg__8888', name: 'TOP500', bangid: '8888' },
{ id: 'kg__6666', name: '飙升榜', bangid: '6666' },
{ id: 'kg__59703', name: '蜂鸟流行音乐榜', bangid: '59703' },
{ id: 'kg__52144', name: '抖音热歌榜', bangid: '52144' },
{ id: 'kg__52767', name: '快手热歌榜', bangid: '52767' },
{ id: 'kg__24971', name: 'DJ热歌榜', bangid: '24971' },
{ id: 'kg__23784', name: '网络红歌榜', bangid: '23784' },
{ id: 'kg__44412', name: '说唱先锋榜', bangid: '44412' },
{ id: 'kg__31308', name: '内地榜', bangid: '31308' },
{ id: 'kg__33160', name: '电音榜', bangid: '33160' },
{ id: 'kg__31313', name: '香港地区榜', bangid: '31313' },
{ id: 'kg__51341', name: '民谣榜', bangid: '51341' },
{ id: 'kg__54848', name: '台湾地区榜', bangid: '54848' },
{ id: 'kg__31310', name: '欧美榜', bangid: '31310' },
{ id: 'kg__33162', name: 'ACG新歌榜', bangid: '33162' },
{ id: 'kg__31311', name: '韩国榜', bangid: '31311' },
{ id: 'kg__31312', name: '日本榜', bangid: '31312' },
{ id: 'kg__49225', name: '80后热歌榜', bangid: '49225' },
{ id: 'kg__49223', name: '90后热歌榜', bangid: '49223' },
{ id: 'kg__49224', name: '00后热歌榜', bangid: '49224' },
{ id: 'kg__33165', name: '粤语金曲榜', bangid: '33165' },
{ id: 'kg__33166', name: '欧美金曲榜', bangid: '33166' },
{ id: 'kg__33163', name: '影视金曲榜', bangid: '33163' },
{ id: 'kg__51340', name: '伤感榜', bangid: '51340' },
{ id: 'kg__35811', name: '会员专享榜', bangid: '35811' },
{ id: 'kg__37361', name: '雷达榜', bangid: '37361' },
{ id: 'kg__21101', name: '分享榜', bangid: '21101' },
{ id: 'kg__46910', name: '综艺新歌榜', bangid: '46910' },
{ id: 'kg__30972', name: '酷狗音乐人原创榜', bangid: '30972' },
{ id: 'kg__60170', name: '闽南语榜', bangid: '60170' },
{ id: 'kg__65234', name: '儿歌榜', bangid: '65234' },
{ id: 'kg__4681', name: '美国BillBoard榜', bangid: '4681' },
{ id: 'kg__25028', name: 'Beatport电子舞曲榜', bangid: '25028' },
{ id: 'kg__4680', name: '英国单曲榜', bangid: '4680' },
{ id: 'kg__38623', name: '韩国Melon音乐榜', bangid: '38623' },
{ id: 'kg__42807', name: 'joox本地热歌榜', bangid: '42807' },
{ id: 'kg__36107', name: '小语种热歌榜', bangid: '36107' },
{ id: 'kg__4673', name: '日本公信榜', bangid: '4673' },
{ id: 'kg__46868', name: '日本SPACE SHOWER榜', bangid: '46868' },
{ id: 'kg__42808', name: 'KKBOX风云榜', bangid: '42808' },
{ id: 'kg__60171', name: '越南语榜', bangid: '60171' },
{ id: 'kg__60172', name: '泰语榜', bangid: '60172' },
{ id: 'kg__59895', name: 'R&B榜', bangid: '59895' },
{ id: 'kg__59896', name: '摇滚榜', bangid: '59896' },
{ id: 'kg__59897', name: '爵士榜', bangid: '59897' },
{ id: 'kg__59898', name: '乡村音乐榜', bangid: '59898' },
{ id: 'kg__59900', name: '纯音乐榜', bangid: '59900' },
{ id: 'kg__59899', name: '古典榜', bangid: '59899' },
{ id: 'kg__22603', name: '5sing音乐榜', bangid: '22603' },
{ id: 'kg__21335', name: '繁星音乐榜', bangid: '21335' },
{ id: 'kg__33161', name: '古风新歌榜', bangid: '33161' }
]
export default {
listDetailLimit: 100,
list: [
{
id: 'kgtop500',
name: 'TOP500',
bangid: '8888'
},
{
id: 'kgwlhgb',
name: '网络榜',
bangid: '23784'
},
{
id: 'kgbsb',
name: '飙升榜',
bangid: '6666'
},
{
id: 'kgfxb',
name: '分享榜',
bangid: '21101'
},
{
id: 'kgcyyb',
name: '纯音乐榜',
bangid: '33164'
},
{
id: 'kggfjqb',
name: '古风榜',
bangid: '33161'
},
{
id: 'kgyyjqb',
name: '粤语榜',
bangid: '33165'
},
{
id: 'kgomjqb',
name: '欧美榜',
bangid: '33166'
},
{
id: 'kgdyrgb',
name: '电音榜',
bangid: '33160'
},
{
id: 'kgjdrgb',
name: 'DJ热歌榜',
bangid: '24971'
},
{
id: 'kghyxgb',
name: '华语新歌榜',
bangid: '31308'
}
],
getUrl(p, id, limit) {
return `http://mobilecdnbj.kugou.com/api/v3/rank/song?version=9108&ranktype=1&plat=0&pagesize=${limit}&area_code=1&page=${p}&rankid=${id}&with_res_tag=0&show_portrait_mv=1`
},
regExps: {
total: /total: '(\d+)',/,
page: /page: '(\d+)',/,
limit: /pagesize: '(\d+)',/,
listData: /global\.features = (\[.+\]);/
},
_requestBoardsObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch(
'http://mobilecdnbj.kugou.com/api/v5/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=1'
)
return this._requestBoardsObj.promise
},
getData(url) {
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
getSinger(singers) {
let arr = []
singers.forEach((singer) => {
arr.push(singer.author_name)
})
return arr.join('、')
},
filterData(rawList) {
// console.log(rawList)
return rawList.map((item) => {
const types = []
const _types = {}
if (item.filesize !== 0) {
let size = sizeFormate(item.filesize)
types.push({ type: '128k', size, hash: item.hash })
_types['128k'] = {
size,
hash: item.hash
}
}
if (item['320filesize'] !== 0) {
let size = sizeFormate(item['320filesize'])
types.push({ type: '320k', size, hash: item['320hash'] })
_types['320k'] = {
size,
hash: item['320hash']
}
}
if (item.sqfilesize !== 0) {
let size = sizeFormate(item.sqfilesize)
types.push({ type: 'flac', size, hash: item.sqhash })
_types.flac = {
size,
hash: item.sqhash
}
}
if (item.filesize_high !== 0) {
let size = sizeFormate(item.filesize_high)
types.push({ type: 'flac24bit', size, hash: item.hash_high })
_types.flac24bit = {
size,
hash: item.hash_high
}
}
return {
singer: formatSingerName(item.authors, 'author_name'),
name: decodeName(item.songname),
albumName: decodeName(item.remark),
albumId: item.album_id,
songmid: item.audio_id,
source: 'kg',
interval: formatPlayTime(item.duration),
img: null,
lrc: null,
hash: item.hash,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.isvol != 1) continue
list.push({
id: 'kg__' + board.rankid,
name: board.rankname,
bangid: String(board.rankid)
})
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body)
// if (response.statusCode !== 200 || response.body.errcode !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.info)
// console.log(list)
// // console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'kg',
// }
this.list = boardList
return {
list: boardList,
source: 'kg'
}
},
async getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) throw new Error('try max num')
const { body } = await this.getData(this.getUrl(page, bangid, this.listDetailLimit))
if (body.errcode != 0) return this.getList(bangid, page, retryNum)
// console.log(body)
let total = body.data.total
let limit = 100
let listData = this.filterData(body.data.info)
// console.log(listData)
return {
total,
list: listData,
limit,
page,
source: 'kg'
}
},
getDetailPageUrl(id) {
if (typeof id == 'string') id = id.replace('kg__', '')
return `https://www.kugou.com/yy/rank/home/1-${id}.html`
}
}

View File

@@ -0,0 +1,122 @@
import { httpFetch } from '../../request'
import { decodeKrc } from '../../../../common/utils/lyricUtils/kg'
export default {
getIntv(interval) {
if (!interval) return 0
let intvArr = interval.split(':')
let intv = 0
let unit = 1
while (intvArr.length) {
intv += intvArr.pop() * unit
unit *= 60
}
return parseInt(intv)
},
// getLyric(songInfo, tryNum = 0) {
// let requestObj = httpFetch(`http://m.kugou.com/app/i/krc.php?cmd=100&keyword=${encodeURIComponent(songInfo.name)}&hash=${songInfo.hash}&timelength=${songInfo._interval || this.getIntv(songInfo.interval)}&d=0.38664927426725626`, {
// headers: {
// 'KG-RC': 1,
// 'KG-THash': 'expand_search_manager.cpp:852736169:451',
// 'User-Agent': 'KuGou2012-9020-ExpandSearchManager',
// },
// })
// requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
// if (statusCode !== 200) {
// if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
// let tryRequestObj = this.getLyric(songInfo, ++tryNum)
// requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
// return tryRequestObj.promise
// }
// return {
// lyric: body,
// tlyric: '',
// }
// })
// return requestObj
// },
searchLyric(name, hash, time, tryNum = 0) {
let requestObj = httpFetch(
`http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${encodeURIComponent(name)}&hash=${hash}&timelength=${time}&lrctxt=1`,
{
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
'User-Agent': 'KuGou2012-9020-ExpandSearchManager'
}
}
)
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
if (statusCode !== 200) {
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
let tryRequestObj = this.searchLyric(name, hash, time, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
if (body.candidates.length) {
let info = body.candidates[0]
return {
id: info.id,
accessKey: info.accesskey,
fmt: info.krctype == 1 && info.contenttype != 1 ? 'krc' : 'lrc'
}
}
return null
})
return requestObj
},
getLyricDownload(id, accessKey, fmt, tryNum = 0) {
let requestObj = httpFetch(
`http://lyrics.kugou.com/download?ver=1&client=pc&id=${id}&accesskey=${accessKey}&fmt=${fmt}&charset=utf8`,
{
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
'User-Agent': 'KuGou2012-9020-ExpandSearchManager'
}
}
)
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
if (statusCode !== 200) {
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
let tryRequestObj = this.getLyric(id, accessKey, fmt, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
switch (body.fmt) {
case 'krc':
return decodeKrc(body.content)
case 'lrc':
return {
lyric: Buffer.from(body.content, 'base64').toString('utf-8'),
tlyric: '',
rlyric: '',
lxlyric: ''
}
default:
return Promise.reject(new Error(`未知歌词格式: ${body.fmt}`))
}
})
return requestObj
},
getLyric(songInfo, tryNum = 0) {
let requestObj = this.searchLyric(
songInfo.name,
songInfo.hash,
songInfo._interval || this.getIntv(songInfo.interval)
)
requestObj.promise = requestObj.promise.then((result) => {
if (!result) return Promise.reject(new Error('Get lyric failed'))
let requestObj2 = this.getLyricDownload(result.id, result.accessKey, result.fmt)
requestObj.cancelHttp = requestObj2.cancelHttp.bind(requestObj2)
return requestObj2.promise
})
return requestObj
}
}

View File

@@ -0,0 +1,120 @@
import { decodeName, formatPlayTime, sizeFormate } from '../../index'
import { createHttpFetch } from './util'
const createGetMusicInfosTask = (hashs) => {
let data = {
area_code: '1',
show_privilege: 1,
show_album_info: '1',
is_publish: '',
appid: 1005,
clientver: 11451,
mid: '1',
dfid: '-',
clienttime: Date.now(),
key: 'OIlwieks28dk2k092lksi2UIkp',
fields: 'album_info,author_name,audio_info,ori_audio_name,base,songname,classification'
}
let list = hashs
let tasks = []
while (list.length) {
tasks.push(Object.assign({ data: list.slice(0, 100) }, data))
if (list.length < 100) break
list = list.slice(100)
}
let url = 'http://gateway.kugou.com/v3/album_audio/audio'
return tasks.map((task) =>
createHttpFetch(url, {
method: 'POST',
body: task,
headers: {
'KG-THash': '13a3164',
'KG-RC': '1',
'KG-Fake': '0',
'KG-RF': '00869891',
'User-Agent': 'Android712-AndroidPhone-11451-376-0-FeeCacheUpdate-wifi',
'x-router': 'kmr.service.kugou.com'
}
}).then((data) => data.map((s) => s[0]))
)
}
export const filterMusicInfoList = (rawList) => {
// console.log(rawList)
let ids = new Set()
let list = []
rawList.forEach((item) => {
if (!item) return
if (ids.has(item.audio_info.audio_id)) return
ids.add(item.audio_info.audio_id)
const types = []
const _types = {}
if (item.audio_info.filesize !== '0') {
let size = sizeFormate(parseInt(item.audio_info.filesize))
types.push({ type: '128k', size, hash: item.audio_info.hash })
_types['128k'] = {
size,
hash: item.audio_info.hash
}
}
if (item.audio_info.filesize_320 !== '0') {
let size = sizeFormate(parseInt(item.audio_info.filesize_320))
types.push({ type: '320k', size, hash: item.audio_info.hash_320 })
_types['320k'] = {
size,
hash: item.audio_info.hash_320
}
}
if (item.audio_info.filesize_flac !== '0') {
let size = sizeFormate(parseInt(item.audio_info.filesize_flac))
types.push({ type: 'flac', size, hash: item.audio_info.hash_flac })
_types.flac = {
size,
hash: item.audio_info.hash_flac
}
}
if (item.audio_info.filesize_high !== '0') {
let size = sizeFormate(parseInt(item.audio_info.filesize_high))
types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high })
_types.flac24bit = {
size,
hash: item.audio_info.hash_high
}
}
list.push({
singer: decodeName(item.author_name),
name: decodeName(item.songname),
albumName: decodeName(item.album_info.album_name),
albumId: item.album_info.album_id,
songmid: item.audio_info.audio_id,
source: 'kg',
interval: formatPlayTime(parseInt(item.audio_info.timelength) / 1000),
img: null,
lrc: null,
hash: item.audio_info.hash,
otherSource: null,
types,
_types,
typeUrl: {}
})
})
return list
}
export const getMusicInfos = async (hashs) => {
return filterMusicInfoList(
await Promise.all(createGetMusicInfosTask(hashs)).then((data) => data.flat())
)
}
export const getMusicInfoRaw = async (hash) => {
return Promise.all(createGetMusicInfosTask([{ hash }])).then((data) => data.flat()[0])
}
export const getMusicInfo = async (hash) => {
return getMusicInfos([{ hash }]).then((data) => data[0])
}
export const getMusicInfosByList = (list) => {
return getMusicInfos(list.map((item) => ({ hash: item.hash })))
}

View File

@@ -0,0 +1,109 @@
import { httpFetch } from '../../request'
import { decodeName, formatPlayTime, sizeFormate } from '../index'
import { formatSingerName } from '../utils'
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
const searchRequest = httpFetch(
`https://songsearch.kugou.com/song_search_v2?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&userid=0&clientver=&platform=WebFilter&filter=2&iscorrection=1&privilege_filter=0&area_code=1`
)
return searchRequest.promise.then(({ body }) => body)
},
filterData(rawData) {
const types = []
const _types = {}
if (rawData.FileSize !== 0) {
let size = sizeFormate(rawData.FileSize)
types.push({ type: '128k', size, hash: rawData.FileHash })
_types['128k'] = {
size,
hash: rawData.FileHash
}
}
if (rawData.HQFileSize !== 0) {
let size = sizeFormate(rawData.HQFileSize)
types.push({ type: '320k', size, hash: rawData.HQFileHash })
_types['320k'] = {
size,
hash: rawData.HQFileHash
}
}
if (rawData.SQFileSize !== 0) {
let size = sizeFormate(rawData.SQFileSize)
types.push({ type: 'flac', size, hash: rawData.SQFileHash })
_types.flac = {
size,
hash: rawData.SQFileHash
}
}
if (rawData.ResFileSize !== 0) {
let size = sizeFormate(rawData.ResFileSize)
types.push({ type: 'flac24bit', size, hash: rawData.ResFileHash })
_types.flac24bit = {
size,
hash: rawData.ResFileHash
}
}
return {
singer: decodeName(formatSingerName(rawData.Singers, 'name')),
name: decodeName(rawData.SongName),
albumName: decodeName(rawData.AlbumName),
albumId: rawData.AlbumID,
songmid: rawData.Audioid,
source: 'kg',
interval: formatPlayTime(rawData.Duration),
_interval: rawData.Duration,
img: null,
lrc: null,
otherSource: null,
hash: rawData.FileHash,
types,
_types,
typeUrl: {}
}
},
handleResult(rawData) {
let ids = new Set()
const list = []
rawData.forEach((item) => {
const key = item.Audioid + item.FileHash
if (ids.has(key)) return
ids.add(key)
list.push(this.filterData(item))
for (const childItem of item.Grp) {
const key = item.Audioid + item.FileHash
if (ids.has(key)) continue
ids.add(key)
list.push(this.filterData(childItem))
}
})
return list
},
search(str, page = 1, limit, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then((result) => {
if (!result || result.error_code !== 0) return this.search(str, page, limit, retryNum)
let list = this.handleResult(result.data.lists)
if (list == null) return this.search(str, page, limit, retryNum)
this.total = result.data.total
this.page = page
this.allPage = Math.ceil(this.total / limit)
return Promise.resolve({
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'kg'
})
})
}
}

View File

@@ -0,0 +1,45 @@
import { httpFetch } from '../../request'
export default {
getPic(songInfo) {
const requestObj = httpFetch('http://media.store.kugou.com/v1/get_res_privilege', {
method: 'POST',
headers: {
'KG-RC': 1,
'KG-THash': 'expand_search_manager.cpp:852736169:451',
'User-Agent': 'KuGou2012-9020-ExpandSearchManager'
},
body: {
appid: 1001,
area_code: '1',
behavior: 'play',
clientver: '9020',
need_hash_offset: 1,
relate: 1,
resource: [
{
album_audio_id:
songInfo.songmid.length == 32 // 修复歌曲ID存储变更导致图片获取失败的问题
? songInfo.audioId.split('_')[0]
: songInfo.songmid,
album_id: songInfo.albumId,
hash: songInfo.hash,
id: 0,
name: `${songInfo.singer} - ${songInfo.name}.mp3`,
type: 'audio'
}
],
token: '',
userid: 2626431536,
vip: 1
}
})
return requestObj.promise.then(({ body }) => {
if (body.error_code !== 0) return Promise.reject(new Error('图片获取失败'))
let info = body.data[0].info
const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image
if (!img) return Promise.reject(new Error('Pic get failed'))
return img
})
}
}

View File

@@ -0,0 +1,91 @@
import { getMusicInfosByList } from './musicInfo'
import { createHttpFetch } from './util'
export default {
/**
* 获取歌手信息
* @param {*} id
*/
getInfo(id) {
if (id == 0) throw new Error('歌手不存在') // kg源某些歌曲在歌手没被kg收录时返回的歌手id为0
return createHttpFetch(`http://mobiles.kugou.com/api/v5/singer/info?singerid=${id}`).then(
(body) => {
if (!body) throw new Error('get singer info faild.')
return {
source: 'kg',
id: body.singerid,
info: {
name: body.singername,
desc: body.intro,
avatar: body.imgurl.replace('{size}', 480),
gender: body.grade === 1 ? 'man' : 'woman'
},
count: {
music: body.songcount,
album: body.albumcount
}
}
}
)
},
/**
* 获取歌手专辑列表
* @param {*} id
* @param {*} page
* @param {*} limit
*/
getAlbumList(id, page = 1, limit = 10) {
if (id == 0) throw new Error('歌手不存在')
return createHttpFetch(
`http://mobiles.kugou.com/api/v5/singer/album?singerid=${id}&page=${page}&pagesize=${limit}`
).then((body) => {
if (!body.info) throw new Error('get singer album list faild.')
const list = this.filterAlbumList(body.info)
return {
source: 'kg',
list,
limit,
page,
total: body.total
}
})
},
/**
* 获取歌手歌曲列表
* @param {*} id
* @param {*} page
* @param {*} limit
*/
async getSongList(id, page = 1, limit = 100) {
if (id == 0) throw new Error('歌手不存在')
const body = await createHttpFetch(
`http://mobiles.kugou.com/api/v5/singer/song?singerid=${id}&page=${page}&pagesize=${limit}`
)
if (!body.info) throw new Error('get singer song list faild.')
const list = await getMusicInfosByList(body.info)
return {
source: 'kg',
list,
limit,
page,
total: body.total
}
},
filterAlbumList(raw) {
return raw.map((item) => {
return {
id: item.albumid,
count: item.songcount,
info: {
name: item.albumname,
author: item.singername,
img: item.replaceAll('{size}', '480'),
desc: item.intro
}
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
import { decodeName, formatPlayTime, sizeFormate } from '../../index'
import { signatureParams, createHttpFetch } from './util'
import { formatSingerName } from '../../utils'
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
const sign = signatureParams(
`userid=0&area_code=1&appid=1005&dopicfull=1&page=${page}&token=0&privilegefilter=0&requestid=0&pagesize=${limit}&user_labels=&clienttime=0&sec_aggre=1&iscorrection=1&uuid=0&mid=0&keyword=${str}&dfid=-&clientver=11409&platform=AndroidFilter&tag=`,
3
)
return createHttpFetch(
`https://gateway.kugou.com/complexsearch/v3/search/song?userid=0&area_code=1&appid=1005&dopicfull=1&page=${page}&token=0&privilegefilter=0&requestid=0&pagesize=${limit}&user_labels=&clienttime=0&sec_aggre=1&iscorrection=1&uuid=0&mid=0&dfid=-&clientver=11409&platform=AndroidFilter&tag=&keyword=${encodeURIComponent(str)}&signature=${sign}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
referer: 'https://kugou.com'
}
}
).then((body) => body)
},
filterList(raw) {
let ids = new Set()
const list = []
raw.forEach((item) => {
if (ids.has(item.Audioid)) return
ids.add(item.Audioid)
const types = []
const _types = {}
if (item.FileSize !== 0) {
let size = sizeFormate(item.FileSize)
types.push({ type: '128k', size, hash: item.FileHash })
_types['128k'] = {
size,
hash: item.FileHash
}
}
if (item.HQ != undefined) {
let size = sizeFormate(item.HQ.FileSize)
types.push({ type: '320k', size, hash: item.HQ.Hash })
_types['320k'] = {
size,
hash: item.HQ.Hash
}
}
if (item.SQ != undefined) {
let size = sizeFormate(item.SQ.FileSize)
types.push({ type: 'flac', size, hash: item.SQ.Hash })
_types.flac = {
size,
hash: item.SQ.Hash
}
}
if (item.Res != undefined) {
let size = sizeFormate(item.Res.FileSize)
types.push({ type: 'flac24bit', size, hash: item.Res.Hash })
_types.flac24bit = {
size,
hash: item.Res.Hash
}
}
list.push({
singer: decodeName(formatSingerName(item.Singers)),
name: decodeName(item.SongName),
albumName: decodeName(item.AlbumName),
albumId: item.AlbumID,
songmid: item.Audioid,
source: 'kg',
interval: formatPlayTime(item.Duration),
_interval: item.Duration,
img: null,
lrc: null,
otherSource: null,
hash: item.FileHash,
types,
_types,
typeUrl: {}
})
})
return list
},
handleResult(rawData) {
const rawList = []
rawData.forEach((item) => {
rawList.push(item)
item.Grp.forEach((e) => rawList.push(e))
})
return this.filterList(rawList)
},
search(str, page = 1, limit, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
return this.musicSearch(str, page, limit).then((data) => {
let list = this.handleResult(data.lists)
if (!list) return this.search(str, page, limit, retryNum)
this.total = data.total
this.page = page
this.allPage = Math.ceil(this.total / limit)
return Promise.resolve({
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'kg'
})
})
}
}

View File

@@ -0,0 +1,915 @@
import { httpFetch } from '../../../request'
import { formatSingerName } from '../../utils'
import {
decodeName,
formatPlayTime,
sizeFormate,
dateFormat,
formatPlayCount
} from '../../../index'
import { signatureParams, createHttpFetch } from './../util'
import { getMusicInfosByList } from '../musicInfo'
import album from '../album'
export default {
_requestObj_tags: null,
_requestObj_listInfo: null,
_requestObj_list: null,
_requestObj_listRecommend: null,
listDetailLimit: 10000,
currentTagInfo: {
id: undefined,
info: undefined
},
sortList: [
{
name: '推荐',
id: '5'
},
{
name: '最热',
id: '6'
},
{
name: '最新',
id: '7'
},
{
name: '热藏',
id: '3'
},
{
name: '飙升',
id: '8'
}
],
cache: new Map(),
collectionIdListInfoCache: new Map(),
regExps: {
listData: /global\.data = (\[.+\]);/,
listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/,
// https://www.kugou.com/yy/special/single/1067062.html
listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/
},
/**
* 获取歌曲列表内的音乐
* @param {*} id
* @param {*} page
*/
async getListDetail(id, page) {
id = id.toString()
if (id.includes('special/single/')) id = id.replace(this.regExps.listDetailLink, '$1')
// fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1
if (/https?:/.test(id)) {
if (id.includes('#')) id = id.replace(/#.*$/, '')
if (id.includes('global_collection_id'))
return this.getUserListDetailByCollectionId(
id.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'),
page
)
if (id.includes('chain='))
return this.getUserListDetail3(id.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (id.includes('.html')) {
if (id.includes('zlist.html')) {
id = id.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
if (id.includes('pagesize')) {
id = id
.replace('pagesize=30', 'pagesize=' + this.listDetailLimit)
.replace('page=1', 'page=' + page)
} else {
id += `&pagesize=${this.listDetailLimit}&page=${page}`
}
} else if (!id.includes('song.html'))
return this.getUserListDetail3(
id.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'),
page
)
}
return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page)
}
if (/^\d+$/.test(id)) return this.getUserListDetailByCode(id, page)
if (id.startsWith('gid_'))
return this.getUserListDetailByCollectionId(id.replace('gid_', ''), page)
if (id.startsWith('id_')) return this.getUserListDetailBySpecialId(id.replace('id_', ''), page)
return new Error('Failed.')
},
/**
* 获取SpecialId歌单
* @param {*} id
*/
async getUserListDetailBySpecialId(id, page, tryNum = 0) {
if (tryNum > 2) throw new Error('try max num')
const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise
let listData = body.match(this.regExps.listData)
let listInfo = body.match(this.regExps.listInfo)
if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum)
let list = await getMusicInfosByList(JSON.parse(listData[1]))
let name
let pic
if (listInfo) {
name = listInfo[1]
pic = listInfo[2]
}
let desc = this.parseHtmlDesc(body)
return {
list,
page: 1,
limit: 10000,
total: list.length,
source: 'kg',
info: {
name,
img: pic,
desc
// author: body.result.info.userinfo.username,
// play_count: formatPlayCount(body.result.listen_num),
}
}
},
parseHtmlDesc(html) {
const prefix = '<div class="pc_specail_text pc_singer_tab_content" id="specailIntroduceWrap">'
let index = html.indexOf(prefix)
if (index < 0) return null
const afterStr = html.substring(index + prefix.length)
index = afterStr.indexOf('</div>')
if (index < 0) return null
return decodeName(afterStr.substring(0, index))
},
/**
* 使用SpecialId获取CollectionId
* @param {*} specialId
*/
async getCollectionIdBySpecialId(specialId) {
return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, {
headers: {
'User-Agent':
'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70'
}
}).promise.then(({ body }) => {
// console.log('getCollectionIdBySpecialId', body)
if (!body.data.global_specialid)
return Promise.reject(new Error('Failed to get global collection id.'))
return body.data.global_specialid
})
},
/**
* 获取歌单URL
* @param {*} sortId
* @param {*} tagId
* @param {*} page
*/
getSongListUrl(sortId, tagId, page) {
if (tagId == null) tagId = ''
return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}`
},
getInfoUrl(tagId) {
return tagId
? `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&cdn=cdn&t=5&c=${tagId}`
: 'http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&'
},
getSongListDetailUrl(id) {
return `http://www2.kugou.kugou.com/yueku/v9/special/single/${id}-5-9999.html`
},
filterInfoHotTag(rawData) {
const result = []
if (rawData.status !== 1) return result
for (const key of Object.keys(rawData.data)) {
let tag = rawData.data[key]
result.push({
id: tag.special_id,
name: tag.special_name,
source: 'kg'
})
}
return result
},
filterTagInfo(rawData) {
const result = []
for (const name of Object.keys(rawData)) {
result.push({
name,
list: rawData[name].data.map((tag) => ({
parent_id: tag.parent_id,
parent_name: tag.pname,
id: tag.id,
name: tag.name,
source: 'kg'
}))
})
}
return result
},
filterSongList(rawData) {
return rawData.map((item) => ({
play_count: item.total_play_count || formatPlayCount(item.play_count),
id: 'id_' + item.specialid,
author: item.nickname,
name: item.specialname,
time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'),
img: item.img || item.imgurl,
total: item.songcount,
grade: item.grade,
desc: item.intro,
source: 'kg'
}))
},
getSongList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(this.getSongListUrl(sortId, tagId, page))
return this._requestObj_list.promise.then(({ body }) => {
if (!body || body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum)
return this.filterSongList(body.special_db)
})
},
getSongListRecommend(tryNum = 0) {
if (this._requestObj_listRecommend) this._requestObj_listRecommend.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listRecommend = httpFetch(
'http://everydayrec.service.kugou.com/guess_special_recommend',
{
method: 'post',
headers: {
'User-Agent': 'KuGou2012-8275-web_browser_event_handler'
},
body: {
appid: 1001,
clienttime: 1566798337219,
clientver: 8275,
key: 'f1f93580115bb106680d2375f8032d96',
mid: '21511157a05844bd085308bc76ef3343',
platform: 'pc',
userid: '262643156',
return_min: 6,
return_max: 15
}
}
)
return this._requestObj_listRecommend.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongListRecommend(++tryNum)
return this.filterSongList(body.data.special_list)
})
},
/**
* 通过CollectionId获取歌单详情
* @param {*} id
*/
async getUserListInfoByCollectionId(id) {
if (!id || id.length > 1000) return Promise.reject(new Error('get list error'))
if (this.collectionIdListInfoCache.has(id)) return this.collectionIdListInfoCache.get(id)
const params = `appid=1058&specialid=0&global_specialid=${id}&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-`
return createHttpFetch(
`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`,
{
headers: {
mid: '1586163242519',
Referer: 'https://m3ws.kugou.com/share/index.php',
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
dfid: '-',
clienttime: '1586163242519'
}
}
).then((body) => {
let info = {
type: body.type,
userName: body.nickname,
userAvatar: body.user_avatar,
imageUrl: body.imgurl,
desc: body.intro,
name: body.specialname,
globalSpecialid: body.global_specialid,
total: body.songcount,
playCount: body.playcount
}
this.collectionIdListInfoCache.set(id, info)
return info
})
},
/**
* 通过SpecialId获取歌单
* @param {*} id
*/
// async getUserListDetailBySpecialId(id, page = 1, limit = 300) {
// if (!id || id.length > 1000) return Promise.reject(new Error('get list error.'))
// const listInfo = await this.getListInfoBySpecialId(id)
// const params = `specialid=${id}&need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&userid=0&page=${page}&type=0&area_code=1&appid=1005`
// return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, {
// headers: {
// 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi',
// },
// }).then(body => {
// if (!body.info) return Promise.reject(new Error('Get list failed.'))
// const songList = this.filterListByCollectionId(body.info)
// return {
// list: songList || [],
// page,
// limit,
// total: body.count,
// source: 'kg',
// info: {
// name: listInfo.name,
// img: listInfo.image,
// desc: listInfo.desc,
// // author: listInfo.userName,
// // play_count: formatPlayCount(listInfo.playCount),
// },
// }
// })
// },
/**
* 通过CollectionId获取歌单
* @param {*} id
*/
async getUserListDetailByCollectionId(id, page = 1, limit = 300) {
if (!id || id.length > 1000) return Promise.reject(new Error('ID error.'))
const listInfo = await this.getUserListInfoByCollectionId(id)
const params = `need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=0&area_code=1&appid=1005`
return createHttpFetch(
`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 'android')}`,
{
headers: {
'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi'
}
}
).then((body) => {
if (!body.info) return Promise.reject(new Error('Get list failed.'))
const songList = this.filterListByCollectionId(body.info)
return {
list: songList || [],
page,
limit,
total: listInfo.total,
source: 'kg',
info: {
name: listInfo.name,
img: listInfo.imageUrl && listInfo.imageUrl.replace('{size}', 240),
desc: listInfo.desc,
author: listInfo.userName,
play_count: formatPlayCount(listInfo.playCount)
}
}
})
},
/**
* 过滤GlobalSpecialId歌单数据
* @param {*} rawData
*/
filterListByCollectionId(rawData) {
let ids = new Set()
let list = []
rawData.forEach((item) => {
if (!item) return
if (ids.has(item.hash)) return
ids.add(item.hash)
const types = []
const _types = {}
item.relate_goods.forEach((data) => {
let size = sizeFormate(data.size)
switch (data.level) {
case 2:
types.push({ type: '128k', size, hash: data.hash })
_types['128k'] = {
size,
hash: data.hash
}
break
case 4:
types.push({ type: '320k', size, hash: data.hash })
_types['320k'] = {
size,
hash: data.hash
}
break
case 5:
types.push({ type: 'flac', size, hash: data.hash })
_types.flac = {
size,
hash: data.hash
}
break
case 6:
types.push({ type: 'flac24bit', size, hash: data.hash })
_types.flac24bit = {
size,
hash: data.hash
}
break
}
})
list.push({
singer:
formatSingerName(item.singerinfo, 'name') ||
decodeName(item.name).split(' - ')[0].replace(/&/g, '、'),
name: decodeName(item.name).split(' - ')[1],
albumName: decodeName(item.albuminfo.name),
albumId: item.albuminfo.id,
songmid: item.audio_id,
source: 'kg',
interval: formatPlayTime(parseInt(item.timelen) / 1000),
img: null,
lrc: null,
hash: item.hash,
otherSource: null,
types,
_types,
typeUrl: {}
})
})
return list
},
/**
* 通过酷狗码获取歌单
* @param {*} id
* @param {*} page
*/
async getUserListDetailByCode(id, page = 1) {
// type 1单曲2歌单3电台4酷狗码5别人的播放队列
const codeData = await createHttpFetch('http://t.kugou.com/command/', {
method: 'POST',
headers: {
'KG-RC': 1,
'KG-THash': 'network_super_call.cpp:3676261689:379',
'User-Agent': ''
},
body: {
appid: 1001,
clientver: 9020,
mid: '21511157a05844bd085308bc76ef3343',
clienttime: 640612895,
key: '36164c4015e704673c588ee202b9ecb8',
data: id
}
})
if (!codeData) return Promise.reject(new Error('Get list failed.'))
const codeInfo = codeData.info
switch (codeInfo.type) {
case 2:
if (!codeInfo.global_collection_id)
return this.getUserListDetailBySpecialId(codeInfo.id, page)
break
case 3:
return album.getAlbumDetail(codeInfo.id, page)
}
if (codeInfo.global_collection_id)
return this.getUserListDetailByCollectionId(codeInfo.global_collection_id, page)
if (codeInfo.userid != null) {
const songList = await createHttpFetch(
'http://www2.kugou.kugou.com/apps/kucodeAndShare/app/',
{
method: 'POST',
headers: {
'KG-RC': 1,
'KG-THash': 'network_super_call.cpp:3676261689:379',
'User-Agent': ''
},
body: {
appid: 1001,
clientver: 9020,
mid: '21511157a05844bd085308bc76ef3343',
clienttime: 640612895,
key: '36164c4015e704673c588ee202b9ecb8',
data: {
id: codeInfo.id,
type: 3,
userid: codeInfo.userid,
collect_type: 0,
page: 1,
pagesize: codeInfo.count
}
}
}
)
// console.log(songList)
let list = await getMusicInfosByList(songList || codeInfo.list)
return {
list,
page: 1,
limit: codeInfo.count,
total: list.length,
source: 'kg',
info: {
name: codeInfo.name,
img: (codeInfo.img_size && codeInfo.img_size.replace('{size}', 240)) || codeInfo.img,
// desc: body.result.info.list_desc,
author: codeInfo.username
// play_count: formatPlayCount(info.count),
}
}
}
},
async getUserListDetail3(chain, page) {
const songInfo = await createHttpFetch(
`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
}
}
)
if (!songInfo.list) {
if (songInfo.global_collection_id)
return this.getUserListDetailByCollectionId(songInfo.global_collection_id, page)
else
return this.getUserListDetail4(songInfo, chain, page).catch(() =>
this.getUserListDetail5(chain)
)
}
let list = await getMusicInfosByList(songInfo.list)
// console.log(info, songInfo)
return {
list,
page: 1,
limit: this.listDetailLimit,
total: list.length,
source: 'kg',
info: {
name: songInfo.info.name,
img: songInfo.info.img,
// desc: body.result.info.list_desc,
author: songInfo.info.username
// play_count: formatPlayCount(info.count),
}
}
},
async getUserListDetailByLink({ info }, link) {
let listInfo = info['0']
let total = listInfo.count
let tasks = []
let page = 0
while (total) {
const limit = total > 90 ? 90 : total
total -= limit
page += 1
tasks.push(
createHttpFetch(
link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page),
{
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link
}
}
).then((data) => data.list.info)
)
}
let result = await Promise.all(tasks).then(([...datas]) => datas.flat())
result = await getMusicInfosByList(result)
// console.log(result)
return {
list: result,
page,
limit: this.listDetailLimit,
total: result.length,
source: 'kg',
info: {
name: listInfo.name,
img: listInfo.pic && listInfo.pic.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.list_create_username
// play_count: formatPlayCount(listInfo.count),
}
}
},
createGetListDetail2Task(id, total) {
let tasks = []
let page = 0
while (total) {
const limit = total > 300 ? 300 : total
total -= limit
page += 1
const params =
'appid=1058&global_specialid=' +
id +
'&specialid=0&plat=0&version=8000&page=' +
page +
'&pagesize=' +
limit +
'&srcappid=2919&clientver=20000&clienttime=1586163263991&mid=1586163263991&uuid=1586163263991&dfid=-'
tasks.push(
createHttpFetch(
`https://mobiles.kugou.com/api/v5/special/song_v2?${params}&signature=${signatureParams(params, 'web')}`,
{
headers: {
mid: '1586163263991',
Referer: 'https://m3ws.kugou.com/share/index.php',
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
dfid: '-',
clienttime: '1586163263991'
}
}
).then((data) => data.info)
)
}
return Promise.all(tasks).then(([...datas]) => datas.flat())
},
async getUserListDetail2(global_collection_id) {
let id = global_collection_id
if (id.length > 1000) throw new Error('get list error')
const params =
'appid=1058&specialid=0&global_specialid=' +
id +
'&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-'
let info = await createHttpFetch(
`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`,
{
headers: {
mid: '1586163242519',
Referer: 'https://m3ws.kugou.com/share/index.php',
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
dfid: '-',
clienttime: '1586163242519'
}
}
)
const songInfo = await this.createGetListDetail2Task(id, info.songcount)
let list = await getMusicInfosByList(songInfo)
// console.log(info, songInfo, list)
return {
list,
page: 1,
limit: this.listDetailLimit,
total: list.length,
source: 'kg',
info: {
name: info.specialname,
img: info.imgurl && info.imgurl.replace('{size}', 240),
desc: info.intro,
author: info.nickname,
play_count: formatPlayCount(info.playcount)
}
}
},
async getListInfoByChain(chain) {
if (this.cache.has(chain)) return this.cache.get(chain)
const { body } = await httpFetch(`https://m.kugou.com/share/?chain=${chain}&id=${chain}`, {
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
}
}).promise
// console.log(body)
let result = body.match(/var\sphpParam\s=\s({.+?});/)
if (result) result = JSON.parse(result[1])
this.cache.set(chain, result)
return result
},
async getUserListDetailByPcChain(chain) {
let key = `${chain}_pc_list`
if (this.cache.has(key)) return this.cache.get(key)
const { body } = await httpFetch(`http://www.kugou.com/share/${chain}.html`, {
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
}
}).promise
let result = body.match(/var\sdataFromSmarty\s=\s(\[.+?\])/)
if (result) result = JSON.parse(result[1])
this.cache.set(chain, result)
result = await getMusicInfosByList(result)
// console.log(info, songInfo)
return result
},
async getUserListDetail4(songInfo, chain, page) {
const limit = 100
const [listInfo, list] = await Promise.all([
this.getListInfoByChain(chain),
this.getUserListDetailBySpecialId(songInfo.id, page, limit)
])
return {
list: list || [],
page,
limit,
total: list.length ?? 0,
source: 'kg',
info: {
name: listInfo.specialname,
img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.nickname
// play_count: formatPlayCount(info.count),
}
}
},
async getUserListDetail5(chain) {
const [listInfo, list] = await Promise.all([
this.getListInfoByChain(chain),
this.getUserListDetailByPcChain(chain)
])
return {
list: list || [],
page: 1,
limit: this.listDetailLimit,
total: list.length ?? 0,
source: 'kg',
info: {
name: listInfo.specialname,
img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.nickname
// play_count: formatPlayCount(info.count),
}
}
},
async getUserListDetail(link, page, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('link try max num'))
const requestLink = httpFetch(link, {
headers: {
'User-Agent':
'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link
},
follow_max: 2
})
const {
headers: { location },
statusCode,
body
} = await requestLink.promise
// console.log(body, location, statusCode)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (typeof body == 'string') {
if (body.includes('"global_collection_id":'))
return this.getUserListDetailByCollectionId(
body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1'),
page
)
if (body.includes('"albumid":'))
return album.getAlbumDetail(body.replace(/^[\s\S]+?"albumid":(\w+)[\s\S]+?$/, '$1'), page)
if (body.includes('"album_id":') && link.includes('album/info'))
return album.getAlbumDetail(body.replace(/^[\s\S]+?"album_id":(\w+)[\s\S]+?$/, '$1'), page)
if (body.includes('list_id = "') && link.includes('album/info'))
return album.getAlbumDetail(body.replace(/^[\s\S]+?list_id = "(\w+)"[\s\S]+?$/, '$1'), page)
}
if (location) {
// 概念版分享链接 https://t1.kugou.com/xxx
if (location.includes('global_specialid'))
return this.getUserListDetailByCollectionId(
location.replace(/^.*?global_specialid=(\w+)(?:&.*$|#.*$|$)/, '$1'),
page
)
if (location.includes('global_collection_id'))
return this.getUserListDetailByCollectionId(
location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'),
page
)
if (location.includes('chain='))
return this.getUserListDetail3(
location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'),
page
)
if (location.includes('.html')) {
if (location.includes('zlist.html')) {
let link = location.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
if (link.includes('pagesize')) {
link = link
.replace('pagesize=30', 'pagesize=' + this.listDetailLimit)
.replace('page=1', 'page=' + page)
} else {
link += `&pagesize=${this.listDetailLimit}&page=${page}`
}
return this.getUserListDetail(link, page, ++retryNum)
} else
return this.getUserListDetail3(
location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'),
page
)
}
return this.getUserListDetail(location, page, ++retryNum)
}
if (body.errcode !== 0) return this.getUserListDetail(link, page, ++retryNum)
return this.getUserListDetailByLink(body, link)
},
// 获取列表信息
getListInfo(tagId, tryNum = 0) {
if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listInfo = httpFetch(this.getInfoUrl(tagId))
return this._requestObj_listInfo.promise.then(({ body }) => {
if (body.status !== 1) return this.getListInfo(tagId, ++tryNum)
return {
limit: body.data.params.pagesize,
page: body.data.params.p,
total: body.data.params.total,
source: 'kg'
}
})
},
// 获取列表数据
getList(sortId, tagId, page) {
let tasks = [this.getSongList(sortId, tagId, page)]
tasks.push(
this.currentTagInfo.id === tagId
? Promise.resolve(this.currentTagInfo.info)
: this.getListInfo(tagId).then((info) => {
this.currentTagInfo.id = tagId
this.currentTagInfo.info = Object.assign({}, info)
return info
})
)
if (!tagId && page === 1 && sortId === this.sortList[0].id)
tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
return Promise.all(tasks).then(([list, info, recommendList]) => {
if (recommendList) list.unshift(...recommendList)
return {
list,
...info
}
})
},
// 获取标签
getTags(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.getInfoUrl())
return this._requestObj_tags.promise.then(({ body }) => {
if (body.status !== 1) return this.getTags(++tryNum)
return {
hotTag: this.filterInfoHotTag(body.data.hotTag),
tags: this.filterTagInfo(body.data.tagids),
source: 'kg'
}
})
},
getDetailPageUrl(id) {
if (typeof id == 'string') {
if (/^https?:\/\//.test(id)) return id
id = id.replace('id_', '')
}
return `https://www.kugou.com/yy/special/single/${id}.html`
},
search(text, page, limit = 20) {
const params = `userid=1384394652&req_custom=1&appid=1005&req_multi=1&version=11589&page=${page}&filter=0&pagesize=${limit}&order=0&clienttime=1681779443&iscorrection=1&searchsong=0&keyword=${text}&mid=288799920684148686226285199951543865551&dfid=3eSBsO1u97EY1zeIZd40hH4p&clientver=11589&platform=AndroidFilter`
const url = encodeURI(
`http://complexsearchretry.kugou.com/v1/search/special?${params}&signature=${signatureParams(params, 'android')}`
)
return createHttpFetch(url).then((body) => {
// console.log(body)
return {
list: body.lists.map((item) => {
return {
play_count: formatPlayCount(item.total_play_count),
id: item.gid ? `gid_${item.gid}` : `id_${item.specialid}`,
author: item.nickname,
name: item.specialname,
time: dateFormat(item.publish_time, 'Y-M-D'),
img: item.img,
grade: item.grade,
desc: item.intro,
total: item.song_count,
source: 'kg'
}
}),
limit,
total: body.total,
source: 'kg'
}
})
// http://msearchretry.kugou.com/api/v3/search/special?version=9209&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6&pagesize=20&filter=0&page=1&sver=2&with_res_tag=0
// http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5
// http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2
}
}
// getList
// getTags
// getListDetail

View File

@@ -0,0 +1,28 @@
import { createHttpFetch } from './util'
export default {
requestObj: null,
cancelTipSearch() {
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
},
tipSearchBySong(str) {
this.cancelTipSearch()
this.requestObj = createHttpFetch(
`https://searchtip.kugou.com/getSearchTip?MusicTipCount=10&keyword=${encodeURIComponent(str)}`,
{
headers: {
referer: 'https://www.kugou.com/'
}
}
)
return this.requestObj.then((body) => {
return body[0].RecordDatas
})
},
handleResult(rawData) {
return rawData.map((info) => info.HintInfo)
},
async search(str) {
return this.tipSearchBySong(str).then((result) => this.handleResult(result))
}
}

View File

@@ -0,0 +1,46 @@
import { toMD5 } from '../utils'
import { httpFetch } from '../../request'
// s.content[0].lyricContent.forEach(([str]) => {
// console.log(str)
// })
/**
* 签名
* @param {*} params
* @param {*} apiver
*/
export const signatureParams = (params, platform = 'android', body = '') => {
let keyparam = 'OIlwieks28dk2k092lksi2UIkp'
if (platform === 'web') keyparam = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
let param_list = params.split('&')
param_list.sort()
let sign_params = `${keyparam}${param_list.join('')}${body}${keyparam}`
return toMD5(sign_params)
}
/**
* 创建一个适用于KG的Http请求
* @param {*} url
* @param {*} options
* @param {*} retryNum
*/
export const createHttpFetch = async (url, options, retryNum = 0) => {
if (retryNum > 2) throw new Error('try max num')
let result
try {
result = await httpFetch(url, options).promise
} catch (err) {
console.log(err)
return createHttpFetch(url, options, ++retryNum)
}
// console.log(result.statusCode, result.body)
if (
result.statusCode !== 200 ||
(result.body.error_code ?? result.body.errcode ?? result.body.err_code) != 0
)
return createHttpFetch(url, options, ++retryNum)
if (result.body.data) return result.body.data
if (Array.isArray(result.body.info)) return result.body
return result.body.info
}

View File

@@ -0,0 +1,907 @@
!(function (t, n) {
'object' == typeof exports && 'undefined' != typeof module
? (module.exports = n())
: 'function' == typeof define && define.amd
? define(n)
: ((t = t || self), (t.infSign = n()))
})(this, function () {
'use strict'
function t(t, n, r) {
return (
n in t
? Object.defineProperty(t, n, { value: r, enumerable: !0, configurable: !0, writable: !0 })
: (t[n] = r),
t
)
}
function n(t, n) {
var r = Object.keys(t)
if (Object.getOwnPropertySymbols) {
var e = Object.getOwnPropertySymbols(t)
;(n &&
(e = e.filter(function (n) {
return Object.getOwnPropertyDescriptor(t, n).enumerable
})),
r.push.apply(r, e))
}
return r
}
function r(r) {
for (var e = 1; e < arguments.length; e++) {
var o = null != arguments[e] ? arguments[e] : {}
e % 2
? n(o, !0).forEach(function (n) {
t(r, n, o[n])
})
: Object.getOwnPropertyDescriptors
? Object.defineProperties(r, Object.getOwnPropertyDescriptors(o))
: n(o).forEach(function (t) {
Object.defineProperty(r, t, Object.getOwnPropertyDescriptor(o, t))
})
}
return r
}
function e(t, n) {
return ((n = { exports: {} }), t(n, n.exports), n.exports)
}
function o(t) {
return (
!!t.constructor && 'function' == typeof t.constructor.isBuffer && t.constructor.isBuffer(t)
)
}
function i(t) {
return 'function' == typeof t.readFloatLE && 'function' == typeof t.slice && o(t.slice(0, 0))
}
function c() {
var t,
n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : '',
o = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {},
i = !1,
c = !1,
a = 'json',
l = r({}, n),
u = s.isInClient()
;('function' == typeof o
? (t = o)
: ((t = o.callback), (i = o.useH5 || !1), (a = o.postType || 'json'), (c = o.isCDN || !1)),
e &&
('[object Object]' != Object.prototype.toString.call(e)
? (u = !1)
: 'urlencoded' == a && (u = !1)))
var f = function () {
var n = new Date().getTime(),
i = [],
s = [],
u = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt',
f = { srcappid: '2919', clientver: '20000', clienttime: n, mid: n, uuid: n, dfid: '-' }
;(c && (delete f.clienttime, delete f.mid, delete f.uuid, delete f.dfid),
(l = r({}, f, {}, l)))
for (var g in l) i.push(g)
if (
(i.sort(),
i.forEach(function (t) {
s.push(t + '=' + l[t])
}),
e)
)
if ('[object Object]' == Object.prototype.toString.call(e))
if ('json' == a) s.push(JSON.stringify(e))
else {
var b = []
for (var g in e) b.push(g + '=' + e[g])
s.push(b.join('&'))
}
else s.push(e)
;(s.unshift(u),
s.push(u),
(l.signature = d(s.join(''))),
o.log && (console.log('H5签名前参数', s), console.log('H5签名后返回', l)),
e
? t &&
t(
l,
'[object Object]' == Object.prototype.toString.call(e) && 'json' == a
? JSON.stringify(e)
: e
)
: t && t(l))
}
if (u && !i) {
var g = !1
s.mobileCall(764, { get: l, post: e }, function (n) {
return (
!g &&
((g = !0),
n && n.status
? (delete n.status,
o.log &&
(console.log('客户端签名前参数', { get: l, post: e }),
console.log('客户端签名后返回', r({}, l, {}, n))),
(l = r({}, l, {}, n)),
e
? t &&
t(
l,
'[object Object]' == Object.prototype.toString.call(e) && 'json' == a
? JSON.stringify(e)
: e
)
: t && t(l),
!1)
: ((u = !1), void f()))
)
})
} else ((u = !1), f())
}
'undefined' != typeof globalThis
? globalThis
: 'undefined' != typeof window
? window
: 'undefined' != typeof global
? global
: 'undefined' != typeof self && self
var s = e(function (t, n) {
!(function (n, r) {
t.exports = (function () {
var t = {
str2Json: function (t) {
var n = {}
if ('[object String]' === Object.prototype.toString.call(t))
try {
n = JSON.parse(t)
} catch (t) {
n = {}
}
return n
},
json2Str: function (t) {
var n = t
if ('string' != typeof t)
try {
n = JSON.stringify(t)
} catch (t) {
n = ''
}
return n
},
_extend: function (t, n) {
if (n) for (var r in t) n.hasOwnProperty(r) || (n[r] = t[r])
return n
},
formatURL: { browser: '', url: '' },
formatSong: {
filename: '',
filesize: '',
hash: '',
bitrate: '',
extname: '',
duration: '',
mvhash: '',
m4afilesize: '',
'320hash': '',
'320filesize': '',
sqhash: '',
sqfilesize: 0,
feetype: 0,
isfirst: 0
},
formatMV: { filename: '', singername: '', hash: '', imgurl: '' },
formatShare: {
shareName: '',
topicName: '',
hash: '',
listID: '',
type: '',
suid: '',
slid: '',
imgUrl: '',
filename: '',
duration: '',
shareData: { linkUrl: '', picUrl: '', content: '', title: '' }
},
cbNum: 0,
isIOS: !!navigator.userAgent.match(/KGBrowser/gi),
isKugouAndroid: !!navigator.userAgent.match(/kugouandroid/gi),
isAndroid: 'undefined' != typeof external && void 0 !== external.superCall,
loadUrl: function (t) {
var n = document.createElement('iframe')
;(n.setAttribute('src', t),
n.setAttribute('style', 'display:none;'),
n.setAttribute('height', '0px'),
n.setAttribute('width', '0px'),
n.setAttribute('frameborder', '0'),
document.body.appendChild(n),
n.parentNode.removeChild(n),
(n = null))
},
callCmd: function (n) {
var r = t
if (r.isKugouAndroid) {
var e = {},
o = ''
if (
(n.cmd && (e.cmd = n.cmd),
n.jsonStr && (e.jsonStr = n.jsonStr),
n.callback &&
((o =
'kgandroidmobilecall' + ++r.cbNum + Math.random().toString().substr(2, 9)),
(e.callback = o),
(window[o] = function (t, e) {
void 0 !== t &&
('[object String]' === Object.prototype.toString.call(t)
? ((t =
'#' === e
? decodeURIComponent(t)
: decodeURIComponent(decodeURIComponent(t))),
n.callback(r.str2Json(t)))
: n.callback(t))
})),
n.AndroidCallback)
) {
var i = r.str2Json(n.jsonStr)
;((i.AndroidCallback = o),
(n.jsonStr = r.json2Str(i)),
n.jsonStr && (e.jsonStr = n.jsonStr))
}
var c = encodeURIComponent(JSON.stringify(e)),
s = 'kugoujsbridge://start.kugou_jsbridge/?'.concat(c)
r.loadUrl(s)
} else if (r.isAndroid) {
var a = '',
l = ''
if (n.jsonStr) {
if (n.callback && '' !== n.callback && !0 === n.AndroidCallback) {
;((l = 'kgmobilecall' + ++r.cbNum + Math.random().toString().substr(2, 9)),
(window[l] = function (t, e) {
void 0 !== t &&
('[object String]' === Object.prototype.toString.call(t)
? ((t =
'#' === e
? decodeURIComponent(t)
: decodeURIComponent(decodeURIComponent(t))),
n.callback(r.str2Json(t)))
: n.callback(t))
}))
var u = r.str2Json(n.jsonStr)
;((u.AndroidCallback = l), (n.jsonStr = r.json2Str(u)))
}
try {
a = external.superCall(n.cmd, n.jsonStr)
} catch (t) {}
} else
try {
a = external.superCall(n.cmd)
} catch (t) {}
n.callback &&
'' !== n.callback &&
'AndroidCallback' != a &&
((a = r.str2Json(a)), n.callback(a))
} else {
var f = '',
d = ''
;(n.callback &&
((d = 'kgmobilecall' + ++r.cbNum + Math.random().toString().substr(2, 9)),
(window[d] = function (t) {
void 0 !== t &&
n.callback &&
('[object String]' === Object.prototype.toString.call(t)
? n.callback(r.str2Json(t))
: n.callback(t))
})),
d &&
'' != d &&
n.jsonStr &&
(f =
'kugouurl://start.music/?{"cmd":' +
n.cmd +
', "jsonStr":' +
n.jsonStr +
', "callback":"' +
d +
'"}'),
d &&
'' != d &&
!n.jsonStr &&
(f = 'kugouurl://start.music/?{"cmd":' + n.cmd + ', "callback":"' + d + '"}'),
'' == d &&
n.jsonStr &&
(f =
'kugouurl://start.music/?{"cmd":' + n.cmd + ', "jsonStr":' + n.jsonStr + '}'),
'' != d || n.jsonStr || (f = 'kugouurl://start.music/?{"cmd":' + n.cmd + '}'),
r.loadUrl(f))
}
},
formartData: function (n, r) {
;(n && 123 == n && r && (r = t._extend(t.formatURL, r)),
n && 123 == n && r && (r = t._extend(t.formatURL, r)))
}
}
return {
isIOS: t.isIOS,
isKugouAndroid: t.isKugouAndroid,
isAndroid: t.isAndroid,
isInClient: function () {
return !(!t.isAndroid && !t.isKugouAndroid && !t.isIOS)
},
mobileCall: function (n, r, e) {
var o = ''
if ((r && (o = t.json2Str(r)), !n)) return (console.error('请输入命令号!'), !1)
var i = {}
;(n && (i.cmd = n),
'' != o && (i.jsonStr = o),
e && (i.callback = e),
n && 186 == n && e && (i.AndroidCallback = !0),
t.callCmd(i))
},
KgWebMobileCall: function (t, n) {
if (t)
try {
var r = t.split('.')
r.reduce(function (e, o) {
if (e[o]) {
if (o === r[r.length - 1]) {
var i = e[o]
return 'function' == typeof i
? ((e[o] = function (t) {
;(i && i(t), n && n(t))
}),
e[o])
: (console.error(
'请检查,当前环境变量已注册了对象:' + t + ',且该对象不是方法'
),
null)
}
return e[o]
}
return (
o === r[r.length - 1]
? (e[o] = function (t) {
n && n(t)
})
: (e[o] = new Object()),
e[o]
)
}, window)
} catch (t) {}
}
}
})()
})()
}),
a = e(function (t) {
!(function () {
var n = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
r = {
rotl: function (t, n) {
return (t << n) | (t >>> (32 - n))
},
rotr: function (t, n) {
return (t << (32 - n)) | (t >>> n)
},
endian: function (t) {
if (t.constructor == Number)
return (16711935 & r.rotl(t, 8)) | (4278255360 & r.rotl(t, 24))
for (var n = 0; n < t.length; n++) t[n] = r.endian(t[n])
return t
},
randomBytes: function (t) {
for (var n = []; t > 0; t--) n.push(Math.floor(256 * Math.random()))
return n
},
bytesToWords: function (t) {
for (var n = [], r = 0, e = 0; r < t.length; r++, e += 8)
n[e >>> 5] |= t[r] << (24 - (e % 32))
return n
},
wordsToBytes: function (t) {
for (var n = [], r = 0; r < 32 * t.length; r += 8)
n.push((t[r >>> 5] >>> (24 - (r % 32))) & 255)
return n
},
bytesToHex: function (t) {
for (var n = [], r = 0; r < t.length; r++)
(n.push((t[r] >>> 4).toString(16)), n.push((15 & t[r]).toString(16)))
return n.join('')
},
hexToBytes: function (t) {
for (var n = [], r = 0; r < t.length; r += 2) n.push(parseInt(t.substr(r, 2), 16))
return n
},
bytesToBase64: function (t) {
for (var r = [], e = 0; e < t.length; e += 3)
for (var o = (t[e] << 16) | (t[e + 1] << 8) | t[e + 2], i = 0; i < 4; i++)
8 * e + 6 * i <= 8 * t.length
? r.push(n.charAt((o >>> (6 * (3 - i))) & 63))
: r.push('=')
return r.join('')
},
base64ToBytes: function (t) {
t = t.replace(/[^A-Z0-9+\/]/gi, '')
for (var r = [], e = 0, o = 0; e < t.length; o = ++e % 4)
0 != o &&
r.push(
((n.indexOf(t.charAt(e - 1)) & (Math.pow(2, -2 * o + 8) - 1)) << (2 * o)) |
(n.indexOf(t.charAt(e)) >>> (6 - 2 * o))
)
return r
}
}
t.exports = r
})()
}),
l = {
utf8: {
stringToBytes: function (t) {
return l.bin.stringToBytes(unescape(encodeURIComponent(t)))
},
bytesToString: function (t) {
return decodeURIComponent(escape(l.bin.bytesToString(t)))
}
},
bin: {
stringToBytes: function (t) {
for (var n = [], r = 0; r < t.length; r++) n.push(255 & t.charCodeAt(r))
return n
},
bytesToString: function (t) {
for (var n = [], r = 0; r < t.length; r++) n.push(String.fromCharCode(t[r]))
return n.join('')
}
}
},
u = l,
f = function (t) {
return null != t && (o(t) || i(t) || !!t._isBuffer)
},
d = e(function (t) {
!(function () {
var n = a,
r = u.utf8,
e = f,
o = u.bin,
i = function (t, c) {
t.constructor == String
? (t = c && 'binary' === c.encoding ? o.stringToBytes(t) : r.stringToBytes(t))
: e(t)
? (t = Array.prototype.slice.call(t, 0))
: Array.isArray(t) || (t = t.toString())
for (
var s = n.bytesToWords(t),
a = 8 * t.length,
l = 1732584193,
u = -271733879,
f = -1732584194,
d = 271733878,
g = 0;
g < s.length;
g++
)
s[g] =
(16711935 & ((s[g] << 8) | (s[g] >>> 24))) |
(4278255360 & ((s[g] << 24) | (s[g] >>> 8)))
;((s[a >>> 5] |= 128 << a % 32), (s[14 + (((a + 64) >>> 9) << 4)] = a))
for (var b = i._ff, p = i._gg, h = i._hh, m = i._ii, g = 0; g < s.length; g += 16) {
var y = l,
j = u,
S = f,
v = d
;((u = m(
(u = m(
(u = m(
(u = m(
(u = h(
(u = h(
(u = h(
(u = h(
(u = p(
(u = p(
(u = p(
(u = p(
(u = b(
(u = b(
(u = b(
(u = b(
u,
(f = b(
f,
(d = b(
d,
(l = b(l, u, f, d, s[g + 0], 7, -680876936)),
u,
f,
s[g + 1],
12,
-389564586
)),
l,
u,
s[g + 2],
17,
606105819
)),
d,
l,
s[g + 3],
22,
-1044525330
)),
(f = b(
f,
(d = b(
d,
(l = b(l, u, f, d, s[g + 4], 7, -176418897)),
u,
f,
s[g + 5],
12,
1200080426
)),
l,
u,
s[g + 6],
17,
-1473231341
)),
d,
l,
s[g + 7],
22,
-45705983
)),
(f = b(
f,
(d = b(
d,
(l = b(l, u, f, d, s[g + 8], 7, 1770035416)),
u,
f,
s[g + 9],
12,
-1958414417
)),
l,
u,
s[g + 10],
17,
-42063
)),
d,
l,
s[g + 11],
22,
-1990404162
)),
(f = b(
f,
(d = b(
d,
(l = b(l, u, f, d, s[g + 12], 7, 1804603682)),
u,
f,
s[g + 13],
12,
-40341101
)),
l,
u,
s[g + 14],
17,
-1502002290
)),
d,
l,
s[g + 15],
22,
1236535329
)),
(f = p(
f,
(d = p(
d,
(l = p(l, u, f, d, s[g + 1], 5, -165796510)),
u,
f,
s[g + 6],
9,
-1069501632
)),
l,
u,
s[g + 11],
14,
643717713
)),
d,
l,
s[g + 0],
20,
-373897302
)),
(f = p(
f,
(d = p(
d,
(l = p(l, u, f, d, s[g + 5], 5, -701558691)),
u,
f,
s[g + 10],
9,
38016083
)),
l,
u,
s[g + 15],
14,
-660478335
)),
d,
l,
s[g + 4],
20,
-405537848
)),
(f = p(
f,
(d = p(
d,
(l = p(l, u, f, d, s[g + 9], 5, 568446438)),
u,
f,
s[g + 14],
9,
-1019803690
)),
l,
u,
s[g + 3],
14,
-187363961
)),
d,
l,
s[g + 8],
20,
1163531501
)),
(f = p(
f,
(d = p(
d,
(l = p(l, u, f, d, s[g + 13], 5, -1444681467)),
u,
f,
s[g + 2],
9,
-51403784
)),
l,
u,
s[g + 7],
14,
1735328473
)),
d,
l,
s[g + 12],
20,
-1926607734
)),
(f = h(
f,
(d = h(
d,
(l = h(l, u, f, d, s[g + 5], 4, -378558)),
u,
f,
s[g + 8],
11,
-2022574463
)),
l,
u,
s[g + 11],
16,
1839030562
)),
d,
l,
s[g + 14],
23,
-35309556
)),
(f = h(
f,
(d = h(
d,
(l = h(l, u, f, d, s[g + 1], 4, -1530992060)),
u,
f,
s[g + 4],
11,
1272893353
)),
l,
u,
s[g + 7],
16,
-155497632
)),
d,
l,
s[g + 10],
23,
-1094730640
)),
(f = h(
f,
(d = h(
d,
(l = h(l, u, f, d, s[g + 13], 4, 681279174)),
u,
f,
s[g + 0],
11,
-358537222
)),
l,
u,
s[g + 3],
16,
-722521979
)),
d,
l,
s[g + 6],
23,
76029189
)),
(f = h(
f,
(d = h(
d,
(l = h(l, u, f, d, s[g + 9], 4, -640364487)),
u,
f,
s[g + 12],
11,
-421815835
)),
l,
u,
s[g + 15],
16,
530742520
)),
d,
l,
s[g + 2],
23,
-995338651
)),
(f = m(
f,
(d = m(
d,
(l = m(l, u, f, d, s[g + 0], 6, -198630844)),
u,
f,
s[g + 7],
10,
1126891415
)),
l,
u,
s[g + 14],
15,
-1416354905
)),
d,
l,
s[g + 5],
21,
-57434055
)),
(f = m(
f,
(d = m(
d,
(l = m(l, u, f, d, s[g + 12], 6, 1700485571)),
u,
f,
s[g + 3],
10,
-1894986606
)),
l,
u,
s[g + 10],
15,
-1051523
)),
d,
l,
s[g + 1],
21,
-2054922799
)),
(f = m(
f,
(d = m(
d,
(l = m(l, u, f, d, s[g + 8], 6, 1873313359)),
u,
f,
s[g + 15],
10,
-30611744
)),
l,
u,
s[g + 6],
15,
-1560198380
)),
d,
l,
s[g + 13],
21,
1309151649
)),
(f = m(
f,
(d = m(
d,
(l = m(l, u, f, d, s[g + 4], 6, -145523070)),
u,
f,
s[g + 11],
10,
-1120210379
)),
l,
u,
s[g + 2],
15,
718787259
)),
d,
l,
s[g + 9],
21,
-343485551
)),
(l = (l + y) >>> 0),
(u = (u + j) >>> 0),
(f = (f + S) >>> 0),
(d = (d + v) >>> 0))
}
return n.endian([l, u, f, d])
}
;((i._ff = function (t, n, r, e, o, i, c) {
var s = t + ((n & r) | (~n & e)) + (o >>> 0) + c
return ((s << i) | (s >>> (32 - i))) + n
}),
(i._gg = function (t, n, r, e, o, i, c) {
var s = t + ((n & e) | (r & ~e)) + (o >>> 0) + c
return ((s << i) | (s >>> (32 - i))) + n
}),
(i._hh = function (t, n, r, e, o, i, c) {
var s = t + (n ^ r ^ e) + (o >>> 0) + c
return ((s << i) | (s >>> (32 - i))) + n
}),
(i._ii = function (t, n, r, e, o, i, c) {
var s = t + (r ^ (n | ~e)) + (o >>> 0) + c
return ((s << i) | (s >>> (32 - i))) + n
}),
(i._blocksize = 16),
(i._digestsize = 16),
(t.exports = function (t, r) {
if (void 0 === t || null === t) throw new Error('Illegal argument ' + t)
var e = n.wordsToBytes(i(t, r))
return r && r.asBytes ? e : r && r.asString ? o.bytesToString(e) : n.bytesToHex(e)
}))
})()
})
return c
})

View File

@@ -0,0 +1,133 @@
import { httpFetch } from '../../request'
import { decodeName } from '../index'
import { formatSinger, objStr2JSON } from './util'
// let requestObj_list
export default {
limit_list: 36,
limit_song: 1000,
filterListDetail(rawList, albumName, albumId) {
// console.log(rawList)
// console.log(rawList.length, rawList2.length)
return rawList.map((item, inedx) => {
let formats = item.formats.split('|')
let types = []
let _types = {}
if (formats.includes('MP3128')) {
types.push({ type: '128k', size: null })
_types['128k'] = {
size: null
}
}
// if (formats.includes('MP3192')) {
// types.push({ type: '192k', size: null })
// _types['192k'] = {
// size: null,
// }
// }
if (formats.includes('MP3H')) {
types.push({ type: '320k', size: null })
_types['320k'] = {
size: null
}
}
// if (formats.includes('AL')) {
// types.push({ type: 'ape', size: null })
// _types.ape = {
// size: null,
// }
// }
if (formats.includes('ALFLAC')) {
types.push({ type: 'flac', size: null })
_types.flac = {
size: null
}
}
if (formats.includes('HIRFLAC')) {
types.push({ type: 'flac24bit', size: null })
_types.flac24bit = {
size: null
}
}
// types.reverse()
return {
singer: formatSinger(decodeName(item.artist)),
name: decodeName(item.name),
albumName,
albumId,
songmid: item.id,
source: 'kw',
interval: null,
img: item.pic,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
/**
* 格式化播放数量
* @param {*} num
*/
formatPlayCount(num) {
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
return num
},
getAlbumListDetail(id, page, retryNum = 0) {
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const requestObj_listDetail = httpFetch(
`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`
)
return requestObj_listDetail.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
body = objStr2JSON(body)
// console.log(body)
if (!body.musiclist) return this.getAlbumListDetail(id, page, ++retryNum)
body.name = decodeName(body.name)
return {
list: this.filterListDetail(body.musiclist, body.name, body.albumid),
page,
limit: this.limit_song,
total: parseInt(body.songnum),
source: 'kw',
info: {
name: body.name,
img: body.img || body.hts_img,
desc: decodeName(body.info),
author: decodeName(body.artist)
// play_count: this.formatPlayCount(body.playnum),
}
}
})
}
// getAlbumListDetail(id, page, retryNum = 0) {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// return tokenRequest(`http://www.kuwo.cn/api/www/album/albumInfo?albumId=${id}&pn=${page}&rn=${this.limit_song}&httpsStatus=1`).then((resp) => {
// return resp.promise.then(({ statusCode, body }) => {
// console.log(body)
// return Promise.reject(new Error('failed'))
// // if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
// // const data = body.data
// // console.log(data)
// // if (!data.musicList) return this.getAlbumListDetail(id, page, ++retryNum)
// // return {
// // list: this.filterListDetail(data.musiclist),
// // page,
// // limit: this.limit_song,
// // total: data.total,
// // source: 'kw',
// // info: {
// // name: data.album,
// // img: data.pic,
// // desc: data.albuminfo,
// // author: data.artist,
// // play_count: this.formatPlayCount(data.playCnt),
// // },
// // }
// })
// })
// },
}

View File

@@ -0,0 +1,28 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_temp = {
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(`http://tm.tempmusics.tk/url/kw/${songInfo.songmid}/${type}`, {
method: 'get',
headers,
timeout,
lookup: dnsLookup,
family: 4
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(body.msg))
}
})
return requestObj
}
}
export default api_temp

View File

@@ -0,0 +1,39 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_test = {
// getMusicUrl(songInfo, type) {
// const requestObj = httpFetch(`http://45.32.53.128:3002/m/kw/u/${songInfo.songmid}/${type}`, {
// method: 'get',
// headers,
// timeout,
// })
// requestObj.promise = requestObj.promise.then(({ body }) => {
// return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg))
// })
// return requestObj
// },
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/url/kw/${songInfo.songmid}/${type}`, {
method: 'get',
timeout,
headers,
lookup: dnsLookup,
family: 4
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(requestMsg.fail))
}
})
return requestObj
}
}
export default api_test

View File

@@ -0,0 +1,88 @@
import { httpFetch } from '../../request'
import { dateFormat2 } from '../index'
export default {
_requestObj: null,
_requestObj2: null,
async getComment({ songmid }, page = 1, limit = 20) {
if (this._requestObj) this._requestObj.cancelHttp()
const _requestObj = httpFetch(
`http://ncomment.kuwo.cn/com.s?f=web&type=get_comment&aapiver=1&prod=kwplayer_ar_10.5.2.0&digest=15&sid=${songmid}&start=${limit * (page - 1)}&msgflag=1&count=${limit}&newver=3&uid=0`,
{
headers: {
'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9;)'
}
}
)
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code != '200') throw new Error('获取评论失败')
// console.log(body)
const total = body.comments_counts
return {
source: 'kw',
comments: this.filterComment(body.comments),
total,
page,
limit,
maxPage: Math.ceil(total / limit) || 1
}
},
async getHotComment({ songmid }, page = 1, limit = 100) {
if (this._requestObj2) this._requestObj2.cancelHttp()
const _requestObj2 = httpFetch(
`http://ncomment.kuwo.cn/com.s?f=web&type=get_rec_comment&aapiver=1&prod=kwplayer_ar_10.5.2.0&digest=15&sid=${songmid}&start=${limit * (page - 1)}&msgflag=1&count=${limit}&newver=3&uid=0`,
{
headers: {
'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9;)'
}
}
)
const { body, statusCode } = await _requestObj2.promise
if (statusCode != 200 || body.code != '200') throw new Error('获取热门评论失败')
// console.log(body)
const total = body.hot_comments_counts
return {
source: 'kw',
comments: this.filterComment(body.hot_comments),
total,
page,
limit,
maxPage: Math.ceil(total / limit) || 1
}
},
filterComment(rawList) {
if (!rawList) return []
return rawList.map((item) => {
return {
id: item.id,
text: item.msg,
time: item.time,
timeStr: dateFormat2(Number(item.time) * 1000),
userName: item.u_name,
avatar: item.u_pic,
userId: item.u_id,
likedCount: item.like_num,
images: item.mpic ? [decodeURIComponent(item.mpic)] : [],
reply: item.child_comments
? item.child_comments.map((i) => {
return {
id: i.id,
text: i.msg,
time: i.time,
timeStr: dateFormat2(Number(i.time) * 1000),
userName: i.u_name,
avatar: i.u_pic,
userId: i.u_id,
likedCount: i.like_num,
images: i.mpic ? [i.mpic] : []
}
})
: []
}
})
}
}

View File

@@ -0,0 +1,25 @@
import { httpFetch } from '../../request'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = httpFetch(
'http://hotword.kuwo.cn/hotword.s?prod=kwplayer_ar_9.3.0.1&corp=kuwo&newver=2&vipver=9.3.0.1&source=kwplayer_ar_9.3.0.1_40.apk&p2p=1&notrace=0&uid=0&plat=kwplayer_ar&rformat=json&encoding=utf8&tabid=1',
{
headers: {
'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9;)'
}
}
)
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.status !== 'ok') throw new Error('获取热搜词失败')
// console.log(body, statusCode)
return { source: 'kw', list: this.filterList(body.tagvalue) }
},
filterList(rawList) {
return rawList.map((item) => item.key)
}
}

View File

@@ -0,0 +1,98 @@
import { httpFetch } from '../../request'
import tipSearch from './tipSearch'
import musicSearch from './musicSearch'
import { formatSinger } from './util'
import leaderboard from './leaderboard'
import lyric from './lyric'
import pic from './pic'
import songList from './songList'
import hotSearch from './hotSearch'
import comment from './comment'
const kw = {
_musicInfoRequestObj: null,
_musicInfoPromiseCancelFn: null,
_musicPicRequestObj: null,
_musicPicPromiseCancelFn: null,
// context: null,
// init(context) {
// if (this.isInited) return
// this.isInited = true
// this.context = context
// // this.musicSearch.search('我又想你了').then(res => {
// // console.log(res)
// // })
// // this.getMusicUrl('62355680', '320k').then(url => {
// // console.log(url)
// // })
// },
tipSearch,
musicSearch,
leaderboard,
songList,
hotSearch,
comment,
getLyric(songInfo, isGetLyricx) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo, isGetLyricx)
},
handleMusicInfo(songInfo) {
return this.getMusicInfo(songInfo).then((info) => {
// console.log(JSON.stringify(info))
songInfo.name = info.name
songInfo.singer = formatSinger(info.artist)
songInfo.img = info.pic
songInfo.albumName = info.album
return songInfo
// return Object.assign({}, songInfo, {
// name: info.name,
// singer: formatSinger(info.artist),
// img: info.pic,
// albumName: info.album,
// })
})
},
getMusicInfo(songInfo) {
if (this._musicInfoRequestObj) this._musicInfoRequestObj.cancelHttp()
this._musicInfoRequestObj = httpFetch(
`http://www.kuwo.cn/api/www/music/musicInfo?mid=${songInfo.songmid}`
)
return this._musicInfoRequestObj.promise.then(({ body }) => {
return body.code === 200 ? body.data : Promise.reject(new Error(body.msg))
})
},
getMusicUrls(musicInfo, cb) {
let tasks = []
let songId = musicInfo.songmid
musicInfo.types.forEach((type) => {
tasks.push(kw.getMusicUrl(songId, type.type).promise)
})
Promise.all(tasks).then((urlInfo) => {
let typeUrl = {}
urlInfo.forEach((info) => {
typeUrl[info.type] = info.url
})
cb(typeUrl)
})
},
getPic(songInfo) {
return pic.getPic(songInfo)
},
getMusicDetailPageUrl(songInfo) {
return `http://www.kuwo.cn/play_detail/${songInfo.songmid}`
}
// init() {
// return getToken()
// },
}
export default kw

View File

@@ -0,0 +1,48 @@
import { inflate } from 'zlib'
import iconv from 'iconv-lite'
const handleInflate = async (data: Buffer) => {
return new Promise((resolve: (result: Buffer) => void, reject) => {
inflate(data, (err, result) => {
if (err) {
reject(err)
return
}
resolve(result)
})
})
}
const buf_key = Buffer.from('yeelion')
const buf_key_len = buf_key.length
const decodeLyric = async (buf: Buffer, isGetLyricx: boolean) => {
// const info = buf.slice(0, index).toString()
// if (!info.startsWith('tp=content')) return null
// const isLyric = info.includes('\r\nlrcx=0\r\n')
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
// const index = buf.indexOf('\r\n\r\n') + 4
const lrcData = await handleInflate(buf.subarray(buf.indexOf('\r\n\r\n') + 4))
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
const buf_str = Buffer.from(lrcData.toString(), 'base64')
const buf_str_len = buf_str.length
const output = new Uint8Array(buf_str_len)
let i = 0
while (i < buf_str_len) {
let j = 0
while (j < buf_key_len && i < buf_str_len) {
output[i] = buf_str[i] ^ buf_key[j]
i++
j++
}
}
return iconv.decode(Buffer.from(output), 'gb18030')
}
export default async (lrcBase64: string, isGetLyricx: boolean) => {
const lrc = await decodeLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx)
return Buffer.from(lrc).toString('base64')
}

View File

@@ -0,0 +1,284 @@
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../index'
import { formatSinger, wbdCrypto } from './util'
const boardList = [
{ id: 'kw__93', name: '飙升榜', bangid: '93' },
{ id: 'kw__17', name: '新歌榜', bangid: '17' },
{ id: 'kw__16', name: '热歌榜', bangid: '16' },
{ id: 'kw__158', name: '抖音热歌榜', bangid: '158' },
{ id: 'kw__292', name: '铃声榜', bangid: '292' },
{ id: 'kw__284', name: '热评榜', bangid: '284' },
{ id: 'kw__290', name: 'ACG新歌榜', bangid: '290' },
{ id: 'kw__286', name: '台湾KKBOX榜', bangid: '286' },
{ id: 'kw__279', name: '冬日暖心榜', bangid: '279' },
{ id: 'kw__281', name: '巴士随身听榜', bangid: '281' },
{ id: 'kw__255', name: 'KTV点唱榜', bangid: '255' },
{ id: 'kw__280', name: '家务进行曲榜', bangid: '280' },
{ id: 'kw__282', name: '熬夜修仙榜', bangid: '282' },
{ id: 'kw__283', name: '枕边轻音乐榜', bangid: '283' },
{ id: 'kw__278', name: '古风音乐榜', bangid: '278' },
{ id: 'kw__264', name: 'Vlog音乐榜', bangid: '264' },
{ id: 'kw__242', name: '电音榜', bangid: '242' },
{ id: 'kw__187', name: '流行趋势榜', bangid: '187' },
{ id: 'kw__204', name: '现场音乐榜', bangid: '204' },
{ id: 'kw__186', name: 'ACG神曲榜', bangid: '186' },
{ id: 'kw__185', name: '最强翻唱榜', bangid: '185' },
{ id: 'kw__26', name: '经典怀旧榜', bangid: '26' },
{ id: 'kw__104', name: '华语榜', bangid: '104' },
{ id: 'kw__182', name: '粤语榜', bangid: '182' },
{ id: 'kw__22', name: '欧美榜', bangid: '22' },
{ id: 'kw__184', name: '韩语榜', bangid: '184' },
{ id: 'kw__183', name: '日语榜', bangid: '183' },
{ id: 'kw__145', name: '会员畅听榜', bangid: '145' },
{ id: 'kw__153', name: '网红新歌榜', bangid: '153' },
{ id: 'kw__64', name: '影视金曲榜', bangid: '64' },
{ id: 'kw__176', name: 'DJ嗨歌榜', bangid: '176' },
{ id: 'kw__106', name: '真声音', bangid: '106' },
{ id: 'kw__12', name: 'Billboard榜', bangid: '12' },
{ id: 'kw__49', name: 'iTunes音乐榜', bangid: '49' },
{ id: 'kw__180', name: 'beatport电音榜', bangid: '180' },
{ id: 'kw__13', name: '英国UK榜', bangid: '13' },
{ id: 'kw__164', name: '百大DJ榜', bangid: '164' },
{ id: 'kw__246', name: 'YouTube音乐排行榜', bangid: '246' },
{ id: 'kw__265', name: '韩国Genie榜', bangid: '265' },
{ id: 'kw__14', name: '韩国M-net榜', bangid: '14' },
{ id: 'kw__8', name: '香港电台榜', bangid: '8' },
{ id: 'kw__15', name: '日本公信榜', bangid: '15' },
{ id: 'kw__151', name: '腾讯音乐人原创榜', bangid: '151' }
]
const sortQualityArray = (array) => {
const qualityMap = {
flac24bit: 4,
flac: 3,
'320k': 2,
'128k': 1
}
const rawQualityArray = []
const newQualityArray = []
array.forEach((item, index) => {
const type = qualityMap[item.type]
if (!type) return
rawQualityArray.push({ type, index })
})
rawQualityArray.sort((a, b) => a.type - b.type)
rawQualityArray.forEach((item) => {
newQualityArray.push(array[item.index])
})
return newQualityArray
}
export default {
list: [
{
id: 'kwbiaosb',
name: '飙升榜',
bangid: 93
},
{
id: 'kwregb',
name: '热歌榜',
bangid: 16
},
{
id: 'kwhuiyb',
name: '会员榜',
bangid: 145
},
{
id: 'kwdouyb',
name: '抖音榜',
bangid: 158
},
{
id: 'kwqsb',
name: '趋势榜',
bangid: 187
},
{
id: 'kwhuaijb',
name: '怀旧榜',
bangid: 26
},
{
id: 'kwhuayb',
name: '华语榜',
bangid: 104
},
{
id: 'kwyueyb',
name: '粤语榜',
bangid: 182
},
{
id: 'kwoumb',
name: '欧美榜',
bangid: 22
},
{
id: 'kwhanyb',
name: '韩语榜',
bangid: 184
},
{
id: 'kwriyb',
name: '日语榜',
bangid: 183
}
],
// getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
regExps: {
mInfo: /level:(\w+),bitrate:(\d+),format:(\w+),size:([\w.]+)/
},
limit: 100,
_requestBoardsObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch(
'http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2'
)
return this._requestBoardsObj.promise
},
getData(url) {
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
filterData(rawList) {
return rawList.map((item) => {
let types = []
const _types = {}
const qualitys = new Set()
item.n_minfo.split(';').forEach((i) => {
const info = i.match(this.regExps.mInfo)
if (!info) return
const quality = info[2]
const size = info[4].toLocaleUpperCase()
if (qualitys.has(quality)) return
qualitys.add(quality)
switch (quality) {
case '4000':
types.push({ type: 'flac24bit', size })
_types.flac24bit = { size }
break
case '2000':
types.push({ type: 'flac', size })
_types.flac = { size }
break
case '320':
types.push({ type: '320k', size })
_types['320k'] = { size }
break
case '128':
types.push({ type: '128k', size })
_types['128k'] = { size }
break
}
})
types = sortQualityArray(types)
return {
singer: formatSinger(decodeName(item.artist)),
name: decodeName(item.name),
albumName: decodeName(item.album),
albumId: item.albumId,
songmid: item.id,
source: 'kw',
interval: formatPlayTime(parseInt(item.duration)),
img: item.pic,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.source != '1') continue
list.push({
id: 'kw__' + board.sourceid,
name: board.name,
bangid: String(board.sourceid)
})
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// console.log(response.body)
// if (response.statusCode !== 200 || !response.body.child) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.child)
// // console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'kw',
// }
this.list = boardList
return {
list: boardList,
source: 'kw'
}
},
getList(id, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
const requestBody = {
uid: '',
devId: '',
sFrom: 'kuwo_sdk',
user_type: 'AP',
carSource: 'kwplayercar_ar_6.0.1.0_apk_keluze.apk',
id,
pn: page - 1,
rn: this.limit
}
const requestUrl = `https://wbd.kuwo.cn/api/bd/bang/bang_info?${wbdCrypto.buildParam(requestBody)}`
const request = httpFetch(requestUrl).promise
return request.then(({ statusCode, body }) => {
const rawData = wbdCrypto.decodeData(body)
// console.log(rawData)
const data = rawData.data
if (statusCode !== 200 || rawData.code != 200 || !data.musiclist)
return this.getList(id, page, retryNum)
const total = parseInt(data.total)
const list = this.filterData(data.musiclist)
return {
total,
list,
limit: this.limit,
page,
source: 'kw'
}
})
}
// getDetailPageUrl(id) {
// return `http://www.kuwo.cn/rankList/${id}`
// },
}

View File

@@ -0,0 +1,272 @@
import { httpFetch } from '../../request'
import { decodeLyric, lrcTools } from './util'
import { decodeName } from '../index'
/*
export default {
formatTime(time) {
let m = parseInt(time / 60)
let s = (time % 60).toFixed(2)
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
},
sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
lrc.push(item)
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
if (lrcT.length && lrc.length > lrcT.length) {
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
}
return {
lrc,
lrcT,
}
},
transformLrc(songinfo, lrclist) {
return `[ti:${songinfo.songName}]\n[ar:${songinfo.artist}]\n[al:${songinfo.album}]\n[by:]\n[offset:0]\n${lrclist ? lrclist.map(l => `[${this.formatTime(l.time)}]${l.lineLyric}\n`).join('') : '暂无歌词'}`
},
getLyric(songId) {
const requestObj = httpFetch(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${songId}`)
requestObj.promise = requestObj.promise.then(({ body }) => {
// console.log(body)
if (!body.data?.lrclist?.length) return Promise.reject(new Error('Get lyric failed'))
let lrcInfo
try {
lrcInfo = this.sortLrcArr(body.data.lrclist)
} catch {
return Promise.reject(new Error('Get lyric failed'))
}
// console.log(body.data.lrclist)
// console.log(lrcInfo.lrc, lrcInfo.lrcT)
// console.log({
// lyric: decodeName(this.transformLrc(body.data.songinfo, lrc)),
// tlyric: decodeName(this.transformLrc(body.data.songinfo, lrcT)),
// })
return {
lyric: decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrc)),
tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(body.data.songinfo, lrcInfo.lrcT)) : '',
}
})
return requestObj
},
}
*/
const buf_key = Buffer.from('yeelion')
const buf_key_len = buf_key.length
const buildParams = (id, isGetLyricx) => {
let params = `user=12345,web,web,web&requester=localhost&req=1&rid=MUSIC_${id}`
if (isGetLyricx) params += '&lrcx=1'
const buf_str = Buffer.from(params)
const buf_str_len = buf_str.length
const output = new Uint16Array(buf_str_len)
let i = 0
while (i < buf_str_len) {
let j = 0
while (j < buf_key_len && i < buf_str_len) {
output[i] = buf_key[j] ^ buf_str[i]
i++
j++
}
}
return Buffer.from(output).toString('base64')
}
// console.log(buildParams('207527604', false))
// console.log(buildParams('207527604', true))
const timeExp = /^\[([\d:.]*)\]{1}/g
const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
const lyricxTag = /^<-?\d+,-?\d+>/
export default {
/* sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
let markIndex = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue
const index = lrc.findIndex(l => l.time == item.time)
markIndex.push(index)
if (index == lrc.length - 1) {
lrcT.push({ ...lrc[index], time: item.time })
lrc.push(item)
} else {
lrcT.push({ ...lrc[index], time: lrc[index + 1].time })
if (item.text) {
// const lastIndex = lrc.length - 1
// markIndex.push(lastIndex)
// lrcT.push({ ...lrc[lastIndex], time: lrc[lastIndex - 1].time })
lrc.push(item)
}
}
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
// console.log(markIndex)
markIndex = Array.from(new Set(markIndex))
for (let index = markIndex.length - 1; index >= 0; index--) {
lrc.splice(markIndex[index], 1)
}
// if (lrcT.length) {
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
// }
console.log(lrc, lrcT)
return {
lrc,
lrcT,
}
}, */
sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
let isLyricx = false
for (const item of arr) {
if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
lrc.push(item)
} else {
lrc.push(item)
lrcSet.add(item.time)
}
if (!isLyricx && lyricxTag.test(item.text)) isLyricx = true
}
if (!isLyricx && lrcT.length > lrc.length * 0.3 && lrc.length - lrcT.length > 6) {
throw new Error('failed')
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
}
return {
lrc,
lrcT
}
},
transformLrc(tags, lrclist) {
return `${tags.join('\n')}\n${lrclist ? lrclist.map((l) => `[${l.time}]${l.text}\n`).join('') : '暂无歌词'}`
},
parseLrc(lrc) {
const lines = lrc.split(/\r\n|\r|\n/)
let tags = []
let lrcArr = []
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
let time = RegExp.$1
if (/\.\d\d$/.test(time)) time += '0'
lrcArr.push({
time,
text
})
} else if (lrcTools.rxps.tagLine.test(line)) {
tags.push(line)
}
}
const lrcInfo = this.sortLrcArr(lrcArr)
return {
lyric: decodeName(this.transformLrc(tags, lrcInfo.lrc)),
tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(tags, lrcInfo.lrcT)) : ''
}
},
// getLyric2(musicInfo, isGetLyricx = true) {
// const requestObj = httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`)
// requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => {
// if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
// return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
// let lrcInfo
// console.log(Buffer.from(base64Data, 'base64').toString())
// try {
// lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
// } catch {
// return Promise.reject(new Error('Get lyric failed'))
// }
// if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '')
// lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric)
// // console.log(lrcInfo.lyric)
// // console.log(lrcInfo.tlyric)
// // console.log(lrcInfo.lxlyric)
// // console.log(JSON.stringify(lrcInfo))
// })
// })
// return requestObj
// },
getLyric(musicInfo, isGetLyricx = true) {
// this.getLyric2(musicInfo)
const requestObj = httpFetch(
`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`
)
requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => {
if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body)))
return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then((base64Data) => {
// let lrcInfo
// try {
// lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
// } catch {
// return Promise.reject(new Error('Get lyric failed'))
// }
let lrcInfo
// console.log(Buffer.from(base64Data, 'base64').toString())
try {
lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString())
} catch (err) {
return Promise.reject(new Error('Get lyric failed'))
}
// console.log(lrcInfo)
if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '')
try {
lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric)
} catch {
lrcInfo.lxlyric = ''
}
lrcInfo.lyric = lrcInfo.lyric.replace(lrcTools.rxps.wordTimeAll, '')
if (!existTimeExp.test(lrcInfo.lyric)) return Promise.reject(new Error('Get lyric failed'))
// console.log(lrcInfo)
return lrcInfo
})
})
return requestObj
}
}

View File

@@ -0,0 +1,129 @@
// import '../../polyfill/array.find'
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../index'
// import { debug } from '../../utils/env'
import { formatSinger } from './util'
export default {
regExps: {
mInfo: /level:(\w+),bitrate:(\d+),format:(\w+),size:([\w.]+)/
},
limit: 30,
total: 0,
page: 0,
allPage: 1,
// cancelFn: null,
musicSearch(str, page, limit) {
const musicSearchRequestObj = httpFetch(
`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`
)
return musicSearchRequestObj.promise
},
// getImg(songId) {
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)
// },
// getLrc(songId) {
// return httpGet(`http://mobile.kuwo.cn/mpage/html5/songinfoandlrc?mid=${songId}&flag=0`)
// },
handleResult(rawData) {
const result = []
if (!rawData) return result
// console.log(rawData)
for (let i = 0; i < rawData.length; i++) {
const info = rawData[i]
let songId = info.MUSICRID.replace('MUSIC_', '')
// const format = (info.FORMATS || info.formats).split('|')
if (!info.N_MINFO) {
console.log('N_MINFO is undefined')
return null
}
const types = []
const _types = {}
let infoArr = info.N_MINFO.split(';')
for (let info of infoArr) {
info = info.match(this.regExps.mInfo)
if (info) {
switch (info[2]) {
case '4000':
types.push({ type: 'flac24bit', size: info[4] })
_types.flac24bit = {
size: info[4].toLocaleUpperCase()
}
break
case '2000':
types.push({ type: 'flac', size: info[4] })
_types.flac = {
size: info[4].toLocaleUpperCase()
}
break
case '320':
types.push({ type: '320k', size: info[4] })
_types['320k'] = {
size: info[4].toLocaleUpperCase()
}
break
case '128':
types.push({ type: '128k', size: info[4] })
_types['128k'] = {
size: info[4].toLocaleUpperCase()
}
break
}
}
}
types.reverse()
let interval = parseInt(info.DURATION)
result.push({
name: decodeName(info.SONGNAME),
singer: formatSinger(decodeName(info.ARTIST)),
source: 'kw',
// img = (info.album.name === '' || info.album.name === '空')
// ? `http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_160911.jpg`
// : `https://y.gtimg.cn/music/photo_new/T002R500x500M000${info.album.mid}.jpg`
songmid: songId,
albumId: decodeName(info.ALBUMID || ''),
interval: Number.isNaN(interval) ? 0 : formatPlayTime(interval),
albumName: info.ALBUM ? decodeName(info.ALBUM) : '',
lrc: null,
img: null,
otherSource: null,
types,
_types,
typeUrl: {}
})
}
// console.log(result)
return result
},
search(str, page = 1, limit, retryNum = 0) {
if (retryNum > 2) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then(({ body: result }) => {
// console.log(result)
if (!result || (result.TOTAL !== '0' && result.SHOW === '0'))
return this.search(str, page, limit, ++retryNum)
let list = this.handleResult(result.abslist)
if (list == null) return this.search(str, page, limit, ++retryNum)
this.total = parseInt(result.TOTAL)
this.page = page
this.allPage = Math.ceil(this.total / limit)
return Promise.resolve({
list,
allPage: this.allPage,
total: this.total,
limit,
source: 'kw'
})
})
}
}

View File

@@ -0,0 +1,11 @@
import { httpFetch } from '../../request'
export default {
getPic({ songmid }) {
const requestObj = httpFetch(
`http://artistpicserver.kuwo.cn/pic.web?corp=kuwo&type=rid_pic&pictype=500&size=500&rid=${songmid}`
)
requestObj.promise = requestObj.promise.then(({ body }) => (/^http/.test(body) ? body : null))
return requestObj.promise
}
}

View File

@@ -0,0 +1,517 @@
import { httpFetch } from '../../request'
import { formatPlayTime, decodeName } from '../index'
import { formatSinger, objStr2JSON } from './util'
import album from './album'
export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
limit_list: 36,
limit_song: 1000,
successCode: 200,
sortList: [
{
name: '最新',
id: 'new'
},
{
name: '最热',
id: 'hot'
}
],
regExps: {
mInfo: /level:(\w+),bitrate:(\d+),format:(\w+),size:([\w.]+)/,
// http://www.kuwo.cn/playlist_detail/2886046289
// https://m.kuwo.cn/h5app/playlist/2736267853?t=qqfriend
listDetailLink: /^.+\/playlist(?:_detail)?\/(\d+)(?:\?.*|&.*$|#.*$|$)/
},
tagsUrl:
'http://wapi.kuwo.cn/api/pc/classify/playlist/getTagList?cmd=rcm_keyword_playlist&user=0&prod=kwplayer_pc_9.0.5.0&vipver=9.0.5.0&source=kwplayer_pc_9.0.5.0&loginUid=0&loginSid=0&appUid=76039576',
hotTagUrl:
'http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmTagList?loginUid=0&loginSid=0&appUid=76039576',
getListUrl({ sortId, id, type, page }) {
if (!id)
return `http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?loginUid=0&loginSid=0&appUid=76039576&&pn=${page}&rn=${this.limit_list}&order=${sortId}`
switch (type) {
case '10000':
return `http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&pn=${page}&id=${id}&rn=${this.limit_list}`
case '43':
return `http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=${id}&prod=pc`
}
// http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&id=173&pn=1&rn=100
},
getListDetailUrl(id, page) {
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2858093057&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
return `http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}&rn=${this.limit_song}&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1`
// http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=140&prod=pc
},
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
// 获取标签
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.tagsUrl)
return this._requestObj_tags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body.data)
})
},
// 获取标签
getHotTag(tryNum = 0) {
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_hotTags = httpFetch(this.hotTagUrl)
return this._requestObj_hotTags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getHotTag(++tryNum)
return this.filterInfoHotTag(body.data[0].data)
})
},
filterInfoHotTag(rawList) {
return rawList.map((item) => ({
id: `${item.id}-${item.digest}`,
name: item.name,
source: 'kw'
}))
},
filterTagInfo(rawList) {
return rawList.map((type) => ({
name: type.name,
list: type.data.map((item) => ({
parent_id: type.id,
parent_name: type.name,
id: `${item.id}-${item.digest}`,
name: item.name,
source: 'kw'
}))
}))
},
// 获取列表数据
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
let id
let type
if (tagId) {
let arr = tagId.split('-')
id = arr[0]
type = arr[1]
} else {
id = null
}
this._requestObj_list = httpFetch(this.getListUrl({ sortId, id, type, page }))
return this._requestObj_list.promise.then(({ body }) => {
if (!id || type == '10000') {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return {
list: this.filterList(body.data.data),
total: body.data.total,
page: body.data.pn,
limit: body.data.rn,
source: 'kw'
}
} else if (!body.length) {
return this.getList(sortId, tagId, page, ++tryNum)
}
return {
list: this.filterList2(body),
total: 1000,
page,
limit: 1000,
source: 'kw'
}
})
},
/**
* 格式化播放数量
* @param {*} num
*/
formatPlayCount(num) {
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
return num
},
filterList(rawData) {
return rawData.map((item) => ({
play_count: this.formatPlayCount(item.listencnt),
id: `digest-${item.digest}__${item.id}`,
author: item.uname,
name: item.name,
// time: item.publish_time,
total: item.total,
img: item.img,
grade: item.favorcnt / 10,
desc: item.desc,
source: 'kw'
}))
},
filterList2(rawData) {
// console.log(rawData)
const list = []
rawData.forEach((item) => {
if (!item.label) return
list.push(
...item.list.map((item) => ({
play_count: item.play_count && this.formatPlayCount(item.listencnt),
id: `digest-${item.digest}__${item.id}`,
author: item.uname,
name: item.name,
total: item.total,
// time: item.publish_time,
img: item.img,
grade: item.favorcnt && item.favorcnt / 10,
desc: item.desc,
source: 'kw'
}))
)
})
return list
},
getListDetailDigest8(id, page, tryNum = 0) {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
const requestObj = httpFetch(this.getListDetailUrl(id, page))
return requestObj.promise.then(({ body }) => {
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body.musiclist),
page,
limit: body.rn,
total: body.total,
source: 'kw',
info: {
name: body.title,
img: body.pic,
desc: body.info,
author: body.uname,
play_count: this.formatPlayCount(body.playnum)
}
}
})
},
getListDetailDigest5Info(id, tryNum = 0) {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
const requestObj = httpFetch(
`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`
)
return requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200 || !body.child) return this.getListDetail(id, ++tryNum)
// console.log(body)
return body.child.length ? body.child[0].sourceid : null
})
},
getListDetailDigest5Music(id, page, tryNum = 0) {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
const requestObj = httpFetch(
`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`
)
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body.musiclist),
page,
limit: body.rn,
total: body.total,
source: 'kw',
info: {
name: body.title,
img: body.pic,
desc: body.info,
author: body.uname,
play_count: this.formatPlayCount(body.playnum)
}
}
})
},
async getListDetailDigest5(id, page, retryNum) {
const detailId = await this.getListDetailDigest5Info(id, retryNum)
return this.getListDetailDigest5Music(detailId, page, retryNum)
},
filterBDListDetail(rawList) {
return rawList.map((item) => {
let types = []
let _types = {}
for (let info of item.audios) {
info.size = info.size?.toLocaleUpperCase()
switch (info.bitrate) {
case '4000':
types.push({ type: 'flac24bit', size: info.size })
_types.flac24bit = {
size: info.size
}
break
case '2000':
types.push({ type: 'flac', size: info.size })
_types.flac = {
size: info.size
}
break
case '320':
types.push({ type: '320k', size: info.size })
_types['320k'] = {
size: info.size
}
break
case '128':
types.push({ type: '128k', size: info.size })
_types['128k'] = {
size: info.size
}
break
}
}
types.reverse()
return {
singer: item.artists.map((s) => s.name).join('、'),
name: item.name,
albumName: item.album,
albumId: item.albumId,
songmid: item.id,
source: 'kw',
interval: formatPlayTime(item.duration),
img: item.albumPic,
releaseDate: item.releaseDate,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
getReqId() {
function t() {
return ((65536 * (1 + Math.random())) | 0).toString(16).substring(1)
}
return t() + t() + t() + t() + t() + t() + t() + t()
},
async getListDetailMusicListByBDListInfo(id, source) {
const { body: infoData } = await httpFetch(
`https://bd-api.kuwo.cn/api/service/playlist/info/${id}?reqId=${this.getReqId()}&source=${source}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
plat: 'h5'
}
}
).promise.catch(() => ({ code: 0 }))
if (infoData.code != 200) return null
return {
name: infoData.data.name,
img: infoData.data.pic,
desc: infoData.data.description,
author: infoData.data.creatorName,
play_count: infoData.data.playNum
}
},
async getListDetailMusicListByBDUserPub(id) {
const { body: infoData } = await httpFetch(
`https://bd-api.kuwo.cn/api/ucenter/users/pub/${id}?reqId=${this.getReqId()}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
plat: 'h5'
}
}
).promise.catch(() => ({ code: 0 }))
if (infoData.code != 200) return null
// console.log(infoData)
return {
name: infoData.data.userInfo.nickname + '喜欢的音乐',
img: infoData.data.userInfo.headImg,
desc: '',
author: infoData.data.userInfo.nickname,
play_count: ''
}
},
async getListDetailMusicListByBDList(id, source, page, tryNum = 0) {
const { body: listData } = await httpFetch(
`https://bd-api.kuwo.cn/api/service/playlist/${id}/musicList?reqId=${this.getReqId()}&source=${source}&pn=${page}&rn=${this.limit_song}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
plat: 'h5'
}
}
).promise.catch(() => {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
return this.getListDetailMusicListByBDList(id, source, page, ++tryNum)
})
if (listData.code !== 200) return Promise.reject(new Error('failed'))
return {
list: this.filterBDListDetail(listData.data.list),
page,
limit: listData.data.pageSize,
total: listData.data.total,
source: 'kw'
}
},
async getListDetailMusicListByBD(id, page) {
const uid = /uid=(\d+)/.exec(id)?.[1]
const listId = /playlistId=(\d+)/.exec(id)?.[1]
const source = /source=(\d+)/.exec(id)?.[1]
if (!listId) return Promise.reject(new Error('failed'))
const task = [this.getListDetailMusicListByBDList(listId, source, page)]
switch (source) {
case '4':
task.push(this.getListDetailMusicListByBDListInfo(listId, source))
break
case '5':
task.push(this.getListDetailMusicListByBDUserPub(uid ?? listId))
break
}
const [listData, info] = await Promise.all(task)
listData.info = info ?? {
name: '',
img: '',
desc: '',
author: '',
play_count: ''
}
// console.log(listData)
return listData
},
// 获取歌曲列表内的音乐
getListDetail(id, page, retryNum = 0) {
// console.log(id)
// https://h5app.kuwo.cn/m/bodian/collection.html?uid=000&playlistId=000&source=5&ownerId=000
// https://h5app.kuwo.cn/m/bodian/collection.html?uid=000&playlistId=000&source=4&ownerId=
if (/\/bodian\//.test(id)) return this.getListDetailMusicListByBD(id, page)
if (/[?&:/]/.test(id)) id = id.replace(this.regExps.listDetailLink, '$1')
else if (/^digest-/.test(id)) {
let [digest, _id] = id.split('__')
digest = digest.replace('digest-', '')
id = _id
switch (digest) {
case '8':
break
case '13':
return album.getAlbumListDetail(id, page, retryNum)
case '5':
default:
return this.getListDetailDigest5(id, page, retryNum)
}
}
return this.getListDetailDigest8(id, page, retryNum)
},
filterListDetail(rawData) {
// console.log(rawData)
return rawData.map((item) => {
let infoArr = item.N_MINFO.split(';')
let types = []
let _types = {}
for (let info of infoArr) {
info = info.match(this.regExps.mInfo)
if (info) {
switch (info[2]) {
case '4000':
types.push({ type: 'flac24bit', size: info[4] })
_types.flac24bit = {
size: info[4].toLocaleUpperCase()
}
break
case '2000':
types.push({ type: 'flac', size: info[4] })
_types.flac = {
size: info[4].toLocaleUpperCase()
}
break
case '320':
types.push({ type: '320k', size: info[4] })
_types['320k'] = {
size: info[4].toLocaleUpperCase()
}
break
case '128':
types.push({ type: '128k', size: info[4] })
_types['128k'] = {
size: info[4].toLocaleUpperCase()
}
break
}
}
}
types.reverse()
return {
singer: formatSinger(decodeName(item.artist)),
name: decodeName(item.name),
albumName: decodeName(item.album),
albumId: item.albumid,
songmid: item.id,
source: 'kw',
interval: formatPlayTime(parseInt(item.duration)),
img: null,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
getTags() {
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({
tags,
hotTag,
source: 'kw'
}))
},
getDetailPageUrl(id) {
if (/[?&:/]/.test(id)) id = id.replace(this.regExps.listDetailLink, '$1')
else if (/^digest-/.test(id)) {
let result = id.split('__')
id = result[1]
}
return `http://www.kuwo.cn/playlist_detail/${id}`
},
search(text, page, limit = 20) {
return httpFetch(
`http://search.kuwo.cn/r.s?all=${encodeURIComponent(text)}&pn=${page - 1}&rn=${limit}&rformat=json&encoding=utf8&ver=mbox&vipver=MUSIC_8.7.7.0_BCS37&plat=pc&devid=28156413&ft=playlist&pay=0&needliveshow=0`
).promise.then(({ body }) => {
body = objStr2JSON(body)
// console.log(body)
return {
list: body.abslist.map((item) => {
return {
play_count: this.formatPlayCount(item.playcnt),
id: String(item.playlistid),
author: decodeName(item.nickname),
name: decodeName(item.name),
total: item.songnum,
// time: item.publish_time,
img: item.pic,
desc: decodeName(item.intro),
source: 'kw'
}
}),
limit,
total: parseInt(body.TOTAL),
source: 'kw'
}
})
}
}
// getList
// getTags
// getListDetail

View File

@@ -0,0 +1,35 @@
// import { decodeName } from '../../index'
// import { tokenRequest } from './util'
import { httpFetch } from '../../request'
export default {
regExps: {
relWord: /RELWORD=(.+)/
},
requestObj: null,
async tipSearchBySong(str) {
// 报错403加了referer还是有问题直接换一个
// this.requestObj = await tokenRequest(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`)
this.cancelTipSearch()
this.requestObj = httpFetch(
`https://tips.kuwo.cn/t.s?corp=kuwo&newver=3&p2p=1&notrace=0&c=mbox&w=${encodeURIComponent(str)}&encoding=utf8&rformat=json`,
{
Referer: 'http://www.kuwo.cn/'
}
)
return this.requestObj.promise.then(({ body, statusCode }) => {
if (statusCode != 200 || !body.WORDITEMS) return Promise.reject(new Error('请求失败'))
return body.WORDITEMS
})
},
handleResult(rawData) {
return rawData.map((item) => item.RELWORD)
},
cancelTipSearch() {
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
},
async search(str) {
return this.tipSearchBySong(str).then((result) => this.handleResult(result))
}
}

View File

@@ -0,0 +1,231 @@
// import { httpGet, httpFetch } from '../../request'
// import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
// import { rendererInvoke } from '@common/rendererIpc'
import kwdecode from './kwdecode'
import { createCipheriv, createDecipheriv } from 'crypto'
import { toMD5 } from '../utils'
// const kw_token = {
// token: null,
// isGetingToken: false,
// }
// const translationMap = {
// "{'": '{"',
// "'}\n": '"}',
// "'}": '"}',
// "':'": '":"',
// "','": '","',
// "':{'": '":{"',
// "':['": '":["',
// "'}],'": '"}],"',
// "':[{'": '":[{"',
// "'},'": '"},"',
// "'},{'": '"},{"',
// "':[],'": '":[],"',
// "':{},'": '":{},"',
// "'}]}": '"}]}',
// }
// export const objStr2JSON = str => {
// return JSON.parse(str.replace(/(^{'|'}\n$|'}$|':'|','|':\[{'|'}\],'|':{'|'},'|'},{'|':\['|':\[\],'|':{},'|'}]})/g, s => translationMap[s]))
// }
export const objStr2JSON = (str) => {
return JSON.parse(
str.replace(/('(?=(,\s*')))|('(?=:))|((?<=([:,]\s*))')|((?<={)')|('(?=}))/g, '"')
)
}
export const formatSinger = (rawData) => rawData.replace(/&/g, '、')
export const matchToken = (headers) => {
try {
return headers['set-cookie'][0].match(/kw_token=(\w+)/)[1]
} catch (err) {
return null
}
}
// const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
// export const getToken = (retryNum = 0) => new Promise((resolve, reject) => {
// if (retryNum > 2) return Promise.reject(new Error('try max num'))
// if (kw_token.isGetingToken) return wait(1000).then(() => getToken(retryNum).then(token => resolve(token)))
// if (kw_token.token) return resolve(kw_token.token)
// kw_token.isGetingToken = true
// httpGet('http://www.kuwo.cn/', (err, resp) => {
// kw_token.isGetingToken = false
// if (err) return getToken(++retryNum)
// if (resp.statusCode != 200) return reject(new Error('获取失败'))
// const token = kw_token.token = matchToken(resp.headers)
// resolve(token)
// })
// })
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const decodeLyric = (base64Data) => kwdecode(base64Data, false)
// export const tokenRequest = async(url, options = {}) => {
// let token = kw_token.token
// if (!token) token = await getToken()
// if (!options.headers) {
// options.headers = {
// Referer: 'http://www.kuwo.cn/',
// csrf: token,
// cookie: 'kw_token=' + token,
// }
// }
// const requestObj = httpFetch(url, options)
// requestObj.promise = requestObj.promise.then(resp => {
// // console.log(resp)
// if (resp.statusCode == 200) {
// kw_token.token = matchToken(resp.headers)
// }
// return resp
// })
// return requestObj
// }
export const lrcTools = {
rxps: {
wordLine: /^(\[\d{1,2}:.*\d{1,4}\])\s*(\S+(?:\s+\S+)*)?\s*/,
tagLine: /\[(ver|ti|ar|al|offset|by|kuwo):\s*(\S+(?:\s+\S+)*)\s*\]/,
wordTimeAll: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/g,
wordTime: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/
},
offset: 1,
offset2: 1,
isOK: false,
lines: [],
tags: [],
getWordInfo(str, str2, prevWord) {
const offset = parseInt(str)
const offset2 = parseInt(str2)
let startTime = Math.abs((offset + offset2) / (this.offset * 2))
let endTime = Math.abs((offset - offset2) / (this.offset2 * 2)) + startTime
if (prevWord) {
if (startTime < prevWord.endTime) {
prevWord.endTime = startTime
if (prevWord.startTime > prevWord.endTime) {
prevWord.startTime = prevWord.endTime
}
prevWord.newTimeStr = `<${prevWord.startTime},${prevWord.endTime - prevWord.startTime}>`
// console.log(prevWord)
}
}
return {
startTime,
endTime,
timeStr: `<${startTime},${endTime - startTime}>`
}
},
parseLine(line) {
if (line.length < 6) return
let result = this.rxps.wordLine.exec(line)
if (result) {
const time = result[1]
let words = result[2]
if (words == null) {
words = ''
}
const wordTimes = words.match(this.rxps.wordTimeAll)
if (!wordTimes) return
// console.log(wordTimes)
let preTimeInfo
for (const timeStr of wordTimes) {
const result = this.rxps.wordTime.exec(timeStr)
const wordInfo = this.getWordInfo(result[1], result[2], preTimeInfo)
words = words.replace(timeStr, wordInfo.timeStr)
if (preTimeInfo?.newTimeStr)
words = words.replace(preTimeInfo.timeStr, preTimeInfo.newTimeStr)
preTimeInfo = wordInfo
}
this.lines.push(time + words)
return
}
result = this.rxps.tagLine.exec(line)
if (!result) return
if (result[1] == 'kuwo') {
let content = result[2]
if (content != null && content.includes('][')) {
content = content.substring(0, content.indexOf(']['))
}
const valueOf = parseInt(content, 8)
this.offset = Math.trunc(valueOf / 10)
this.offset2 = Math.trunc(valueOf % 10)
if (
this.offset == 0 ||
Number.isNaN(this.offset) ||
this.offset2 == 0 ||
Number.isNaN(this.offset2)
) {
this.isOK = false
}
} else {
this.tags.push(line)
}
},
parse(lrc) {
// console.log(lrc)
const lines = lrc.split(/\r\n|\r|\n/)
const tools = Object.create(this)
tools.isOK = true
tools.offset = 1
tools.offset2 = 1
tools.lines = []
tools.tags = []
for (const line of lines) {
if (!tools.isOK) throw new Error('failed')
tools.parseLine(line)
}
if (!tools.lines.length) return ''
let lrcs = tools.lines.join('\n')
if (tools.tags.length) lrcs = `${tools.tags.join('\n')}\n${lrcs}`
// console.log(lrcs)
return lrcs
}
}
const createAesEncrypt = (buffer, mode, key, iv) => {
const cipher = createCipheriv(mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
const createAesDecrypt = (buffer, mode, key, iv) => {
const cipher = createDecipheriv(mode, key, iv)
return Buffer.concat([cipher.update(buffer), cipher.final()])
}
export const wbdCrypto = {
aesMode: 'aes-128-ecb',
aesKey: Buffer.from(
[112, 87, 39, 61, 199, 250, 41, 191, 57, 68, 45, 114, 221, 94, 140, 228],
'binary'
),
aesIv: '',
appId: 'y67sprxhhpws',
decodeData(base64Result) {
const data = Buffer.from(decodeURIComponent(base64Result), 'base64')
return JSON.parse(createAesDecrypt(data, this.aesMode, this.aesKey, this.aesIv).toString())
},
createSign(data, time) {
const str = `${this.appId}${data}${time}`
return toMD5(str).toUpperCase()
},
buildParam(jsonData) {
const data = Buffer.from(JSON.stringify(jsonData))
const time = Date.now()
const encodeData = createAesEncrypt(data, this.aesMode, this.aesKey, this.aesIv).toString(
'base64'
)
const sign = this.createSign(encodeData, time)
return `data=${encodeURIComponent(encodeData)}&time=${time}&appId=${this.appId}&sign=${sign}`
}
}

View File

@@ -0,0 +1,55 @@
import { createHttpFetch } from './utils'
import { filterMusicInfoList } from './musicInfo'
import { formatPlayCount } from '../../index'
export default {
/**
* 通过AlbumId获取专辑
* @param {*} id
* @param {*} page
*/
async getAlbumDetail(id, page = 1) {
const list = await createHttpFetch(
`http://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/queryAlbumSong?albumId=${id}&pageNo=${page}`
)
if (!list.songList) return Promise.reject(new Error('Get album list error.'))
const songList = filterMusicInfoList(list.songList)
const listInfo = await this.getAlbumInfo(id)
return {
list: songList || [],
page,
limit: listInfo.total,
total: listInfo.total,
source: 'mg',
info: {
name: listInfo.name,
img: listInfo.image,
desc: listInfo.desc,
author: listInfo.author,
play_count: listInfo.play_count
}
}
},
/**
* 通过AlbumId获取专辑信息
* @param {*} id
* @param {*} page
*/
async getAlbumInfo(id) {
const info = await createHttpFetch(
`https://app.c.nf.migu.cn/MIGUM3.0/resource/album/v2.0?albumId=${id}`
)
if (!info) return Promise.reject(new Error('Get album info error.'))
return {
name: info.title,
image: info.imgItems.length ? info.imgItems[0].img : null,
desc: info.summary,
author: info.singer,
play_count: formatPlayCount(info.opNumItem.playNum),
total: info.totalCount
}
}
}

View File

@@ -0,0 +1,28 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_test = {
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/url/mg/${songInfo.copyrightId}/${type}`, {
method: 'get',
timeout,
headers,
lookup: dnsLookup,
family: 4
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(requestMsg.fail))
}
})
return requestObj
}
}
export default api_test

View File

@@ -0,0 +1,110 @@
import { httpFetch } from '../../request'
import getSongId from './songId'
import { dateFormat2 } from '../index'
export default {
_requestObj: null,
_requestObj2: null,
_requestObj3: null,
async getComment(musicInfo, page = 1, limit = 10) {
if (this._requestObj) this._requestObj.cancelHttp()
if (!musicInfo.songId) {
let id = await getSongId(musicInfo)
if (!id) throw new Error('获取评论失败')
musicInfo.songId = id
}
const _requestObj = httpFetch(
`https://music.migu.cn/v3/api/comment/listComments?targetId=${musicInfo.songId}&pageSize=${limit}&pageNo=${page}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4195.1 Safari/537.36',
Referer: 'https://music.migu.cn'
}
}
)
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.returnCode !== '000000') throw new Error('获取评论失败')
return {
source: 'mg',
comments: this.filterComment(body.data.items),
total: body.data.itemTotal,
page,
limit,
maxPage: Math.ceil(body.data.itemTotal / limit) || 1
}
},
async getHotComment(musicInfo, page = 1, limit = 5) {
if (this._requestObj2) this._requestObj2.cancelHttp()
if (!musicInfo.songId) {
let id = await getSongId(musicInfo)
if (!id) throw new Error('获取评论失败')
musicInfo.songId = id
}
const _requestObj2 = httpFetch(
`https://music.migu.cn/v3/api/comment/listTopComments?targetId=${musicInfo.songId}&pageSize=${limit}&pageNo=${page}`,
{
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4195.1 Safari/537.36',
Referer: 'https://music.migu.cn'
}
}
)
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.returnCode !== '000000') throw new Error('获取热门评论失败')
return {
source: 'mg',
comments: this.filterComment(body.data.items),
total: body.data.itemTotal,
page,
limit,
maxPage: Math.ceil(body.data.itemTotal / limit) || 1
}
},
async getReplyComment(musicInfo, replyId, page = 1, limit = 10) {
if (this._requestObj2) this._requestObj2.cancelHttp()
const _requestObj2 = httpFetch(
`https://music.migu.cn/v3/api/comment/listCommentsById?commentId=${replyId}&pageSize=${limit}&pageNo=${page}`,
{
headers: {
'User-Agent': 'Android712-AndroidPhone-8983-18-0-COMMENT-wifi'
}
}
)
const { body, statusCode } = await _requestObj2.promise
// console.log(body)
if (statusCode != 200 || body.returnCode !== '000000') throw new Error('获取回复评论失败')
return { source: 'mg', comments: this.filterComment(body.data.items) }
},
filterComment(rawList) {
return rawList.map((item) => ({
id: item.commentId,
text: item.body,
time: item.createTime,
timeStr: dateFormat2(new Date(item.createTime).getTime()),
userName: item.author.name,
avatar: /^\/\//.test(item.author.avatar) ? `http:${item.author.avatar}` : item.author.avatar,
userId: item.author.id,
likedCount: item.praiseCount,
replyNum: item.replyTotal,
reply: item.replyCommentList.map((c) => ({
id: c.commentId,
text: c.body,
time: c.createTime,
timeStr: dateFormat2(new Date(c.createTime).getTime()),
userName: c.author.name,
avatar: /^\/\//.test(c.author.avatar) ? `http:${c.author.avatar}` : c.author.avatar,
userId: c.author.id,
likedCount: c.praiseCount,
replyNum: c.replyTotal
}))
}))
}
}

View File

@@ -0,0 +1,18 @@
import { httpFetch } from '../../request'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = httpFetch('http://jadeite.migu.cn:7090/music_search/v3/search/hotword')
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code !== '000000') throw new Error('获取热搜词失败')
// console.log(body, statusCode)
return { source: 'mg', list: this.filterList(body.data.hotwords[0].hotwordList) }
},
filterList(rawList) {
return rawList.filter((item) => item.resourceType == 'song').map((item) => item.word)
}
}

View File

@@ -0,0 +1,28 @@
import leaderboard from './leaderboard'
import songList from './songList'
import musicSearch from './musicSearch'
import pic from './pic'
import lyric from './lyric'
import hotSearch from './hotSearch'
import comment from './comment'
// import tipSearch from './tipSearch'
const mg = {
// tipSearch,
songList,
musicSearch,
leaderboard,
hotSearch,
comment,
getLyric(songInfo) {
return lyric.getLyric(songInfo)
},
getPic(songInfo) {
return pic.getPic(songInfo)
},
getMusicDetailPageUrl(songInfo) {
return `http://music.migu.cn/v3/music/song/${songInfo.copyrightId}`
}
}
export default mg

View File

@@ -0,0 +1,175 @@
import { httpFetch } from '../../request'
import { filterMusicInfoList } from './musicInfo'
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
const boardList = [
{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' },
{ id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' },
{ id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' },
{ id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' },
{ id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' },
{ id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' },
{ id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' },
{ id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' },
{ id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' },
{ id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' }
// { id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' },
// { id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' },
// { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' },
// { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' },
// { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' },
]
export default {
limit: 200,
list: [
{
id: 'mgyyb',
name: '音乐榜',
bangid: '27553319'
},
{
id: 'mgysb',
name: '影视榜',
bangid: '23603721'
},
{
id: 'mghybnd',
name: '华语内地榜',
bangid: '23603926'
},
{
id: 'mghyjqbgt',
name: '华语港台榜',
bangid: '23603954'
},
{
id: 'mgomb',
name: '欧美榜',
bangid: '23603974'
},
{
id: 'mgrhb',
name: '日韩榜',
bangid: '23603982'
},
{
id: 'mgwlb',
name: '网络榜',
bangid: '23604058'
},
{
id: 'mgclb',
name: '彩铃榜',
bangid: '23604023'
},
{
id: 'mgktvb',
name: 'KTV榜',
bangid: '23604040'
},
{
id: 'mgrcb',
name: '原创榜',
bangid: '23604032'
}
],
getUrl(id, page) {
return `https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/querycontentbyId.do?columnId=${id}&needAll=0`
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
},
successCode: '000000',
requestBoardsObj: null,
getBoardsData() {
if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp()
this.requestBoardsObj = httpFetch(
'https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release',
{
// this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/indexrank.do?templateVersion=8', {
headers: {
Referer: 'https://app.c.nf.migu.cn/',
'User-Agent':
'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36',
channel: '0146921'
}
}
)
return this.requestBoardsObj.promise
},
getData(url) {
const requestObj = httpFetch(url)
return requestObj.promise
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.template != 'group1') continue
for (const item of board.itemList) {
if (
(item.template != 'row1' && item.template != 'grid1' && !item.actionUrl) ||
!item.actionUrl.includes('rank-info')
)
continue
let data = item.displayLogId.param
list.push({
id: 'mg__' + data.rankId,
name: data.rankName,
bangid: String(data.rankId)
})
}
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body.data.contentItemList)
// if (response.statusCode !== 200 || response.body.code !== this.successCode) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.contentItemList)
// // console.log(list)
// // console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'mg',
// }
this.list = boardList
return {
list: boardList,
source: 'mg'
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(bangid, page)).then(({ statusCode, body }) => {
// console.log(body)
if (statusCode !== 200 || body.code !== this.successCode)
return this.getList(bangid, page, retryNum)
const list = filterMusicInfoList(body.columnInfo.contents.map((m) => m.objectInfo))
return {
total: list.length,
list,
limit: this.limit,
page,
source: 'mg'
}
})
},
getDetailPageUrl(id) {
if (typeof id == 'string') id = id.replace('mg__', '')
for (const item of boardList) {
if (item.bangid == id) {
return `https://music.migu.cn/v3/music/top/${item.webId}`
}
}
return null
}
}

View File

@@ -0,0 +1,151 @@
import { httpFetch } from '../../request'
import { getMusicInfo } from './musicInfo'
import { decrypt } from './utils/mrc'
const mrcTools = {
rxps: {
lineTime: /^\s*\[(\d+),\d+\]/,
wordTime: /\(\d+,\d+\)/,
wordTimeAll: /(\(\d+,\d+\))/g
},
parseLyric(str) {
str = str.replace(/\r/g, '')
const lines = str.split('\n')
const lxlrcLines = []
const lrcLines = []
for (const line of lines) {
if (line.length < 6) continue
let result = this.rxps.lineTime.exec(line)
if (!result) continue
const startTime = parseInt(result[1])
let time = startTime
let ms = time % 1000
time /= 1000
let m = parseInt(time / 60)
.toString()
.padStart(2, '0')
time %= 60
let s = parseInt(time).toString().padStart(2, '0')
time = `${m}:${s}.${ms}`
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
let times = words.match(this.rxps.wordTimeAll)
if (!times) continue
times = times.map((time) => {
const result = /\((\d+),(\d+)\)/.exec(time)
return `<${parseInt(result[1]) - startTime},${result[2]}>`
})
const wordArr = words.split(this.rxps.wordTime)
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
lxlrcLines.push(`[${time}]${newWords}`)
}
return {
lyric: lrcLines.join('\n'),
lxlyric: lxlrcLines.join('\n')
}
},
getText(url, tryNum = 0) {
const requestObj = httpFetch(url, {
headers: {
Referer: 'https://app.c.nf.migu.cn/',
'User-Agent':
'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36',
channel: '0146921'
}
})
return requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 200) return body
if (tryNum > 5 || statusCode == 404) return Promise.reject(new Error('歌词获取失败'))
return this.getText(url, ++tryNum)
})
},
getMrc(url) {
return this.getText(url).then((text) => {
return this.parseLyric(decrypt(text))
})
},
getLrc(url) {
return this.getText(url).then((text) => ({ lxlyric: '', lyric: text }))
},
getTrc(url) {
if (!url) return Promise.resolve('')
return this.getText(url)
},
async getMusicInfo(songInfo) {
return songInfo.mrcUrl == null ? getMusicInfo(songInfo.copyrightId) : songInfo
},
getLyric(songInfo) {
return {
promise: this.getMusicInfo(songInfo).then((info) => {
let p
if (info.mrcUrl) p = this.getMrc(info.mrcUrl)
else if (info.lrcUrl) p = this.getLrc(info.lrcUrl)
if (p == null) return Promise.reject(new Error('获取歌词失败'))
return Promise.all([p, this.getTrc(info.trcUrl)]).then(([lrcInfo, tlyric]) => {
lrcInfo.tlyric = tlyric
return lrcInfo
})
}),
cancelHttp() {}
}
}
}
export default {
getLyricWeb(songInfo, tryNum = 0) {
// console.log(songInfo.copyrightId)
if (songInfo.lrcUrl) {
let requestObj = httpFetch(songInfo.lrcUrl)
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
if (statusCode !== 200) {
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
return {
lyric: body,
tlyric: ''
}
})
return requestObj
} else {
let requestObj = httpFetch(
`https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`,
{
headers: {
Referer: 'https://music.migu.cn/v3/music/player/audio?from=migu'
}
}
)
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.returnCode !== '000000' || !body.lyric) {
if (tryNum > 5) return Promise.reject(new Error('Get lyric failed'))
let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
return {
lyric: body.lyric,
tlyric: ''
}
})
return requestObj
}
},
getLyric(songInfo) {
let requestObj = mrcTools.getLyric(songInfo)
requestObj.promise = requestObj.promise.catch(() => {
let webRequestObj = this.getLyricWeb(songInfo)
requestObj.cancelHttp = webRequestObj.cancelHttp.bind(webRequestObj)
return webRequestObj.promise
})
return requestObj
}
}

View File

@@ -0,0 +1,102 @@
import { sizeFormate } from '../index'
import { createHttpFetch } from './utils'
import { formatSingerName } from '../utils'
const createGetMusicInfosTask = (ids) => {
let list = ids
let tasks = []
while (list.length) {
tasks.push(list.slice(0, 100))
if (list.length < 100) break
list = list.slice(100)
}
let url = 'https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?resourceType=2'
return Promise.all(
tasks.map((task) =>
createHttpFetch(url, {
method: 'POST',
form: {
resourceId: task.join('|')
}
}).then((data) => data.resource)
)
)
}
export const filterMusicInfoList = (rawList) => {
// console.log(rawList)
let ids = new Set()
const list = []
rawList.forEach((item) => {
if (!item.songId || ids.has(item.songId)) return
ids.add(item.songId)
const types = []
const _types = {}
item.newRateFormats?.forEach((type) => {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
break
case 'HQ':
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
break
case 'SQ':
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size })
_types.flac = {
size
}
break
case 'ZQ':
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
break
}
})
const intervalTest = /(\d\d:\d\d)$/.test(item.length)
list.push({
singer: formatSingerName(item.artists, 'name'),
name: item.songName,
albumName: item.album,
albumId: item.albumId,
songmid: item.songId,
copyrightId: item.copyrightId,
source: 'mg',
interval: intervalTest ? RegExp.$1 : null,
img: item.albumImgs?.length ? item.albumImgs[0].img : null,
lrc: null,
lrcUrl: item.lrcUrl,
mrcUrl: item.mrcUrl,
trcUrl: item.trcUrl,
otherSource: null,
types,
_types,
typeUrl: {}
})
})
return list
}
export const getMusicInfo = async (copyrightId) => {
return getMusicInfos([copyrightId]).then((data) => data[0])
}
export const getMusicInfos = async (copyrightIds) => {
return filterMusicInfoList(
await Promise.all(createGetMusicInfosTask(copyrightIds)).then((data) => data.flat())
)
}

View File

@@ -0,0 +1,231 @@
import { httpFetch } from '../../request'
import { sizeFormate, formatPlayTime } from '../index'
import { toMD5, formatSingerName } from '../utils'
export const createSignature = (time, str) => {
const deviceId = '963B7AA0D21511ED807EE5846EC87D20'
const signatureMd5 = '6cdc72a439cef99a3418d2a78aa28c73'
const sign = toMD5(
`${str}${signatureMd5}yyapp2d16148780a1dcc7408e06336b98cfd50${deviceId}${time}`
)
return { sign, deviceId }
}
export default {
limit: 20,
total: 0,
page: 0,
allPage: 1,
// 旧版API
// musicSearch(str, page, limit) {
// const searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
// searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
// searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, {
// searchRequest = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do?isCopyright=1&isCorrect=1&pageNo=${page}&pageSize=${limit}&searchSwitch={%22song%22:1,%22album%22:0,%22singer%22:0,%22tagSong%22:0,%22mvSong%22:0,%22songlist%22:0,%22bestShow%22:0}&sort=0&text=${encodeURIComponent(str)}`)
// // searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, {
// headers: {
// // sign: 'c3b7ae985e2206e97f1b2de8f88691e2',
// // timestamp: 1578225871982,
// // appId: 'yyapp2',
// // mode: 'android',
// // ua: 'Android_migu',
// // version: '6.9.4',
// osVersion: 'android 7.0',
// 'User-Agent': 'okhttp/3.9.1',
// },
// })
// // searchRequest = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/search_all.do?isCopyright=1&isCorrect=1&pageNo=${page}&pageSize=${limit}&searchSwitch={%22song%22:1,%22album%22:0,%22singer%22:0,%22tagSong%22:0,%22mvSong%22:0,%22songlist%22:0,%22bestShow%22:0}&sort=0&text=${encodeURIComponent(str)}`)
// return searchRequest.promise.then(({ body }) => body)
// },
// handleResult(rawData) {
// // console.log(rawData)
// let ids = new Set()
// const list = []
// rawData.forEach(item => {
// if (ids.has(item.id)) return
// ids.add(item.id)
// const types = []
// const _types = {}
// item.newRateFormats && item.newRateFormats.forEach(type => {
// let size
// switch (type.formatType) {
// case 'PQ':
// size = sizeFormate(type.size ?? type.androidSize)
// types.push({ type: '128k', size })
// _types['128k'] = {
// size,
// }
// break
// case 'HQ':
// size = sizeFormate(type.size ?? type.androidSize)
// types.push({ type: '320k', size })
// _types['320k'] = {
// size,
// }
// break
// case 'SQ':
// size = sizeFormate(type.size ?? type.androidSize)
// types.push({ type: 'flac', size })
// _types.flac = {
// size,
// }
// break
// case 'ZQ':
// size = sizeFormate(type.size ?? type.androidSize)
// types.push({ type: 'flac24bit', size })
// _types.flac24bit = {
// size,
// }
// break
// }
// })
// const albumNInfo = item.albums && item.albums.length
// ? {
// id: item.albums[0].id,
// name: item.albums[0].name,
// }
// : {}
// list.push({
// singer: this.getSinger(item.singers),
// name: item.name,
// albumName: albumNInfo.name,
// albumId: albumNInfo.id,
// songmid: item.songId,
// copyrightId: item.copyrightId,
// source: 'mg',
// interval: null,
// img: item.imgItems && item.imgItems.length ? item.imgItems[0].img : null,
// lrc: null,
// lrcUrl: item.lyricUrl,
// mrcUrl: item.mrcurl,
// trcUrl: item.trcUrl,
// otherSource: null,
// types,
// _types,
// typeUrl: {},
// })
// })
// return list
// },
musicSearch(str, page, limit) {
const time = Date.now().toString()
const signData = createSignature(time, str)
const searchRequest = httpFetch(
`https://jadeite.migu.cn/music_search/v3/search/searchAll?isCorrect=0&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0&sid=USS`,
{
headers: {
uiVersion: 'A_music_3.6.1',
deviceId: signData.deviceId,
timestamp: time,
sign: signData.sign,
channel: '0146921',
'User-Agent':
'Mozilla/5.0 (Linux; U; Android 11.0.0; zh-cn; MI 11 Build/OPR1.170623.032) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
}
}
)
return searchRequest.promise.then(({ body }) => body)
},
filterData(rawData) {
// console.log(rawData)
const list = []
const ids = new Set()
rawData.forEach((item) => {
item.forEach((data) => {
if (!data.songId || !data.copyrightId || ids.has(data.copyrightId)) return
ids.add(data.copyrightId)
const types = []
const _types = {}
data.audioFormats &&
data.audioFormats.forEach((type) => {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.asize ?? type.isize)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
break
case 'HQ':
size = sizeFormate(type.asize ?? type.isize)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
break
case 'SQ':
size = sizeFormate(type.asize ?? type.isize)
types.push({ type: 'flac', size })
_types.flac = {
size
}
break
case 'ZQ24':
size = sizeFormate(type.asize ?? type.isize)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
break
}
})
let img = data.img3 || data.img2 || data.img1 || null
if (img && !/https?:/.test(data.img3)) img = 'http://d.musicapp.migu.cn' + img
list.push({
singer: formatSingerName(data.singerList),
name: data.name,
albumName: data.album,
albumId: data.albumId,
songmid: data.songId,
copyrightId: data.copyrightId,
source: 'mg',
interval: formatPlayTime(data.duration),
img,
lrc: null,
lrcUrl: data.lrcUrl,
mrcUrl: data.mrcurl,
trcUrl: data.trcUrl,
types,
_types,
typeUrl: {}
})
})
})
return list
},
search(str, page = 1, limit, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then((result) => {
// console.log(result)
if (!result || result.code !== '000000')
return Promise.reject(new Error(result ? result.info : '搜索失败'))
const songResultData = result.songResultData || { resultList: [], totalCount: 0 }
let list = this.filterData(songResultData.resultList)
if (list == null) return this.search(str, page, limit, retryNum)
this.total = parseInt(songResultData.totalCount)
this.page = page
this.allPage = Math.ceil(this.total / limit)
return {
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'mg'
}
})
}
}

View File

@@ -0,0 +1,31 @@
import { httpFetch } from '../../request'
import getSongId from './songId'
export default {
async getPicUrl(songId, tryNum = 0) {
let requestObj = httpFetch(
`http://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${songId}`,
{
headers: {
Referer: 'http://music.migu.cn/v3/music/player/audio?from=migu'
}
}
)
requestObj.promise.then(({ body }) => {
if (body.returnCode !== '000000') {
if (tryNum > 5) return Promise.reject(new Error('图片获取失败'))
let tryRequestObj = this.getPic(songId, ++tryNum)
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
return tryRequestObj.promise
}
let url = body.largePic || body.mediumPic || body.smallPic
if (!/https?:/.test(url)) url = 'http:' + url
return url
})
return requestObj
},
async getPic(songInfo) {
const songId = await getSongId(songInfo)
return this.getPicUrl(songId)
}
}

View File

@@ -0,0 +1,30 @@
// import { httpFetch } from '../../request'
import { getMusicInfo } from './musicInfo'
const getSongId = async (mInfo) => {
if (mInfo.songmid != mInfo.copyrightId) return mInfo.songmid
const musicInfo = await getMusicInfo(mInfo.copyrightId)
return musicInfo.songmid
}
// export const getSongId = async(musicInfo, retry = 0) => {
// if (musicInfo.songmid != musicInfo.copyrightId) return musicInfo.songmid
// if (++retry > 2) return Promise.reject(new Error('max retry'))
// const requestObj = httpFetch(`https://app.c.nf.migu.cn/MIGUM2.0/v2.0/content/listen-url?netType=00&resourceType=2&songId=${musicInfo.copyrightId}&toneFlag=PQ`, {
// headers: {
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
// channel: '0146921',
// },
// })
// return requestObj.promise.then(({ body }) => {
// console.log(body)
// if (!body || body.code !== '000000') return this.getSongId(musicInfo, retry)
// const id = body.data.songItem.songId
// if (!id) throw new Error('failed')
// return id
// })
// }
export default getSongId

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,196 @@
import { httpFetch } from '../../../request'
import { formatPlayTime } from '../../../index'
// import { sizeFormate } from '../../index'
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
const boardList = [
{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' },
{ id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' },
{ id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' },
{ id: 'mg__migumusic', name: '音乐榜', bangid: 'migumusic', webId: 'migumusic' },
{ id: 'mg__movies', name: '影视榜', bangid: 'movies', webId: 'movies' },
{ id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' },
{ id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' },
{ id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' },
{ id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' },
{ id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' },
{ id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' },
{ id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' },
{ id: 'mg__23217754', name: 'MV榜', bangid: '23217754', webId: 'mv' },
{ id: 'mg__23218151', name: '新专辑榜', bangid: '23218151', webId: 'newalbum' },
{ id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' },
{ id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' },
{ id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' },
{ id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' },
{ id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' },
{ id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' }
]
// const boardList = [
// { id: 'mg__jianjiao_newsong', bangid: 'jianjiao_newsong', name: '尖叫新歌榜' },
// { id: 'mg__jianjiao_hotsong', bangid: 'jianjiao_hotsong', name: '尖叫热歌榜' },
// { id: 'mg__jianjiao_original', bangid: 'jianjiao_original', name: '尖叫原创榜' },
// { id: 'mg__migumusic', bangid: 'migumusic', name: '音乐榜' },
// { id: 'mg__movies', bangid: 'movies', name: '影视榜' },
// { id: 'mg__mainland', bangid: 'mainland', name: '内地榜' },
// { id: 'mg__hktw', bangid: 'hktw', name: '港台榜' },
// { id: 'mg__eur_usa', bangid: 'eur_usa', name: '欧美榜' },
// { id: 'mg__jpn_kor', bangid: 'jpn_kor', name: '日韩榜' },
// { id: 'mg__coloring', bangid: 'coloring', name: '彩铃榜' },
// { id: 'mg__ktv', bangid: 'ktv', name: 'KTV榜' },
// { id: 'mg__network', bangid: 'network', name: '网络榜' },
// { id: 'mg__newalbum', bangid: 'newalbum', name: '新专辑榜' },
// { id: 'mg__mv', bangid: 'mv', name: 'MV榜' },
// { id: 'mg__itunes', bangid: 'itunes', name: '美国iTunes榜' },
// { id: 'mg__billboard', bangid: 'billboard', name: '美国billboard榜' },
// { id: 'mg__hito', bangid: 'hito', name: 'Hito中文榜' },
// { id: 'mg__mnet', bangid: 'mnet', name: '韩国Melon榜' },
// { id: 'mg__uk', bangid: 'uk', name: '英国UK榜' },
// ]
export default {
limit: 10000,
getUrl(id, page) {
const targetBoard = boardList.find((board) => board.bangid == id)
return `https://music.migu.cn/v3/music/top/${targetBoard.webId}`
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
},
successCode: '000000',
requestBoardsObj: null,
regExps: {
listData: /var listData = (\{.+\})<\/script>/
},
getData(url) {
const requestObj = httpFetch(url)
return requestObj.promise
},
getSinger(singers) {
let arr = []
singers.forEach((singer) => {
arr.push(singer.name)
})
return arr.join('、')
},
getIntv(interval) {
if (!interval) return 0
let intvArr = interval.split(':')
let intv = 0
let unit = 1
while (intvArr.length) {
intv += intvArr.pop() * unit
unit *= 60
}
return parseInt(intv)
},
formateIntv() {},
filterData(rawData) {
// console.log(JSON.stringify(rawData))
// console.log(rawData)
let ids = new Set()
const list = []
rawData.forEach((item) => {
if (ids.has(item.copyrightId)) return
ids.add(item.copyrightId)
const types = []
const _types = {}
const size = null
types.push({ type: '128k', size })
_types['128k'] = { size }
if (item.hq) {
const size = null
types.push({ type: '320k', size })
_types['320k'] = { size }
}
if (item.sq) {
const size = null
types.push({ type: 'flac', size })
_types.flac = { size }
}
list.push({
singer: this.getSinger(item.singers),
name: item.name,
albumName: item.album && item.album.albumName,
albumId: item.album && item.album.albumId,
songmid: item.id,
copyrightId: item.copyrightId,
source: 'mg',
interval: item.duration ? formatPlayTime(this.getIntv(item.duration)) : null,
img: item.mediumPic ? `https:${item.mediumPic}` : null,
lrc: null,
// lrcUrl: item.lrcUrl,
otherSource: null,
types,
_types,
typeUrl: {}
})
})
return list
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.template != 'group1') continue
for (const item of board.itemList) {
if (
(item.template != 'row1' && item.template != 'grid1' && !item.actionUrl) ||
!item.actionUrl.includes('rank-info')
)
continue
let data = item.displayLogId.param
list.push({
id: 'mg__' + data.rankId,
name: data.rankName,
bangid: String(data.rankId)
})
}
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body.data.contentItemList)
// if (response.statusCode !== 200 || response.body.code !== this.successCode) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.contentItemList)
// // console.log(list)
// // console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'mg',
// }
this.list = boardList
return {
list: boardList,
source: 'mg'
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(bangid, page)).then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getList(bangid, page, retryNum)
let listData = body.match(this.regExps.listData)
if (!listData) return this.getList(bangid, page, retryNum)
const datas = JSON.parse(RegExp.$1)
// console.log(datas)
listData = this.filterData(datas.songs.items)
return {
total: datas.songs.itemTotal,
list: this.filterData(datas.songs.items),
limit: this.limit,
page,
source: 'mg'
}
})
}
}

View File

@@ -0,0 +1,28 @@
import { createHttpFetch } from './utils'
export default {
requestObj: null,
cancelTipSearch() {
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
},
tipSearchBySong(str) {
this.cancelTipSearch()
this.requestObj = createHttpFetch(
`https://music.migu.cn/v3/api/search/suggest?keyword=${encodeURIComponent(str)}`,
{
headers: {
referer: 'https://music.migu.cn/v3'
}
}
)
return this.requestObj.then((body) => {
return body.songs
})
},
handleResult(rawData) {
return rawData.map((info) => `${info.name} - ${info.singerName}`)
},
async search(str) {
return this.tipSearchBySong(str).then((result) => this.handleResult(result))
}
}

View File

@@ -0,0 +1,29 @@
import { httpFetch } from '../../../request'
/**
* 创建一个适用于MG的Http请求
* @param {*} url
* @param {*} options
* @param {*} retryNum
*/
export const createHttpFetch = async (url, options, retryNum = 0) => {
if (retryNum > 2) throw new Error('try max num')
let result
try {
result = await httpFetch(url, options).promise
} catch (err) {
console.log(err)
return createHttpFetch(url, options, ++retryNum)
}
if (
result.statusCode !== 200 ||
(result.body.code !== undefined
? result.body.code
: result.body.returnCode !== undefined
? result.body.returnCode
: result.body.code) !== '000000'
)
return createHttpFetch(url, options, ++retryNum)
if (result.body.data) return result.body.data
return result.body
}

View File

@@ -0,0 +1,116 @@
// const key = 'karakal@123Qcomyidongtiantianhaoting'
const DELTA = 2654435769n
const MIN_LENGTH = 32
// const SPECIAL_CHAR = '0'
const keyArr = [
27303562373562475n,
18014862372307051n,
22799692160172081n,
34058940340699235n,
30962724186095721n,
27303523720101991n,
27303523720101998n,
31244139033526382n,
28992395054481524n
]
const teaDecrypt = (data, key) => {
const length = data.length
const lengthBitint = BigInt(length)
if (length >= 1) {
// let j = data[data.length - 1];
let j2 = data[0]
let j3 = toLong((6n + 52n / lengthBitint) * DELTA)
while (true) {
let j4 = j3
if (j4 == 0n) break
let j5 = toLong(3n & toLong(j4 >> 2n))
let j6 = lengthBitint
while (true) {
j6--
if (j6 > 0n) {
let j7 = data[j6 - 1n]
let i = j6
j2 = toLong(
data[i] -
(toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^
toLong(
toLong(toLong(j7 >> 5n) ^ toLong(j2 << 2n)) +
toLong(toLong(j2 >> 3n) ^ toLong(j7 << 4n))
))
)
data[i] = j2
} else break
}
let j8 = data[lengthBitint - 1n]
j2 = toLong(
data[0n] -
toLong(
toLong(toLong(key[toLong(toLong(j6 & 3n) ^ j5)] ^ j8) + toLong(j2 ^ j4)) ^
toLong(
toLong(toLong(j8 >> 5n) ^ toLong(j2 << 2n)) +
toLong(toLong(j2 >> 3n) ^ toLong(j8 << 4n))
)
)
)
data[0] = j2
j3 = toLong(j4 - DELTA)
}
}
return data
}
const longArrToString = (data) => {
const arrayList = []
for (const j of data) arrayList.push(longToBytes(j).toString('utf16le'))
return arrayList.join('')
}
// https://stackoverflow.com/a/29132118
const longToBytes = (l) => {
const result = Buffer.alloc(8)
for (let i = 0; i < 8; i++) {
result[i] = parseInt(l & 0xffn)
l >>= 8n
}
return result
}
const toBigintArray = (data) => {
const length = Math.floor(data.length / 16)
const jArr = Array(length)
for (let i = 0; i < length; i++) {
jArr[i] = toLong(data.substring(i * 16, i * 16 + 16))
}
return jArr
}
// https://github.com/lyswhut/lx-music-desktop/issues/445#issuecomment-1139338682
const MAX = 9223372036854775807n
const MIN = -9223372036854775808n
const toLong = (str) => {
const num = typeof str == 'string' ? BigInt('0x' + str) : str
if (num > MAX) return toLong(num - (1n << 64n))
else if (num < MIN) return toLong(num + (1n << 64n))
return num
}
export const decrypt = (data) => {
// console.log(data.length)
// -3551594764563790630
// console.log(toLongArrayFromArr(Buffer.from(key)))
// console.log(teaDecrypt(toBigintArray(data), keyArr))
// console.log(longArrToString(teaDecrypt(toBigintArray(data), keyArr)))
// console.log(toByteArray(teaDecrypt(toBigintArray(data), keyArr)))
return data == null || data.length < MIN_LENGTH
? data
: longArrToString(teaDecrypt(toBigintArray(data), keyArr))
}
// console.log(14895149309145760986n - )
// console.log(toLong('14895149309145760986'))
// console.log(decrypt(str))
// console.log(decrypt(str))
// console.log(toByteArray([6048138644744000495n]))
// console.log(toByteArray([16325999628386395n]))
// console.log(toLong(90994076459972177136n))

View File

@@ -0,0 +1,16 @@
export const bHh = '624868746c'
export const headers = {
'User-Agent': 'lx-music request',
[bHh]: [bHh]
}
export const timeout = 15000
// 添加 getOptions 函数
export const getOptions = () => {
return {
headers,
timeout
}
}

View File

@@ -0,0 +1,33 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_messoer = {
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/url/tx/${songInfo.songmid}/${type}`, {
method: 'get',
timeout,
headers,
lookup: dnsLookup,
family: 4
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(requestMsg.fail))
}
})
return requestObj
},
getPic(songInfo) {
return Promise.resolve(
`https://y.gtimg.cn/music/photo_new/T002R500x500M000${songInfo.albumId}.jpg`
)
}
}
export default api_messoer

View File

@@ -0,0 +1,289 @@
import { httpFetch } from '../../request'
import { dateFormat2 } from '../index'
import getMusicInfo from './musicInfo'
const emojis = {
e400846: '😘',
e400874: '😴',
e400825: '😃',
e400847: '😙',
e400835: '😍',
e400873: '😳',
e400836: '😎',
e400867: '😭',
e400832: '😊',
e400837: '😏',
e400875: '😫',
e400831: '😉',
e400855: '😡',
e400823: '😄',
e400862: '😨',
e400844: '😖',
e400841: '😓',
e400830: '😈',
e400828: '😆',
e400833: '😋',
e400822: '😀',
e400843: '😕',
e400829: '😇',
e400824: '😂',
e400834: '😌',
e400877: '😷',
e400132: '🍉',
e400181: '🍺',
e401067: '☕️',
e400186: '🥧',
e400343: '🐷',
e400116: '🌹',
e400126: '🍃',
e400613: '💋',
e401236: '❤️',
e400622: '💔',
e400637: '💣',
e400643: '💩',
e400773: '🔪',
e400102: '🌛',
e401328: '🌞',
e400420: '👏',
e400914: '🙌',
e400408: '👍',
e400414: '👎',
e401121: '✋',
e400396: '👋',
e400384: '👉',
e401115: '✊',
e400402: '👌',
e400905: '🙈',
e400906: '🙉',
e400907: '🙊',
e400562: '👻',
e400932: '🙏',
e400644: '💪',
e400611: '💉',
e400185: '🎁',
e400655: '💰',
e400325: '🐥',
e400612: '💊',
e400198: '🎉',
e401685: '⚡️',
e400631: '💝',
e400768: '🔥',
e400432: '👑'
}
const songIdMap = new Map()
const promises = new Map()
export default {
_requestObj: null,
_requestObj2: null,
async getSongId({ songId, songmid }) {
if (songId) return songId
if (songIdMap.has(songmid)) return songIdMap.get(songmid)
if (promises.has(songmid)) return (await promises.get(songmid)).songId
const promise = getMusicInfo(songmid)
promises.set(promise)
const info = await promise
songIdMap.set(songmid, info.songId)
promises.delete(songmid)
return info.songId
},
async getComment(mInfo, page = 1, limit = 20) {
if (this._requestObj) this._requestObj.cancelHttp()
const songId = await this.getSongId(mInfo)
const _requestObj = httpFetch('http://c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg', {
method: 'POST',
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'
},
form: {
uin: '0',
format: 'json',
cid: '205360772',
reqtype: '2',
biztype: '1',
topid: songId,
cmd: '8',
needmusiccrit: '1',
pagenum: page - 1,
pagesize: limit
}
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code !== 0) throw new Error('获取评论失败')
// console.log(body, statusCode)
const comment = body.comment
return {
source: 'tx',
comments: this.filterNewComment(comment.commentlist),
total: comment.commenttotal,
page,
limit,
maxPage: Math.ceil(comment.commenttotal / limit) || 1
}
},
async getHotComment(mInfo, page = 1, limit = 20) {
// const _requestObj2 = httpFetch('http://c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg', {
// method: 'POST',
// headers: {
// 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)',
// },
// form: {
// uin: '0',
// format: 'json',
// cid: '205360772',
// reqtype: '2',
// biztype: '1',
// topid: songId,
// cmd: '9',
// needmusiccrit: '1',
// pagenum: page - 1,
// pagesize: limit,
// },
// })
if (this._requestObj2) this._requestObj2.cancelHttp()
const songId = await this.getSongId(mInfo)
const _requestObj2 = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'POST',
body: {
comm: {
cv: 4747474,
ct: 24,
format: 'json',
inCharset: 'utf-8',
outCharset: 'utf-8',
notice: 0,
platform: 'yqq.json',
needNewCode: 1,
uin: 0
},
req: {
module: 'music.globalComment.CommentRead',
method: 'GetHotCommentList',
param: {
BizType: 1,
BizId: String(songId),
LastCommentSeqNo: '',
PageSize: limit,
PageNum: page - 1,
HotType: 1,
WithAirborne: 0,
PicEnable: 1
}
}
},
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.0.0',
referer: 'https://y.qq.com/',
origin: 'https://y.qq.com'
}
})
const { body, statusCode } = await _requestObj2.promise
// console.log('body', body)
if (statusCode != 200 || body.code !== 0 || body.req.code !== 0)
throw new Error('获取热门评论失败')
const comment = body.req.data.CommentList
return {
source: 'tx',
comments: this.filterHotComment(comment.Comments),
total: comment.Total,
page,
limit,
maxPage: Math.ceil(comment.Total / limit) || 1
}
},
filterNewComment(rawList) {
return rawList.map((item) => {
let time = this.formatTime(item.time)
let timeStr = time ? dateFormat2(time) : null
if (item.middlecommentcontent) {
let firstItem = item.middlecommentcontent[0]
firstItem.avatarurl = item.avatarurl
firstItem.praisenum = item.praisenum
item.avatarurl = null
item.praisenum = null
item.middlecommentcontent.reverse()
}
return {
id: `${item.rootcommentid}_${item.commentid}`,
rootId: item.rootcommentid,
text: item.rootcommentcontent
? this.replaceEmoji(item.rootcommentcontent).replace(/\\n/g, '\n')
: '',
time: item.rootcommentid == item.commentid ? time : null,
timeStr: item.rootcommentid == item.commentid ? timeStr : null,
userName: item.rootcommentnick ? item.rootcommentnick.substring(1) : '',
avatar: item.avatarurl,
userId: item.encrypt_rootcommentuin,
likedCount: item.praisenum,
reply: item.middlecommentcontent
? item.middlecommentcontent.map((c) => {
// let index = c.subcommentid.lastIndexOf('_')
return {
id: `sub_${item.rootcommentid}_${c.subcommentid}`,
text: this.replaceEmoji(c.subcommentcontent).replace(/\\n/g, '\n'),
time: c.subcommentid == item.commentid ? time : null,
timeStr: c.subcommentid == item.commentid ? timeStr : null,
userName: c.replynick.substring(1),
avatar: c.avatarurl,
userId: c.encrypt_replyuin,
likedCount: c.praisenum
}
})
: []
}
})
},
filterHotComment(rawList) {
return rawList.map((item) => {
return {
id: `${item.SeqNo}_${item.CmId}`,
rootId: item.SeqNo,
text: item.Content ? this.replaceEmoji(item.Content).replace(/\\n/g, '\n') : '',
time: item.PubTime ? this.formatTime(item.PubTime) : null,
timeStr: item.PubTime ? dateFormat2(this.formatTime(item.PubTime)) : null,
userName: item.Nick ?? '',
images: item.Pic ? [item.Pic] : [],
avatar: item.Avatar,
location: item.Location ? item.Location : '',
userId: item.EncryptUin,
likedCount: item.PraiseNum,
reply: item.SubComments
? item.SubComments.map((c) => {
return {
id: `sub_${c.SeqNo}_${c.CmId}`,
text: this.replaceEmoji(c.Content).replace(/\\n/g, '\n'),
time: c.PubTime ? this.formatTime(c.PubTime) : null,
timeStr: c.PubTime ? dateFormat2(this.formatTime(c.PubTime)) : null,
userName: c.Nick ?? '',
avatar: c.Avatar,
images: c.Pic ? [c.Pic] : [],
userId: c.EncryptUin,
likedCount: c.PraiseNum
}
})
: []
}
})
},
replaceEmoji(msg) {
let rxp = /^\[em\](e\d+)\[\/em\]$/
let result = msg.match(/\[em\]e\d+\[\/em\]/g)
if (!result) return msg
result = Array.from(new Set(result))
for (let item of result) {
let code = item.replace(rxp, '$1')
msg = msg.replace(
new RegExp(item.replace('[em]', '\\[em\\]').replace('[/em]', '\\[\\/em\\]'), 'g'),
emojis[code] || ''
)
}
return msg
},
formatTime(time) {
return String(time).length < 10 ? null : parseInt(time + '000')
}
}

View File

@@ -0,0 +1,54 @@
import { httpFetch } from '../../request'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
// const _requestObj = httpFetch('https://c.y.qq.com/splcloud/fcgi-bin/gethotkey.fcg', {
// method: 'get',
// headers: {
// Referer: 'https://y.qq.com/portal/player.html',
// },
// })
const _requestObj = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
body: {
comm: {
ct: '19',
cv: '1803',
guid: '0',
patch: '118',
psrf_access_token_expiresAt: 0,
psrf_qqaccess_token: '',
psrf_qqopenid: '',
psrf_qqunionid: '',
tmeAppID: 'qqmusic',
tmeLoginType: 0,
uin: '0',
wid: '0'
},
hotkey: {
method: 'GetHotkeyForQQMusicPC',
module: 'tencent_musicsoso_hotkey.HotkeyService',
param: {
search_id: '',
uin: 0
}
}
},
headers: {
Referer: 'https://y.qq.com/portal/player.html'
}
})
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.code !== 0) throw new Error('获取热搜词失败')
// console.log(body)
return { source: 'tx', list: this.filterList(body.hotkey.data.vec_hotkey) }
},
filterList(rawList) {
return rawList.map((item) => item.query)
}
}

View File

@@ -0,0 +1,25 @@
import leaderboard from './leaderboard'
import lyric from './lyric'
import songList from './songList'
import musicSearch from './musicSearch'
import hotSearch from './hotSearch'
import comment from './comment'
// import tipSearch from './tipSearch'
const tx = {
// tipSearch,
leaderboard,
songList,
musicSearch,
hotSearch,
comment,
getLyric(songInfo) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo)
},
getMusicDetailPageUrl(songInfo) {
return `https://y.qq.com/n/yqq/song/${songInfo.songmid}.html`
}
}
export default tx

View File

@@ -0,0 +1,279 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../index'
import { formatSingerName } from '../utils'
let boardList = [
{ id: 'tx__4', name: '流行指数榜', bangid: '4' },
{ id: 'tx__26', name: '热歌榜', bangid: '26' },
{ id: 'tx__27', name: '新歌榜', bangid: '27' },
{ id: 'tx__62', name: '飙升榜', bangid: '62' },
{ id: 'tx__58', name: '说唱榜', bangid: '58' },
{ id: 'tx__57', name: '喜力电音榜', bangid: '57' },
{ id: 'tx__28', name: '网络歌曲榜', bangid: '28' },
{ id: 'tx__5', name: '内地榜', bangid: '5' },
{ id: 'tx__3', name: '欧美榜', bangid: '3' },
{ id: 'tx__59', name: '香港地区榜', bangid: '59' },
{ id: 'tx__16', name: '韩国榜', bangid: '16' },
{ id: 'tx__60', name: '抖快榜', bangid: '60' },
{ id: 'tx__29', name: '影视金曲榜', bangid: '29' },
{ id: 'tx__17', name: '日本榜', bangid: '17' },
{ id: 'tx__52', name: '腾讯音乐人原创榜', bangid: '52' },
{ id: 'tx__36', name: 'K歌金曲榜', bangid: '36' },
{ id: 'tx__61', name: '台湾地区榜', bangid: '61' },
{ id: 'tx__63', name: 'DJ舞曲榜', bangid: '63' },
{ id: 'tx__64', name: '综艺新歌榜', bangid: '64' },
{ id: 'tx__65', name: '国风热歌榜', bangid: '65' },
{ id: 'tx__67', name: '听歌识曲榜', bangid: '67' },
{ id: 'tx__72', name: '动漫音乐榜', bangid: '72' },
{ id: 'tx__73', name: '游戏音乐榜', bangid: '73' },
{ id: 'tx__75', name: '有声榜', bangid: '75' },
{ id: 'tx__131', name: '校园音乐人排行榜', bangid: '131' }
]
export default {
limit: 300,
list: [
{
id: 'txlxzsb',
name: '流行榜',
bangid: 4
},
{
id: 'txrgb',
name: '热歌榜',
bangid: 26
},
{
id: 'txwlhgb',
name: '网络榜',
bangid: 28
},
{
id: 'txdyb',
name: '抖音榜',
bangid: 60
},
{
id: 'txndb',
name: '内地榜',
bangid: 5
},
{
id: 'txxgb',
name: '香港榜',
bangid: 59
},
{
id: 'txtwb',
name: '台湾榜',
bangid: 61
},
{
id: 'txoumb',
name: '欧美榜',
bangid: 3
},
{
id: 'txhgb',
name: '韩国榜',
bangid: 16
},
{
id: 'txrbb',
name: '日本榜',
bangid: 17
},
{
id: 'txtybb',
name: 'YouTube榜',
bangid: 128
}
],
listDetailRequest(id, period, limit) {
// console.log(id, period, limit)
return httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'
},
body: {
toplist: {
module: 'musicToplist.ToplistInfoServer',
method: 'GetDetail',
param: {
topid: id,
num: limit,
period
}
},
comm: {
uin: 0,
format: 'json',
ct: 20,
cv: 1859
}
}
}).promise
},
regExps: {
periodList:
/<i class="play_cover__btn c_tx_link js_icon_play" data-listkey=".+?" data-listname=".+?" data-tid=".+?" data-date=".+?" .+?<\/i>/g,
period: /data-listname="(.+?)" data-tid=".*?\/(.+?)" data-date="(.+?)" .+?<\/i>/
},
periods: {},
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
_requestBoardsObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch(
'https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5'
)
return this._requestBoardsObj.promise
},
getData(url) {
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
filterData(rawList) {
// console.log(rawList)
return rawList.map((item) => {
let types = []
let _types = {}
if (item.file.size_128mp3 !== 0) {
let size = sizeFormate(item.file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
}
if (item.file.size_320mp3 !== 0) {
let size = sizeFormate(item.file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
}
if (item.file.size_flac !== 0) {
let size = sizeFormate(item.file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size
}
}
if (item.file.size_hires !== 0) {
let size = sizeFormate(item.file.size_hires)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
}
// types.reverse()
return {
singer: formatSingerName(item.singer, 'name'),
name: item.title,
albumName: item.album.name,
albumId: item.album.mid,
source: 'tx',
interval: formatPlayTime(item.interval),
songId: item.id,
albumMid: item.album.mid,
strMediaMid: item.file.media_mid,
songmid: item.mid,
img:
item.album.name === '' || item.album.name === '空'
? item.singer?.length
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: ''
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${item.album.mid}.jpg`,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
getPeriods(bangid) {
return this.getData(this.periodUrl).then(({ body: html }) => {
let result = html.match(this.regExps.periodList)
if (!result) return Promise.reject(new Error('get data failed'))
result.forEach((item) => {
let result = item.match(this.regExps.period)
if (!result) return
this.periods[result[2]] = {
name: result[1],
bangid: result[2],
period: result[3]
}
})
const info = this.periods[bangid]
return info && info.period
})
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
// 排除 MV榜
if (board.id == 201) continue
if (board.topTitle.startsWith('巅峰榜·')) {
board.topTitle = board.topTitle.substring(4, board.topTitle.length)
}
if (!board.topTitle.endsWith('榜')) board.topTitle += '榜'
list.push({
id: 'tx__' + board.id,
name: board.topTitle,
bangid: String(board.id)
})
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body)
// if (response.statusCode !== 200 || response.body.code !== 0) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.topList)
// console.log(list)
// console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'tx',
// }
this.list = boardList
return {
list: boardList,
source: 'tx'
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
bangid = parseInt(bangid)
let info = this.periods[bangid]
let p = info ? Promise.resolve(info.period) : this.getPeriods(bangid)
return p.then((period) => {
return this.listDetailRequest(bangid, period, this.limit).then((resp) => {
if (resp.body.code !== 0) return this.getList(bangid, page, retryNum)
return {
total: resp.body.toplist.data.songInfoList.length,
list: this.filterData(resp.body.toplist.data.songInfoList),
limit: this.limit,
page: 1,
source: 'tx'
}
})
})
},
getDetailPageUrl(id) {
if (typeof id == 'string') id = id.replace('tx__', '')
return `https://y.qq.com/n/ryqq/toplist/${id}`
}
}

View File

@@ -0,0 +1,70 @@
import { httpFetch } from '../../request'
import getMusicInfo from './musicInfo'
const songIdMap = new Map()
const promises = new Map()
export default {
successCode: 0,
async getSongId({ songId, songmid }) {
if (songId) return songId
if (songIdMap.has(songmid)) return songIdMap.get(songmid)
if (promises.has(songmid)) return (await promises.get(songmid)).songId
const promise = getMusicInfo(songmid)
promises.set(promise)
const info = await promise
songIdMap.set(songmid, info.songId)
promises.delete(songmid)
return info.songId
},
getLyric(mInfo, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('Get lyric failed'))
return {
promise: this.getSongId(mInfo).then((songId) => {
const requestObj = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
referer: 'https://y.qq.com',
'user-agent':
'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
},
body: {
comm: {
ct: '19',
cv: '1859',
uin: '0'
},
req: {
method: 'GetPlayLyricInfo',
module: 'music.musichallSong.PlayLyricInfo',
param: {
format: 'json',
crypt: 1,
ct: 19,
cv: 1873,
interval: 0,
lrc_t: 0,
qrc: 1,
qrc_t: 0,
roma: 1,
roma_t: 0,
songID: songId,
trans: 1,
trans_t: 0,
type: -1
}
}
}
})
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code != this.successCode || body.req.code != this.successCode)
return this.getLyric(songId, ++retryNum)
const data = body.req.data
return this.parseLyric(data.lyric, data.trans, data.roma)
})
})
}
}
}

View File

@@ -0,0 +1,100 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../index'
const getSinger = (singers) => {
let arr = []
singers.forEach((singer) => {
arr.push(singer.name)
})
return arr.join('、')
}
export default (songmid) => {
const requestObj = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'
},
body: {
comm: {
ct: '19',
cv: '1859',
uin: '0'
},
req: {
module: 'music.pf_song_detail_svr',
method: 'get_song_detail_yqq',
param: {
song_type: 0,
song_mid: songmid
}
}
}
})
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code != 0 || body.req.code != 0) return Promise.reject(new Error('获取歌曲信息失败'))
const item = body.req.data.track_info
if (!item.file?.media_mid) return null
let types = []
let _types = {}
const file = item.file
if (file.size_128mp3 != 0) {
let size = sizeFormate(file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
}
if (file.size_320mp3 !== 0) {
let size = sizeFormate(file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
}
if (file.size_flac !== 0) {
let size = sizeFormate(file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size
}
}
if (file.size_hires !== 0) {
let size = sizeFormate(file.size_hires)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
}
// types.reverse()
let albumId = ''
let albumName = ''
if (item.album) {
albumName = item.album.name
albumId = item.album.mid
}
return {
singer: getSinger(item.singer),
name: item.title,
albumName,
albumId,
source: 'tx',
interval: formatPlayTime(item.interval),
songId: item.id,
albumMid: item.album?.mid ?? '',
strMediaMid: item.file.media_mid,
songmid: item.mid,
img:
albumId === '' || albumId === '空'
? item.singer?.length
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: ''
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${albumId}.jpg`,
types,
_types,
typeUrl: {}
}
})
}

View File

@@ -0,0 +1,141 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../index'
import { formatSingerName } from '../utils'
export default {
limit: 50,
total: 0,
page: 0,
allPage: 1,
successCode: 0,
musicSearch(str, page, limit, retryNum = 0) {
if (retryNum > 5) return Promise.reject(new Error('搜索失败'))
// searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`)
// const searchRequest = httpFetch(`https://shc.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`)
const searchRequest = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'
},
body: {
comm: {
ct: 11,
cv: '1003006',
v: '1003006',
os_ver: '12',
phonetype: '0',
devicelevel: '31',
tmeAppID: 'qqmusiclight',
nettype: 'NETWORK_WIFI'
},
req: {
module: 'music.search.SearchCgiService',
method: 'DoSearchForQQMusicLite',
param: {
query: str,
search_type: 0,
num_per_page: limit,
page_num: page,
nqc_flag: 0,
grp: 1
}
}
}
})
// searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
return searchRequest.promise.then(({ body }) => {
// console.log(body)
if (body.code != this.successCode || body.req.code != this.successCode)
return this.musicSearch(str, page, limit, ++retryNum)
return body.req.data
})
},
handleResult(rawList) {
// console.log(rawList)
const list = []
rawList.forEach((item) => {
if (!item.file?.media_mid) return
let types = []
let _types = {}
const file = item.file
if (file.size_128mp3 != 0) {
let size = sizeFormate(file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
}
if (file.size_320mp3 !== 0) {
let size = sizeFormate(file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
}
if (file.size_flac !== 0) {
let size = sizeFormate(file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size
}
}
if (file.size_hires !== 0) {
let size = sizeFormate(file.size_hires)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
}
// types.reverse()
let albumId = ''
let albumName = ''
if (item.album) {
albumName = item.album.name
albumId = item.album.mid
}
list.push({
singer: formatSingerName(item.singer, 'name'),
name: item.name + (item.title_extra ?? ''),
albumName,
albumId,
source: 'tx',
interval: formatPlayTime(item.interval),
songId: item.id,
albumMid: item.album?.mid ?? '',
strMediaMid: item.file.media_mid,
songmid: item.mid,
img:
albumId === '' || albumId === '空'
? item.singer?.length
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: ''
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${albumId}.jpg`,
types,
_types,
typeUrl: {}
})
})
// console.log(list)
return list
},
search(str, page = 1, limit) {
if (limit == null) limit = this.limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page, limit).then(({ body, meta }) => {
let list = this.handleResult(body.item_song)
this.total = meta.estimate_sum
this.page = page
this.allPage = Math.ceil(this.total / limit)
return Promise.resolve({
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'tx'
})
})
}
}

View File

@@ -0,0 +1,252 @@
import { httpFetch } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
import { formatSingerName } from '../utils'
export const filterMusicInfoItem = (item) => {
const types = []
const _types = {}
if (item.file.size_128mp3 != 0) {
let size = sizeFormate(item.file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
}
if (item.file.size_320mp3 !== 0) {
let size = sizeFormate(item.file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
}
if (item.file.size_flac !== 0) {
let size = sizeFormate(item.file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size
}
}
if (item.file.size_hires !== 0) {
let size = sizeFormate(item.file.size_hires)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
}
const albumId = item.album.id ?? ''
const albumMid = item.album.mid ?? ''
const albumName = item.album.name ?? ''
return {
source: 'tx',
singer: formatSingerName(item.singer, 'name'),
name: item.title,
albumName,
albumId,
albumMid,
interval: formatPlayTime(item.interval),
songId: item.id,
songmid: item.mid,
strMediaMid: item.file.media_mid,
img:
albumId === '' || albumId === '空'
? item.singer?.length
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: ''
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${albumMid}.jpg`,
types,
_types,
typeUrl: {}
}
}
/**
* 创建一个适用于TX的Http请求
* @param {*} url
* @param {*} options
* @param {*} retryNum
*/
const createMusicuFetch = async (data, options, retryNum = 0) => {
if (retryNum > 2) throw new Error('try max num')
let result
try {
result = await httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'POST',
body: {
comm: {
cv: 4747474,
ct: 24,
format: 'json',
inCharset: 'utf-8',
outCharset: 'utf-8',
uin: 0
},
...data
},
headers: {
'User-Angent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'
}
}).promise
} catch (err) {
console.log(err)
return createMusicuFetch(data, options, ++retryNum)
}
if (result.statusCode !== 200 || result.body.code != 0)
return createMusicuFetch(data, options, ++retryNum)
return result.body
}
export default {
/**
* 获取歌手信息
* @param {*} id
*/
getInfo(id) {
return createMusicuFetch({
req_1: {
module: 'music.musichallSinger.SingerInfoInter',
method: 'GetSingerDetail',
param: {
singer_mid: [id],
ex_singer: 1,
wiki_singer: 1,
group_singer: 0,
pic: 1,
photos: 0
}
},
req_2: {
module: 'music.musichallAlbum.AlbumListServer',
method: 'GetAlbumList',
param: {
singerMid: id,
order: 0,
begin: 0,
num: 1,
songNumTag: 0,
singerID: 0
}
},
req_3: {
module: 'musichall.song_list_server',
method: 'GetSingerSongList',
param: {
singerMid: id,
order: 1,
begin: 0,
num: 1
}
}
}).then((body) => {
if (body.req_1.code != 0 || body.req_2 != 0 || body.req_3 != 0)
throw new Error('get singer info faild.')
const info = body.req_1.data.singer_list[0]
const music = body.req_3.data
const album = body.req_3.data
return {
source: 'tx',
id: info.basic_info.singer_mid,
info: {
name: info.basic_info.name,
desc: info.ex_info.desc,
avatar: info.pic.pic,
gender: info.ex_info.genre === 1 ? 'man' : 'woman'
},
count: {
music: music.totalNum,
album: album.total
}
}
})
},
/**
* 获取歌手专辑列表
* @param {*} id
* @param {*} page
* @param {*} limit
*/
getAlbumList(id, page = 1, limit = 10) {
if (page === 1) page = 0
return createMusicuFetch({
req: {
module: 'music.musichallAlbum.AlbumListServer',
method: 'GetAlbumList',
param: {
singerMid: id,
order: 0,
begin: page * limit,
num: limit,
songNumTag: 0,
singerID: 0
}
}
}).then((body) => {
if (body.req.code != 0) throw new Error('get singer album faild.')
const list = this.filterAlbumList(body.req.data.albumList)
return {
source: 'tx',
list,
limit,
page,
total: body.req.data.total
}
})
},
/**
* 获取歌手歌曲列表
* @param {*} id
* @param {*} page
* @param {*} limit
*/
async getSongList(id, page = 1, limit = 100) {
if (page === 1) page = 0
return createMusicuFetch({
req: {
module: 'musichall.song_list_server',
method: 'GetSingerSongList',
param: {
singerMid: id,
order: 1,
begin: page * limit,
num: limit
}
}
}).then((body) => {
if (body.req.code != 0) throw new Error('get singer song list faild.')
const list = this.filterSongList(body.req.data.songList)
return {
source: 'tx',
list,
limit,
page,
total: body.req.data.totalNum
}
})
},
filterAlbumList(raw) {
return raw.map((item) => {
return {
id: item.albumID,
mid: item.albumMid,
count: item.totalNum,
info: {
name: item.albumName,
author: item.singerName,
img: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${item.albumMid}.jpg`,
desc: null
}
}
})
},
filterSongList(raw) {
raw.map((item) => {
return filterMusicInfoItem(item.songInfo)
})
}
}

View File

@@ -0,0 +1,351 @@
import { httpFetch } from '../../request'
import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../index'
import { formatSingerName } from '../utils'
export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
limit_list: 36,
limit_song: 100000,
successCode: 0,
sortList: [
{
name: '最热',
id: 5
},
{
name: '最新',
id: 2
}
],
regExps: {
hotTagHtml: /class="c_bg_link js_tag_item" data-id="\w+">.+?<\/a>/g,
hotTag: /data-id="(\w+)">(.+?)<\/a>/,
// https://y.qq.com/n/yqq/playlist/7217720898.html
// https://i.y.qq.com/n2/m/share/details/taoge.html?platform=11&appshare=android_qq&appversion=9050006&id=7217720898&ADTAG=qfshare
listDetailLink: /\/playlist\/(\d+)/,
listDetailLink2: /id=(\d+)/
},
tagsUrl:
'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=%7B%22tags%22%3A%7B%22method%22%3A%22get_all_categories%22%2C%22param%22%3A%7B%22qq%22%3A%22%22%7D%2C%22module%22%3A%22playlist.PlaylistAllCategoriesServer%22%7D%7D',
hotTagUrl: 'https://c.y.qq.com/node/pc/wk_v15/category_playlist.html',
getListUrl(sortId, id, page) {
if (id) {
id = parseInt(id)
return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(
JSON.stringify({
comm: { cv: 1602, ct: 20 },
playlist: {
method: 'get_category_content',
param: {
titleid: id,
caller: '0',
category_id: id,
size: this.limit_list,
page: page - 1,
use_page: 1
},
module: 'playlist.PlayListCategoryServer'
}
})
)}`
}
return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(
JSON.stringify({
comm: { cv: 1602, ct: 20 },
playlist: {
method: 'get_playlist_by_tag',
param: {
id: 10000000,
sin: this.limit_list * (page - 1),
size: this.limit_list,
order: sortId,
cur_page: page
},
module: 'playlist.PlayListPlazaServer'
}
})
)}`
},
getListDetailUrl(id) {
return `https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&new_format=1&disstid=${id}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0`
},
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
// 获取标签
getTag(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.tagsUrl)
return this._requestObj_tags.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getTag(++tryNum)
return this.filterTagInfo(body.tags.data.v_group)
})
},
// 获取标签
getHotTag(tryNum = 0) {
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_hotTags = httpFetch(this.hotTagUrl)
return this._requestObj_hotTags.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getHotTag(++tryNum)
return this.filterInfoHotTag(body)
})
},
filterInfoHotTag(html) {
let hotTag = html.match(this.regExps.hotTagHtml)
const hotTags = []
if (!hotTag) return hotTags
hotTag.forEach((tagHtml) => {
let result = tagHtml.match(this.regExps.hotTag)
if (!result) return
hotTags.push({
id: parseInt(result[1]),
name: result[2],
source: 'tx'
})
})
return hotTags
},
filterTagInfo(rawList) {
return rawList.map((type) => ({
name: type.group_name,
list: type.v_item.map((item) => ({
parent_id: type.group_id,
parent_name: type.group_name,
id: item.id,
name: item.name,
source: 'tx'
}))
}))
},
// 获取列表数据
getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(this.getListUrl(sortId, tagId, page))
// console.log(this.getListUrl(sortId, tagId, page))
return this._requestObj_list.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return tagId
? this.filterList2(body.playlist.data, page)
: this.filterList(body.playlist.data, page)
})
},
filterList(data, page) {
return {
list: data.v_playlist.map((item) => ({
play_count: formatPlayCount(item.access_num),
id: String(item.tid),
author: item.creator_info.nick,
name: item.title,
time: item.modify_time ? dateFormat(item.modify_time * 1000, 'Y-M-D') : '',
img: item.cover_url_medium,
// grade: item.favorcnt / 10,
total: item.song_ids?.length,
desc: decodeName(item.desc).replace(/<br>/g, '\n'),
source: 'tx'
})),
total: data.total,
page,
limit: this.limit_list,
source: 'tx'
}
},
filterList2({ content }, page) {
// console.log(content.v_item)
return {
list: content.v_item.map(({ basic }) => ({
play_count: formatPlayCount(basic.play_cnt),
id: String(basic.tid),
author: basic.creator.nick,
name: basic.title,
// time: basic.publish_time,
img: basic.cover.medium_url || basic.cover.default_url,
// grade: basic.favorcnt / 10,
desc: decodeName(basic.desc).replace(/<br>/g, '\n'),
source: 'tx'
})),
total: content.total_cnt,
page,
limit: this.limit_list,
source: 'tx'
}
},
async handleParseId(link, retryNum = 0) {
if (retryNum > 2) return Promise.reject(new Error('link try max num'))
const requestObj_listDetailLink = httpFetch(link)
const {
headers: { location },
statusCode
} = await requestObj_listDetailLink.promise
// console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum)
return location == null ? link : location
},
async getListId(id) {
if (/[?&:/]/.test(id)) {
if (!this.regExps.listDetailLink.test(id)) {
id = await this.handleParseId(id)
}
let result = this.regExps.listDetailLink.exec(id)
if (!result) {
result = this.regExps.listDetailLink2.exec(id)
if (!result) throw new Error('failed')
}
id = result[1]
// console.log(id)
}
return id
},
// 获取歌曲列表内的音乐
async getListDetail(id, tryNum = 0) {
if (tryNum > 2) return Promise.reject(new Error('try max num'))
id = await this.getListId(id)
const requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
headers: {
Origin: 'https://y.qq.com',
Referer: `https://y.qq.com/n/yqq/playsquare/${id}.html`
}
})
const { body } = await requestObj_listDetail.promise
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
const cdlist = body.cdlist[0]
return {
list: this.filterListDetail(cdlist.songlist),
page: 1,
limit: cdlist.songlist.length + 1,
total: cdlist.songlist.length,
source: 'tx',
info: {
name: cdlist.dissname,
img: cdlist.logo,
desc: decodeName(cdlist.desc).replace(/<br>/g, '\n'),
author: cdlist.nickname,
play_count: formatPlayCount(cdlist.visitnum)
}
}
},
filterListDetail(rawList) {
// console.log(rawList)
return rawList.map((item) => {
let types = []
let _types = {}
if (item.file.size_128mp3 !== 0) {
let size = sizeFormate(item.file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size
}
}
if (item.file.size_320mp3 !== 0) {
let size = sizeFormate(item.file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size
}
}
if (item.file.size_flac !== 0) {
let size = sizeFormate(item.file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size
}
}
if (item.file.size_hires !== 0) {
let size = sizeFormate(item.file.size_hires)
types.push({ type: 'flac24bit', size })
_types.flac24bit = {
size
}
}
// types.reverse()
return {
singer: formatSingerName(item.singer, 'name'),
name: item.title,
albumName: item.album.name,
albumId: item.album.mid,
source: 'tx',
interval: formatPlayTime(item.interval),
songId: item.id,
albumMid: item.album.mid,
strMediaMid: item.file.media_mid,
songmid: item.mid,
img:
item.album.name === '' || item.album.name === '空'
? item.singer?.length
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: ''
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${item.album.mid}.jpg`,
lrc: null,
otherSource: null,
types,
_types,
typeUrl: {}
}
})
},
getTags() {
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({
tags,
hotTag,
source: 'tx'
}))
},
async getDetailPageUrl(id) {
id = await this.getListId(id)
return `https://y.qq.com/n/ryqq/playlist/${id}`
},
search(text, page, limit = 20, retryNum = 0) {
if (retryNum > 5) throw new Error('max retry')
return httpFetch(
`http://c.y.qq.com/soso/fcgi-bin/client_music_search_songlist?page_no=${page - 1}&num_per_page=${limit}&format=json&query=${encodeURIComponent(text)}&remoteplace=txt.yqq.playlist&inCharset=utf8&outCharset=utf-8`,
{
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)',
Referer: 'http://y.qq.com/portal/search.html'
}
}
).promise.then(({ body }) => {
if (body.code != 0) return this.search(text, page, limit, ++retryNum)
// console.log(body.data.list)
return {
list: body.data.list.map((item) => {
return {
play_count: formatPlayCount(item.listennum),
id: String(item.dissid),
author: decodeName(item.creator.name),
name: decodeName(item.dissname),
time: dateFormat(item.createtime, 'Y-M-D'),
img: item.imgurl,
// grade: item.favorcnt / 10,
total: item.song_count,
desc: decodeName(decodeName(item.introduction)).replace(/<br>/g, '\n'),
source: 'tx'
}
}),
limit,
total: body.data.sum,
source: 'tx'
}
})
}
}
// getList
// getTags
// getListDetail

View File

@@ -0,0 +1,32 @@
import { httpFetch } from '../../request'
export default {
// regExps: {
// relWord: /RELWORD=(.+)/,
// },
requestObj: null,
tipSearch(str) {
this.cancelTipSearch()
this.requestObj = httpFetch(
`https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?is_xml=0&format=json&key=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`,
{
headers: {
Referer: 'https://y.qq.com/portal/player.html'
}
}
)
return this.requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200 || body.code != 0) return Promise.reject(new Error('请求失败'))
return body.data
})
},
handleResult(rawData) {
return rawData.map((info) => `${info.name} - ${info.singer}`)
},
cancelTipSearch() {
if (this.requestObj && this.requestObj.cancelHttp) this.requestObj.cancelHttp()
},
async search(str) {
return this.tipSearch(str).then((result) => this.handleResult(result.song.itemlist))
}
}

View File

@@ -0,0 +1,52 @@
import crypto from 'crypto'
import dns from 'dns'
import { decodeName } from '../utils'
export const toMD5 = (str) => crypto.createHash('md5').update(str).digest('hex')
const ipMap = new Map()
export const getHostIp = (hostname) => {
const result = ipMap.get(hostname)
if (typeof result === 'object') return result
if (result === true) return
ipMap.set(hostname, true)
// console.log(hostname)
dns.lookup(
hostname,
{
// family: 4,
all: false
},
(err, address, family) => {
if (err) return console.log(err)
// console.log(address, family)
ipMap.set(hostname, { address, family })
}
)
}
export const dnsLookup = (hostname, options, callback) => {
const result = getHostIp(hostname)
if (result) return callback(null, result.address, result.family)
dns.lookup(hostname, options, callback)
}
/**
* 格式化歌手
* @param singers 歌手数组
* @param nameKey 歌手名键值
* @param join 歌手分割字符
*/
export const formatSingerName = (singers, nameKey = 'name', join = '、') => {
if (Array.isArray(singers)) {
const singer = []
singers.forEach((item) => {
let name = item[nameKey]
if (!name) return
singer.push(name)
})
return decodeName(singer.join(join))
}
return decodeName(String(singers ?? ''))
}

View File

@@ -0,0 +1,52 @@
import { httpFetch } from '../../request'
import { requestMsg } from '../../message'
import { headers, timeout } from '../options'
import { dnsLookup } from '../utils'
const api_test = {
getMusicUrl(songInfo, type) {
const requestObj = httpFetch(`http://ts.tempmusics.tk/url/wy/${songInfo.songmid}/${type}`, {
method: 'get',
timeout,
headers,
lookup: dnsLookup,
family: 4
})
requestObj.promise = requestObj.promise.then(({ statusCode, body }) => {
if (statusCode == 429) return Promise.reject(new Error(requestMsg.tooManyRequests))
switch (body.code) {
case 0:
return Promise.resolve({ type, url: body.data })
default:
return Promise.reject(new Error(requestMsg.fail))
}
})
return requestObj
}
/* getPic(songInfo) {
const requestObj = httpFetch(`http://localhost:3100/pic/wy/${songInfo.songmid}`, {
method: 'get',
timeout,
headers,
family: 4,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
},
getLyric(songInfo) {
const requestObj = httpFetch(`http://localhost:3100/lrc/wy/${songInfo.songmid}`, {
method: 'get',
timeout,
headers,
family: 4,
})
requestObj.promise = requestObj.promise.then(({ body }) => {
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
})
return requestObj
}, */
}
export default api_test

View File

@@ -0,0 +1,224 @@
import { httpFetch } from '../../request'
import { weapi } from './utils/crypto'
import { dateFormat2 } from '../index'
const emojis = [
['大笑', '😃'],
['可爱', '😊'],
['憨笑', '☺️'],
['色', '😍'],
['亲亲', '😙'],
['惊恐', '😱'],
['流泪', '😭'],
['亲', '😚'],
['呆', '😳'],
['哀伤', '😔'],
['呲牙', '😁'],
['吐舌', '😝'],
['撇嘴', '😒'],
['怒', '😡'],
['奸笑', '😏'],
['汗', '😓'],
['痛苦', '😖'],
['惶恐', '😰'],
['生病', '😨'],
['口罩', '😷'],
['大哭', '😂'],
['晕', '😵'],
['发怒', '👿'],
['开心', '😄'],
['鬼脸', '😜'],
['皱眉', '😞'],
['流感', '😢'],
['爱心', '❤️'],
['心碎', '💔'],
['钟情', '💘'],
['星星', '⭐️'],
['生气', '💢'],
['便便', '💩'],
['强', '👍'],
['弱', '👎'],
['拜', '🙏'],
['牵手', '👫'],
['跳舞', '👯‍♀️'],
['禁止', '🙅‍♀️'],
['这边', '💁‍♀️'],
['爱意', '💏'],
['示爱', '👩‍❤️‍👨'],
['嘴唇', '👄'],
['狗', '🐶'],
['猫', '🐱'],
['猪', '🐷'],
['兔子', '🐰'],
['小鸡', '🐤'],
['公鸡', '🐔'],
['幽灵', '👻'],
['圣诞', '🎅'],
['外星', '👽'],
['钻石', '💎'],
['礼物', '🎁'],
['男孩', '👦'],
['女孩', '👧'],
['蛋糕', '🎂'],
['18', '🔞'],
['圈', '⭕'],
['叉', '❌']
]
const applyEmoji = (text) => {
for (const e of emojis) text = text.replaceAll(`[${e[0]}]`, e[1])
return text
}
let cursorTools = {
cache: {},
getCursor(id, page, limit) {
let cacheData = this.cache[id]
if (!cacheData) cacheData = this.cache[id] = {}
let orderType
let cursor
let offset
if (page == 1) {
cacheData.page = 1
cursor = cacheData.cursor = cacheData.prevCursor = Date.now()
orderType = 1
offset = 0
} else if (cacheData.page) {
cursor = cacheData.cursor
if (page > cacheData.page) {
orderType = 1
offset = (page - cacheData.page - 1) * limit
} else if (page < cacheData.page) {
orderType = 0
offset = (cacheData.page - page - 1) * limit
} else {
cursor = cacheData.cursor = cacheData.prevCursor
offset = cacheData.offset
orderType = cacheData.orderType
}
}
return {
orderType,
cursor,
offset
}
},
setCursor(id, cursor, orderType, offset, page) {
let cacheData = this.cache[id]
if (!cacheData) cacheData = this.cache[id] = {}
cacheData.prevCursor = cacheData.cursor
cacheData.cursor = cursor
cacheData.orderType = orderType
cacheData.offset = offset
cacheData.page = page
}
}
export default {
_requestObj: null,
_requestObj2: null,
async getComment({ songmid }, page = 1, limit = 20) {
if (this._requestObj) this._requestObj.cancelHttp()
const id = 'R_SO_4_' + songmid
const cursorInfo = cursorTools.getCursor(songmid, page, limit)
const _requestObj = httpFetch('https://music.163.com/weapi/comment/resource/comments/get', {
method: 'post',
headers: {
'User-Agent':
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
origin: 'https://music.163.com',
Refere: 'http://music.163.com/'
},
form: weapi({
cursor: cursorInfo.cursor,
offset: cursorInfo.offset,
orderType: cursorInfo.orderType,
pageNo: page,
pageSize: limit,
rid: id,
threadId: id
})
})
const { body, statusCode } = await _requestObj.promise
// console.log(body)
if (statusCode != 200 || body.code !== 200) throw new Error('获取评论失败')
cursorTools.setCursor(songmid, body.data.cursor, cursorInfo.orderType, cursorInfo.offset, page)
return {
source: 'wy',
comments: this.filterComment(body.data.comments),
total: body.data.totalCount,
page,
limit,
maxPage: Math.ceil(body.data.totalCount / limit) || 1
}
},
async getHotComment({ songmid }, page = 1, limit = 100) {
if (this._requestObj2) this._requestObj2.cancelHttp()
const id = 'R_SO_4_' + songmid
page = page - 1
const _requestObj2 = httpFetch(`https://music.163.com/weapi/v1/resource/hotcomments/${id}`, {
method: 'post',
headers: {
'User-Agent':
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
origin: 'https://music.163.com',
Refere: 'http://music.163.com/'
},
form: weapi({
rid: id,
limit,
offset: limit * page,
beforeTime: Date.now().toString()
})
})
const { body, statusCode } = await _requestObj2.promise
if (statusCode != 200 || body.code !== 200) throw new Error('获取热门评论失败')
const total = body.total ?? 0
return {
source: 'wy',
comments: this.filterComment(body.hotComments),
total,
page,
limit,
maxPage: Math.ceil(total / limit) || 1
}
},
filterComment(rawList) {
return rawList.map((item) => {
let data = {
id: item.commentId,
text: item.content ? applyEmoji(item.content) : '',
time: item.time ? item.time : '',
timeStr: item.time ? dateFormat2(item.time) : '',
location: item.ipLocation?.location,
userName: item.user.nickname,
avatar: item.user.avatarUrl,
userId: item.user.userId,
likedCount: item.likedCount,
reply: []
}
let replyData = item.beReplied && item.beReplied[0]
return replyData
? {
id: item.commentId,
rootId: replyData.beRepliedCommentId,
text: replyData.content ? applyEmoji(replyData.content) : '',
time: item.time,
timeStr: null,
location: replyData.ipLocation?.location,
userName: replyData.user.nickname,
avatar: replyData.user.avatarUrl,
userId: replyData.user.userId,
likedCount: null,
reply: [data]
}
: data
})
}
}

View File

@@ -0,0 +1,20 @@
import { eapiRequest } from './utils/index'
export default {
_requestObj: null,
async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = eapiRequest('/api/search/chart/detail', {
id: 'HOT_SEARCH_SONG#@#'
})
const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code !== 200) throw new Error('获取热搜词失败')
return { source: 'wy', list: this.filterList(body.data.itemList) }
},
filterList(rawList) {
return rawList.map((item) => item.searchWord)
}
}

Some files were not shown because too many files have changed in this diff Show More