mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-20 22:51:23 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae1197f552 | ||
|
|
3e8273780a | ||
|
|
e866ead286 | ||
|
|
eee3daa2d9 | ||
|
|
8efe78a3a3 | ||
|
|
04510ab4ae | ||
|
|
36411fb613 | ||
|
|
355057c56e | ||
|
|
da9d8822fc | ||
|
|
2d8b2e27e4 | ||
|
|
f108803509 | ||
|
|
563c491dad | ||
|
|
6a460caba5 | ||
|
|
890ce471a5 | ||
|
|
64aa95532e |
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
!/module
|
||||
!/plugins
|
||||
!/public
|
||||
!/static
|
||||
!/util
|
||||
!/app.js
|
||||
!/server.js
|
||||
!/package.json
|
||||
!/index.js
|
||||
!/generateConfig.js
|
||||
!/main.js
|
||||
!/data
|
||||
!/.env
|
||||
!/pnpm-lock.yaml
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.{js,py}]
|
||||
charset = utf-8
|
||||
|
||||
# 4 space indentation
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Indentation override for all JS under lib directory
|
||||
[*.{js,ts}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,27 +0,0 @@
|
||||
### CORS资源共享设置
|
||||
## 如果你需要配置, 请取消下面的注释并设置具体的域名
|
||||
# CORS_ALLOW_ORIGIN = "*"
|
||||
|
||||
### 代理设置
|
||||
## 启用反向代理
|
||||
ENABLE_PROXY = false
|
||||
## 代理配置
|
||||
PROXY_URL = "https://your-proxy-url.com/?proxy="
|
||||
|
||||
### 随机IP设置
|
||||
## 启用随机中国IP(默认关闭);启用后,所有请求默认使用随机中国IP,除非请求参数randomCNIP显式关闭
|
||||
ENABLE_RANDOM_CN_IP = false
|
||||
|
||||
### UnblockNeteaseMusic 设置项
|
||||
## 启用全局解灰, 无论是否调用参数都会使用解灰(不推荐开启)
|
||||
ENABLE_GENERAL_UNBLOCK = false
|
||||
## 歌曲启用无损音质
|
||||
ENABLE_FLAC = true
|
||||
## 启用无损音质时,是否选择音质最高的
|
||||
SELECT_MAX_BR = false
|
||||
## 严格按照配置音源设置顺序进行匹配
|
||||
FOLLOW_SOURCE_ORDER = true
|
||||
## Cookie设置项; 推荐在Vercel等平台进行环境变量的设置或者设置github secret
|
||||
# 网易云 cookie
|
||||
# 格式:MUSIC_U=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# NETEASE_COOKIE = ""
|
||||
@@ -1 +0,0 @@
|
||||
custom: ["https://rewards.focalors.ltd"]
|
||||
@@ -1,117 +0,0 @@
|
||||
name: 🐛 Bug报告
|
||||
description: 报告API其中的错误或问题
|
||||
title: "[Bug] "
|
||||
labels: ["bug"]
|
||||
assignees: []
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您报告Bug!请填写以下信息帮助我们快速定位和修复问题。
|
||||
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: 确认事项
|
||||
description: |
|
||||
在提交Bug报告前,请确认以下事项:
|
||||
|
||||
- 我已经搜索了现有的issues,确认这不是重复问题
|
||||
- 我使用的是最新版本的API, 而且是官方发布的版本,而不是fork或修改版
|
||||
- 不处理别人搭建的线上服务的问题,此项目提供任何线上服务不保证质量
|
||||
- 如果不是提建议,提 issues 如果不照着模版来将不会优先处理或放着不管
|
||||
- 维护项目都是业余时间,精力有限,我只能挑容易解决的issues处理,为了节约双方时间,请尽可能提供足够的有用的信息,给的信息不够我只能根据精力和时间看情况处理,如果模板信息看都不看就删掉,我不会进行任何回复,并且一个月后close掉issue
|
||||
|
||||
options:
|
||||
- label: 我已确认以上事项
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: API版本
|
||||
description: 请提供您使用的API版本以及nodejs版本
|
||||
placeholder: "v4.2x.x, Node.js v22.x.x"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: deployment
|
||||
attributes:
|
||||
label: 部署方式
|
||||
description: 此项目支持本地部署和云平台部署,您使用的是哪种方式?
|
||||
options:
|
||||
- 本机直接运行 (node / pm2)
|
||||
- 本机 Docker 部署
|
||||
- Vercel
|
||||
- Heroku
|
||||
- Railway
|
||||
- 其他云平台 (请在描述中说明)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Bug描述
|
||||
description: 请详细描述您遇到的问题
|
||||
placeholder: |
|
||||
清晰简洁地描述bug的具体表现...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: 重现步骤
|
||||
description: 请提供详细的重现步骤
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: 期望行为
|
||||
description: 请描述您期望发生的行为
|
||||
placeholder: 清晰简洁地描述您期望应该发生什么...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: actual-behavior
|
||||
attributes:
|
||||
label: 实际行为
|
||||
description: 请描述实际发生的行为
|
||||
placeholder: 清晰简洁地描述实际发生了什么...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: error-output
|
||||
attributes:
|
||||
label: 错误输出日志
|
||||
description: 请提供完整的错误信息日志
|
||||
render: shell
|
||||
placeholder: |
|
||||
错误信息输出...
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 额外信息
|
||||
description: 添加任何其他有助于理解问题的信息
|
||||
placeholder: |
|
||||
- 截图
|
||||
- 相关配置文件
|
||||
- 其他相关信息
|
||||
|
||||
- type: checkboxes
|
||||
id: help
|
||||
attributes:
|
||||
label: 愿意协助
|
||||
description: 您是否愿意帮助修复这个bug?
|
||||
options:
|
||||
- label: 我愿意提交PR来修复这个bug
|
||||
- label: 我可以提供更多测试用例
|
||||
- label: 我可以帮助测试修复方案
|
||||
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: ↑请尽量使用议题模板创建议题↑
|
||||
url: https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced/issues/new/choose
|
||||
about: 选择适合的议题模板可以帮助我们更快地定位和解决问题
|
||||
- name: 提问的艺术
|
||||
url: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md
|
||||
about: 默认所有 Issues 发起者均已了解此处的内容
|
||||
@@ -1,72 +0,0 @@
|
||||
name: ✨ 接口添加请求
|
||||
description: 希望添加新接口
|
||||
title: "[Feature] "
|
||||
labels: ["enhancement"]
|
||||
assignees: []
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您提出新功能建议!请填写以下信息帮助我们理解您的需求。
|
||||
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: 确认事项
|
||||
description: 在提交功能请求前,请确认以下事项
|
||||
options:
|
||||
- label: 我已经搜索了现有的issues,确认这不是重复请求
|
||||
required: true
|
||||
- label: 我已经查看了项目文档
|
||||
required: true
|
||||
- label: 该项目符合该项目的目标和范围
|
||||
required: true
|
||||
- label: 我确认我提供的接口请求是合理且有意义的
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
id: proposed-solution
|
||||
attributes:
|
||||
label: 功能描述
|
||||
description: 请详细描述您希望添加的功能,最好贴上对应客户端界面的截图
|
||||
placeholder: |
|
||||
请详细描述您期望的功能:
|
||||
- 新的网易云音乐api 接口?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: 优先级
|
||||
description: 您认为这个功能的优先级如何?
|
||||
options:
|
||||
- 低 (可有可无的改进)
|
||||
- 中 (会显著改善使用体验)
|
||||
- 高 (严重影响了我使用)
|
||||
- 紧急 (阻塞了重要工作)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: compatibility
|
||||
attributes:
|
||||
label: 兼容性考虑
|
||||
description: 这个功能是否会影响现有代码的兼容性?
|
||||
placeholder: |
|
||||
- 是否是破坏性变更?
|
||||
- 是否需要语法变更?
|
||||
- 对性能的影响?
|
||||
|
||||
- type: checkboxes
|
||||
id: implementation
|
||||
attributes:
|
||||
label: 实现意愿
|
||||
description: 您是否愿意参与这个功能的实现?
|
||||
options:
|
||||
- label: 我愿意提交PR来实现这个功能
|
||||
- label: 我可以协助设计API接口
|
||||
- label: 我可以提供测试用例
|
||||
- label: 我可以帮助编写文档
|
||||
@@ -1,41 +0,0 @@
|
||||
# .github/dependabot.yml
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# 更新 npm (JavaScript) 依赖
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/" # package.json 所在的位置
|
||||
schedule:
|
||||
interval: "weekly" # 每周一检查
|
||||
# 默认情况下,Dependabot 会自动获取最新版本。
|
||||
# 你可以使用 `ignore` 来排除某些特定更新
|
||||
ignore:
|
||||
- dependency-name: "eslint"
|
||||
# 对于 ESLint,忽略主版本 8 以上的所有更新
|
||||
versions: ["> 8"]
|
||||
# 自动合并小版本更新的 PR (可选)
|
||||
# 可以设置针对 patch 或 minor 版本更新自动合并
|
||||
commit-message:
|
||||
prefix: "build(packages):" # PR 合并提交信息的前缀
|
||||
|
||||
# 更新 GitHub Actions 依赖
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # .github/workflows/ 目录所在的位置
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
|
||||
# 更新 Python (pip) 依赖,如果你的项目有 requirements.txt
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# 如果你有多个目录(例如一个 monorepo),可以为每个目录单独配置
|
||||
# - package-ecosystem: "npm"
|
||||
# directory: "/packages/frontend"
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# - package-ecosystem: "npm"
|
||||
# directory: "/packages/backend"
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Build Artifacts
|
||||
|
||||
on:
|
||||
workflow_dispatch: # 手动触发
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
target: node18-linux-x64
|
||||
output: precompiled/app
|
||||
- os: windows-latest
|
||||
platform: win
|
||||
target: node18-win-x64
|
||||
output: precompiled/app.exe
|
||||
- os: macos-latest
|
||||
platform: macos
|
||||
target: node18-macos-x64
|
||||
output: precompiled/app
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Build for ${{ matrix.platform }}
|
||||
run: |
|
||||
npm run pkg${{ matrix.platform }}
|
||||
env:
|
||||
PKG_TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: app-${{ matrix.platform }}
|
||||
path: ${{ matrix.output }}
|
||||
if-no-files-found: error
|
||||
@@ -1,79 +0,0 @@
|
||||
name: Issue Management
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
welcome-new-issues:
|
||||
if: github.event.action == 'opened'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Welcome new issue
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
## 快速链接
|
||||
|
||||
在我们处理您的问题之前,您可以先查看以下资源:
|
||||
|
||||
- 📖 查看[项目文档](https://neteasecloudmusicapienhanced.js.org)
|
||||
- 💬 加入[QQ交流群](https://qm.qq.com/q/TpeP9Uv2yk)
|
||||
- 🔍 搜索[现有issues](https://github.com/neteasecloudmusicapienhanced/api-enhanced/issues) 看是否有类似问题
|
||||
|
||||
handle-help-wanted:
|
||||
if: contains(github.event.issue.labels.*.name, 'question')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add help resources
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
🆘 需要帮助?
|
||||
|
||||
这里有一些可能对您有用的资源:
|
||||
|
||||
📚 **文档资源**
|
||||
- [项目文档](https://neteasecloudmusicapienhanced.js.org)
|
||||
|
||||
🔍 **常见问题**
|
||||
- 搜索[已关闭的issues](https://github.com/neteasecloudmusicapienhanced/api-enhanced/issues?q=is%3Aissue+is%3Aclosed) 看看是否有类似的问题已经被解答。
|
||||
|
||||
💬 **即时帮助**
|
||||
- 加入QQ群:https://qm.qq.com/q/TpeP9Uv2yk
|
||||
|
||||
如果以上资源无法解决您的问题,请提供更多详细信息,我们会尽快为您解答!
|
||||
|
||||
stale-issues:
|
||||
if: github.event.action == 'opened'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark stale issues
|
||||
uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: |
|
||||
这个issue已经30天没有活动了。
|
||||
|
||||
如果这个问题仍然存在,请回复此评论说明当前状态。
|
||||
否则,这个issue将在7天后自动关闭。
|
||||
|
||||
您可以随时重新打开已关闭的issue。
|
||||
stale-pr-message: |
|
||||
这个PR已经30天没有活动了。
|
||||
|
||||
如果您仍在处理这个PR,请回复此评论。
|
||||
否则,这个PR将在7天后自动关闭。
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
stale-issue-label: "stale"
|
||||
stale-pr-label: "stale"
|
||||
exempt-issue-labels: "pinned,enhancement,priority:high,ongoing,fatal"
|
||||
exempt-pr-labels: "pinned,priority:high,ongoing,fatal"
|
||||
@@ -1,315 +0,0 @@
|
||||
name: Create Release on Version Change
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- package.json
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
detect:
|
||||
name: Detect Version Change
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.value }}
|
||||
should_release: ${{ steps.check.outputs.should_release }}
|
||||
tag_exists: ${{ steps.tagcheck.outputs.exists }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract version from package.json
|
||||
id: version
|
||||
run: |
|
||||
echo "value=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if version changed
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if git diff "${{ github.event.before }}" "${{ github.sha }}" -- package.json | grep -q '"version"'; then
|
||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_release=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Check if tag already exists
|
||||
id: tagcheck
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.value }}"
|
||||
|
||||
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build ${{ matrix.platform }}
|
||||
needs: detect
|
||||
|
||||
if: |
|
||||
needs.detect.outputs.should_release == 'true' &&
|
||||
needs.detect.outputs.tag_exists != 'true'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
target: node18-linux-x64
|
||||
output: ncm-api-linux-x64
|
||||
|
||||
- os: windows-latest
|
||||
platform: windows
|
||||
target: node18-win-x64
|
||||
output: ncm-api-win-x64.exe
|
||||
|
||||
- os: macos-latest
|
||||
platform: macos
|
||||
target: node18-macos-x64
|
||||
output: ncm-api-macos-x64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build binary
|
||||
shell: bash
|
||||
env:
|
||||
PKG_TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
mkdir -p release-artifacts
|
||||
|
||||
case "${{ matrix.platform }}" in
|
||||
linux)
|
||||
npm run pkglinux
|
||||
mv precompiled/app "release-artifacts/${{ matrix.output }}"
|
||||
;;
|
||||
|
||||
windows)
|
||||
npm run pkgwin
|
||||
mv precompiled/app.exe "release-artifacts/${{ matrix.output }}"
|
||||
;;
|
||||
|
||||
macos)
|
||||
npm run pkgmacos
|
||||
mv precompiled/app "release-artifacts/${{ matrix.output }}"
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ matrix.platform }}-binary
|
||||
path: release-artifacts/*
|
||||
|
||||
release:
|
||||
name: Create GitHub Release
|
||||
|
||||
needs:
|
||||
- detect
|
||||
- build
|
||||
|
||||
if: |
|
||||
needs.detect.outputs.should_release == 'true' &&
|
||||
needs.detect.outputs.tag_exists != 'true'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: release-artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: List flattened artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p final-artifacts
|
||||
cp release-artifacts/* final-artifacts/
|
||||
ls -lah final-artifacts
|
||||
|
||||
- name: Generate release notes
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="${{ needs.detect.outputs.version }}"
|
||||
|
||||
PREV_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
|
||||
{
|
||||
echo "# Release v${VERSION}"
|
||||
echo ""
|
||||
echo "## 更新内容 / Changelog"
|
||||
echo ""
|
||||
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
echo "从 \`$PREV_TAG\` 到 \`v$VERSION\` 的提交记录:"
|
||||
echo ""
|
||||
|
||||
git log "$PREV_TAG..HEAD" \
|
||||
--no-merges \
|
||||
--pretty=format:"- %s (%h)"
|
||||
|
||||
else
|
||||
echo "首次发布,包含以下提交:"
|
||||
echo ""
|
||||
|
||||
git log \
|
||||
--no-merges \
|
||||
--pretty=format:"- %s (%h)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "自动发布 via GitHub Actions"
|
||||
|
||||
} > release-notes.md
|
||||
|
||||
cat release-notes.md
|
||||
|
||||
- name: Create Git tag
|
||||
run: |
|
||||
git tag "v${{ needs.detect.outputs.version }}"
|
||||
git push origin "v${{ needs.detect.outputs.version }}"
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ needs.detect.outputs.version }}
|
||||
name: Release v${{ needs.detect.outputs.version }}
|
||||
body_path: release-notes.md
|
||||
files: final-artifacts/*
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
publish-docker:
|
||||
name: Publish Docker Image
|
||||
needs: detect
|
||||
|
||||
if: |
|
||||
needs.detect.outputs.should_release == 'true' &&
|
||||
needs.detect.outputs.tag_exists != 'true'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Read package version
|
||||
id: pkg
|
||||
run: echo "VERSION=$(jq -r .version package.json)" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
moefurina/ncm-api:latest
|
||||
moefurina/ncm-api:${{ env.VERSION }}
|
||||
ghcr.io/neteasecloudmusicapienhanced/ncm-api:latest
|
||||
ghcr.io/neteasecloudmusicapienhanced/ncm-api:${{ env.VERSION }}
|
||||
platforms: linux/amd64,linux/arm64/v8
|
||||
|
||||
publish-npm:
|
||||
name: Publish to npm
|
||||
needs: detect
|
||||
|
||||
if: |
|
||||
needs.detect.outputs.should_release == 'true' &&
|
||||
needs.detect.outputs.tag_exists != 'true'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- run: pnpm publish --access public --provenance
|
||||
@@ -1,36 +0,0 @@
|
||||
name: Upstream Sync
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 4 * * *" # At 12PM UTC+8
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync_latest_from_upstream:
|
||||
name: Sync latest commits from upstream repo
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.repository.fork }}
|
||||
|
||||
steps:
|
||||
# Step 1: run a standard checkout action
|
||||
- name: Checkout target repo
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Step 2: run the sync action
|
||||
- name: Sync upstream changes
|
||||
id: sync
|
||||
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.3
|
||||
with:
|
||||
upstream_sync_repo: NeteaseCloudMusicApiEnhanced/api-enhanced
|
||||
upstream_sync_branch: main
|
||||
target_sync_branch: main
|
||||
target_repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Sync check
|
||||
if: failure()
|
||||
run: |
|
||||
echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork."
|
||||
exit 1
|
||||
+1
-12
@@ -1,12 +1 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.log
|
||||
.idea
|
||||
.vscode
|
||||
.history
|
||||
examples/moddef.json
|
||||
bin
|
||||
anonymous_token
|
||||
.vercel
|
||||
.env
|
||||
IFLOW.md
|
||||
node_modules/
|
||||
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 22
|
||||
@@ -1,23 +0,0 @@
|
||||
# Agent Instructions for NeteaseCloudMusicApiEnhanced
|
||||
|
||||
## Quick Start
|
||||
- **Package Manager**: Use `pnpm` (not npm or yarn).
|
||||
- **Node Version**: Requires Node.js 18 or later.
|
||||
|
||||
## Developer Commands
|
||||
- **Install dependencies**: `pnpm i`
|
||||
- **Start server**: `pnpm start` or `node app.js`
|
||||
- **Start dev server**: `pnpm dev` (uses nodemon)
|
||||
- **Run tests**: `pnpm test` (uses Mocha)
|
||||
- **Linting**: `pnpm lint` (check) or `pnpm lint-fix` (auto-fix)
|
||||
|
||||
## Architecture & Entrypoints
|
||||
- **Executable Server**: `app.js` is the main entrypoint for running the API server.
|
||||
- **Module Exports**: `main.js` is the entrypoint when the project is imported as a Node.js dependency.
|
||||
- **API Endpoints**: Located in the `module/` directory. Each file typically corresponds to an API route.
|
||||
- **Core Utilities**: Request handling, encryption, and core utilities are found in the `util/` directory.
|
||||
|
||||
## Important Gotchas & Quirks
|
||||
- **Environment Variables**: The server defaults to port 3000 but can be overridden with the `PORT` environment variable.
|
||||
- **Proxy Variables**: Be very careful with proxy environment variables (`http_proxy`, `https_proxy`, `no_proxy`). The request library (like axios) will automatically pick these up. If they point to an unavailable proxy (especially common in Docker environments), requests will fail silently or throw connection errors.
|
||||
- **Code Style**: The project uses ESLint and Prettier. Always run `pnpm lint-fix` before committing changes to ensure formatting consistency.
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
FROM node:lts-alpine
|
||||
|
||||
RUN apk add --no-cache tini
|
||||
|
||||
ENV NODE_ENV production
|
||||
USER node
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --chown=node:node . ./
|
||||
|
||||
RUN yarn --network-timeout=100000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "/sbin/tini", "--", "node", "app.js" ]
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2022 Binaryify
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,253 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="./public/static/neteaselogo.png" alt="网易云音乐 API Enhanced Logo" />
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@neteasecloudmusicapienhanced/api">
|
||||
<img src="https://img.shields.io/npm/v/@neteasecloudmusicapienhanced/api?style=flat-square" alt="NPM Version" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@neteasecloudmusicapienhanced/api">
|
||||
<img src="https://img.shields.io/npm/dm/@neteasecloudmusicapienhanced/api?style=flat-square" alt="NPM Downloads" />
|
||||
</a>
|
||||
<a href="https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced">
|
||||
<img src="https://img.shields.io/github/license/NeteaseCloudMusicApiEnhanced/api-enhanced?style=flat-square" alt="License" />
|
||||
</a>
|
||||
<a href="https://nodejs.org/">
|
||||
<img src="https://img.shields.io/node/v/@neteasecloudmusicapienhanced/api?style=flat-square" alt="Node Version" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/moefurina/ncm-api">
|
||||
<img src="https://img.shields.io/docker/v/moefurina/ncm-api?style=flat-square" alt="Docker Version" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/moefurina/ncm-api">
|
||||
<img src="https://img.shields.io/docker/pulls/moefurina/ncm-api?style=flat-square" alt="Docker Pulls" />
|
||||
</a>
|
||||
<a href="https://www.typescriptlang.org/">
|
||||
<img src="https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript" alt="TypeScript" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
## 项目简介
|
||||
|
||||
网易云音乐第三方 Node.js API, 支持丰富的音乐相关接口,适合自建服务、二次开发和多平台部署
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> ## 注意
|
||||
>
|
||||
> - 本项目是自由项目, 不带任何担保, 在基于`MIT License`的许可下, 任何人都可以自由使用、修改和分发本项目的代码, 包括用于商业目的.
|
||||
> - 该项目是个合作计划, 有很多人参与了此次贡献, 前往 [Contributors](https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced/graphs/contributors) 页面查看所有贡献者
|
||||
> - 原作者项目 [Binaryify/NeteaseCloudMusicApi](https://github.com/binaryify/NeteaseCloudMusicApi) 并非完全停止维护, 你可以在 [NeteaseCloudMusicApi 的 NPMJS 页面](https://www.npmjs.com/package/NeteaseCloudMusicApi) 查看最新版本信息。
|
||||
> - 感谢尊重和理解!
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js 18 及以上
|
||||
- 推荐使用 pnpm 进行依赖管理
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
git clone https://github.com/neteasecloudmusicapienhanced/api-enhanced.git
|
||||
cd api
|
||||
pnpm i
|
||||
```
|
||||
|
||||
### 启动服务
|
||||
|
||||
```bash
|
||||
# 默认端口 3000
|
||||
node app.js
|
||||
|
||||
# 指定端口(如 4000)
|
||||
PORT=4000 node app.js # Mac/Linux
|
||||
set PORT=4000 && node app.js # Windows
|
||||
```
|
||||
|
||||
### 重要提示
|
||||
|
||||
- 调用前请务必阅读文档的 [调用前须知](https://neteasecloudmusicapienhanced.js.org/#/?id=%e8%b0%83%e7%94%a8%e5%89%8d%e9%a1%bb%e7%9f%a5) 部分。
|
||||
- 推荐将敏感信息(如 cookie)通过部署平台的环境变量进行配置。
|
||||
|
||||
## 在线体验与文档
|
||||
|
||||
- [在线文档](https://neteasecloudmusicapienhanced.js.org/)
|
||||
- [NPM 包地址](https://www.npmjs.com/package/@neteasecloudmusicapienhanced/api)
|
||||
- [Dockerhub 主页](https://hub.docker.com/r/moefurina/ncm-api)
|
||||
|
||||
## 常见部署方式
|
||||
|
||||
- 推荐参考[在线文档](https://neteasecloudmusicapienhanced.js.org/)
|
||||
|
||||
## Docker 部署说明
|
||||
|
||||
本项目支持通过 Docker 一键部署,无需手动安装 Node.js 或依赖。
|
||||
|
||||
> 注意: 在 docker 中运行的时候, 由于使用了 request 来发请求, 所以会检查几个 proxy 相关的环境变量(如下所列), 这些环境变量 会影响到 request 的代理, 详情请参考[request 的文档](https://github.com/request/request#proxies), 如果这些环境变量 指向的代理不可用, 那么就会造成错误, 所以在使用 docker 的时候一定要注意这些环境变量. 不过, 要是你在 query 中加上了 proxy 参数, 那么环境变量会被覆盖, 就会用你通过 proxy 参数提供的代理了.
|
||||
|
||||
request 相关的环境变量
|
||||
|
||||
1. http_proxy
|
||||
2. https_proxy
|
||||
3. HTTP_PROXY
|
||||
4. HTTPS_PROXY
|
||||
5. no_proxy
|
||||
6. NO_PROXY
|
||||
|
||||
```shell
|
||||
docker pull moefurina/ncm-api:latest
|
||||
|
||||
docker run -d -p 3000:3000 --name ncm-api moefurina/ncm-api:latest
|
||||
|
||||
|
||||
// 或者
|
||||
docker run -d -p 3000:3000 moefurina/ncm-api:latest
|
||||
|
||||
// 去掉或者设置相关的环境变量
|
||||
|
||||
docker run -d -p 3000:3000 --name ncm-api -e http_proxy= -e https_proxy= -e no_proxy= -e HTTP_PROXY= -e HTTPS_PROXY= -e NO_PROXY= moefurina/ncm-api:latest
|
||||
|
||||
// 或者
|
||||
docker run -d -p 3000:3000 -e http_proxy= -e https_proxy= -e no_proxy= -e HTTP_PROXY= -e HTTPS_PROXY= -e NO_PROXY= moefurina/ncm-api:latest
|
||||
```
|
||||
|
||||
> 以下是自行 build docker 镜像方式
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced && cd api-enhanced
|
||||
|
||||
$ sudo docker build . -t ncm-api
|
||||
|
||||
$ sudo docker run -d -p 3000:3000 ncm-api
|
||||
```
|
||||
|
||||
## 3. 环境变量
|
||||
|
||||
| 变量名 | 默认值 | 说明 |
|
||||
|----------------------------|--------------------------------------|----------------------------------------------------|
|
||||
| **CORS_ALLOW_ORIGIN** | `*` | 允许跨域请求的域名。可填写单个源,或使用逗号分隔多个源(例如 `https://a.com,https://b.com`)。 |
|
||||
| **ENABLE_PROXY** | `false` | 是否启用反向代理功能。 |
|
||||
| **PROXY_URL** | `https://your-proxy-url.com/?proxy=` | 代理服务地址。仅当 `ENABLE_PROXY=true` 时生效。 |
|
||||
| **ENABLE_RANDOM_CN_IP** | `false` | 是否默认启用随机中国IP。启用后,所有请求默认使用随机中国IP,除非请求参数 `randomCNIP` 显式关闭。 |
|
||||
| **ENABLE_GENERAL_UNBLOCK** | `true` | 是否启用全局解灰(推荐开启)。开启后所有歌曲都尝试自动解锁。 |
|
||||
| **ENABLE_FLAC** | `true` | 是否启用无损音质(FLAC)。 |
|
||||
| **SELECT_MAX_BR** | `false` | 启用无损音质时,是否选择最高码率音质。 |
|
||||
| **FOLLOW_SOURCE_ORDER** | `true` | 是否严格按照音源列表顺序进行匹配。 |
|
||||
|
||||
---
|
||||
|
||||
### 4. 更新镜像
|
||||
|
||||
更新到最新版本:
|
||||
|
||||
```bash
|
||||
docker pull moefurina/ncm-api:latest
|
||||
docker stop ncm-api && docker rm ncm-api
|
||||
docker run -d -p 3000:3000 moefurina/ncm-api:latest
|
||||
```
|
||||
|
||||
## Vercel 一键部署
|
||||
|
||||
1. fork 本项目
|
||||
2. 在 Vercel 官网新建项目,导入 fork 的仓库
|
||||
3. 直接 Deploy
|
||||
|
||||
## 腾讯云 Serverless 部署
|
||||
|
||||
1. fork 本项目
|
||||
2. 在腾讯云 serverless 控制台新建 Web 应用,选择 Express 框架
|
||||
3. 代码仓库选择 fork 的项目,启动文件填写:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
export PORT=9000
|
||||
/var/lang/node16/bin/node app.js
|
||||
```
|
||||
4. 完成部署后,访问 API 网关的 URL 即可
|
||||
|
||||
## Node.js 方式调用
|
||||
|
||||
支持直接在 Node.js 项目中引入和调用,返回 Promise:
|
||||
|
||||
```js
|
||||
const {
|
||||
login_cellphone,
|
||||
user_cloud,
|
||||
} = require('@neteasecloudmusicapienhanced/api')
|
||||
async function main() {
|
||||
const result = await login_cellphone({ phone: '手机号', password: '密码' })
|
||||
console.log(result)
|
||||
const result2 = await user_cloud({ cookie: result.body.cookie })
|
||||
console.log(result2.body)
|
||||
}
|
||||
main()
|
||||
```
|
||||
|
||||
## TypeScript 支持
|
||||
|
||||
```ts
|
||||
import { banner } from '@neteasecloudmusicapienhanced/api'
|
||||
banner({ type: 0 }).then((res) => console.log(res))
|
||||
```
|
||||
|
||||
## 单元测试
|
||||
|
||||
```bash
|
||||
pnpm test
|
||||
```
|
||||
|
||||
## 主要功能特性
|
||||
|
||||
- 登录/注册/验证码
|
||||
- 用户信息、歌单、动态、播放记录
|
||||
- 歌曲、专辑、歌手、MV、歌词、评论、排行榜
|
||||
- 搜索、推荐、私人 FM、签到、云盘
|
||||
- 歌曲解锁(解灰)
|
||||
|
||||
## 贡献与社区
|
||||
|
||||
- 欢迎提交 PR、Issue 参与维护
|
||||
- 要贡献新接口, 请参考[这篇文章](https://www.focalors.ltd/post/how-to-contribute-ncm-api)
|
||||
|
||||
### 致谢
|
||||
|
||||
原作者 [Binaryify/NeteaseCloudMusicApi](https://github.com/binaryify/NeteaseCloudMusicApi) 项目为本项目基础 (该项目在`npmjs`网站上仍持续维护, 但 github 仓库已不再更新)
|
||||
|
||||
感谢大佬们为逆向eapi, weapi, xeapi等加密算法所做的贡献
|
||||
|
||||
项目参考:
|
||||
|
||||
[trazyn/ieaseMusic](https://github.com/trazyn/ieaseMusic)
|
||||
|
||||
[listen1/listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension)
|
||||
|
||||
[chaunsin/netease-cloud-music](https://github.com/chaunsin/netease-cloud-music)
|
||||
|
||||
[folltoshe/netease-report-listen-song](https://github.com/folltoshe/netease-report-listen-song)
|
||||
|
||||
### SDK 生态
|
||||
|
||||
| 语言 | 作者 | 地址 | 类型 |
|
||||
|--------|---------------------------------------------|------------------------------------------------------------------------------------------|-----|
|
||||
| Java | [JackuXL](https://github.com/JackuXL) | [NeteaseCloudMusicApi-SDK](https://github.com/JackuXL/NeteaseCloudMusicApi-SDK) | 第三方 |
|
||||
| Java | [1015770492](https://github.com/1015770492) | [yumbo-music-utils](https://github.com/1015770492/yumbo-music-utils) | 第三方 |
|
||||
| Python | [盧瞳](https://github.com/2061360308) | [NeteaseCloudMusic_PythonSDK](https://github.com/2061360308/NeteaseCloudMusic_PythonSDK) | 第三方 |
|
||||
| Swift | [Lincb522](https://github.com/Lincb522) | [NeteaseCloudMusicApi-Swift](https://github.com/Lincb522/NeteaseCloudMusicAPI-Swift) | 第三方 |
|
||||
| Rust | [SPlayer-Dev](https://github.com/splayer-dev) | [ncm-api-rs](https://github.com/SPlayer-Dev/ncm-api-rs) | 第三方 |
|
||||
|
||||
|
||||
### 依赖此项目的优秀开源项目
|
||||
|
||||
- [qier2222/YesPlayMusic](https://github.com/qier222/YesPlayMusic)
|
||||
|
||||
- [MaigoLabs/amaoke.app](https://github.com/MaigoLabs/amaoke.app)
|
||||
|
||||
- [Yueby/music-together](https://github.com/Yueby/music-together)
|
||||
|
||||
- [chthollyphlie/folia-major](https://github.com/chthollyphile/folia-major)
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](https://github.com/MoeFurina/NeteaseCloudMusicApiEnhanced/blob/main/LICENSE)
|
||||
@@ -0,0 +1,4 @@
|
||||
## 这是什么
|
||||
这是网易云音乐api enhanced的文档页面
|
||||
|
||||
## [前往](https://docs-neteasecloudmusicapi.focalors.ltd)
|
||||
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const tmpPath = require('os').tmpdir()
|
||||
|
||||
async function start() {
|
||||
// 检测是否存在 anonymous_token 文件,没有则生成
|
||||
if (!fs.existsSync(path.resolve(tmpPath, 'anonymous_token'))) {
|
||||
fs.writeFileSync(path.resolve(tmpPath, 'anonymous_token'), '', 'utf-8')
|
||||
}
|
||||
// 启动时更新anonymous_token
|
||||
const generateConfig = require('./generateConfig')
|
||||
await generateConfig()
|
||||
require('./server').serveNcmApi({
|
||||
checkVersion: true,
|
||||
})
|
||||
}
|
||||
start()
|
||||
File diff suppressed because it is too large
Load Diff
-24641
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
||||
const { defineConfig, globalIgnores } = require('eslint/config')
|
||||
|
||||
const html = require('eslint-plugin-html')
|
||||
const globals = require('globals')
|
||||
const tsParser = require('@typescript-eslint/parser')
|
||||
const js = require('@eslint/js')
|
||||
|
||||
const { FlatCompat } = require('@eslint/eslintrc')
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
})
|
||||
|
||||
module.exports = defineConfig([
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
|
||||
plugins: {
|
||||
html,
|
||||
},
|
||||
|
||||
extends: compat.extends('plugin:prettier/recommended'),
|
||||
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
|
||||
indent: [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
},
|
||||
],
|
||||
|
||||
'space-infix-ops': [
|
||||
'error',
|
||||
{
|
||||
int32Hint: false,
|
||||
},
|
||||
],
|
||||
|
||||
'key-spacing': [
|
||||
2,
|
||||
{
|
||||
beforeColon: false,
|
||||
afterColon: true,
|
||||
},
|
||||
],
|
||||
|
||||
'no-octal': 2,
|
||||
'no-redeclare': 2,
|
||||
'comma-spacing': 2,
|
||||
'no-new-object': 2,
|
||||
'arrow-spacing': 2,
|
||||
|
||||
quotes: [
|
||||
2,
|
||||
'single',
|
||||
{
|
||||
avoidEscape: true,
|
||||
allowTemplateLiterals: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
globalIgnores(['**/public/']),
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
},
|
||||
extends: compat.extends('plugin:@typescript-eslint/recommended'),
|
||||
},
|
||||
])
|
||||
@@ -1,23 +0,0 @@
|
||||
const fsPromises = require('fs/promises')
|
||||
const path = require('path')
|
||||
const server = require('../server')
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
const exportFile = path.join(__dirname, 'moddef.json')
|
||||
|
||||
async function main() {
|
||||
const def = await server.getModulesDefinitions(
|
||||
path.join(__dirname, '..', 'module'),
|
||||
{
|
||||
'daily_signin.js': '/daily_signin',
|
||||
'fm_trash.js': '/fm_trash',
|
||||
'personal_fm.js': '/personal_fm',
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
fsPromises.writeFile(exportFile, JSON.stringify(def, null, 4))
|
||||
logger.info(`👍 Get your own definition at: ${exportFile}`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,41 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { register_anonimous } = require('./main')
|
||||
const { cookieToJson, generateRandomChineseIP } = require('./util/index')
|
||||
const { getXeapiPublicKey } = require('./util/xeapiKey')
|
||||
const tmpPath = require('os').tmpdir()
|
||||
|
||||
async function generateConfig() {
|
||||
global.cnIp = generateRandomChineseIP()
|
||||
try {
|
||||
const res = await register_anonimous()
|
||||
const cookie = res.body.cookie
|
||||
if (cookie) {
|
||||
const cookieObj = cookieToJson(cookie)
|
||||
fs.writeFileSync(
|
||||
path.resolve(tmpPath, 'anonymous_token'),
|
||||
cookieObj.MUSIC_A,
|
||||
'utf-8',
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
try {
|
||||
let currentPublicKey = {}
|
||||
try {
|
||||
currentPublicKey = JSON.parse(
|
||||
fs.readFileSync(path.resolve(tmpPath, 'xeapi_public_key'), 'utf-8'),
|
||||
)
|
||||
} catch (_) {}
|
||||
const publicKey = await getXeapiPublicKey(currentPublicKey, global.deviceId)
|
||||
fs.writeFileSync(
|
||||
path.resolve(tmpPath, 'xeapi_public_key'),
|
||||
JSON.stringify(publicKey),
|
||||
'utf-8',
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
module.exports = generateConfig
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="KEYWords" contect="网易云音乐,网易云音乐 api,网易云音乐 nodejs,网易云音乐 node.js">
|
||||
@@ -7,7 +8,8 @@
|
||||
<title>网易云音乐 NodeJS API Enhanced</title>
|
||||
<link rel="icon" href="netease.png">
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta name="referrer" content="never">
|
||||
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css" id="theme-css">
|
||||
<style>
|
||||
@@ -19,8 +21,8 @@
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
background: rgba(30,30,46,0.75);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(30, 30, 46, 0.75);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
color: #fff;
|
||||
@@ -29,27 +31,37 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.15);
|
||||
transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
outline: none;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
transform: scale(1.12) rotate(12deg);
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.theme-toggle:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
.theme-toggle.light {
|
||||
background: rgba(255,255,255,0.85);
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
color: #333;
|
||||
border-color: rgba(0,0,0,0.08);
|
||||
border-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
html, body, .sidebar, .content, .markdown-section,
|
||||
.sidebar-nav, .search, .docsify-pagination-container {
|
||||
|
||||
html,
|
||||
body,
|
||||
.sidebar,
|
||||
.content,
|
||||
.markdown-section,
|
||||
.sidebar-nav,
|
||||
.search,
|
||||
.docsify-pagination-container {
|
||||
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
</style>
|
||||
@@ -61,12 +73,13 @@
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme">🌙</button>
|
||||
</body>
|
||||
<script>
|
||||
(function() {
|
||||
(function () {
|
||||
var STORAGE_KEY = 'docsify-theme';
|
||||
var link = document.getElementById('theme-css');
|
||||
var btn = document.getElementById('themeToggle');
|
||||
@@ -95,12 +108,12 @@
|
||||
btn.addEventListener('click', toggleTheme);
|
||||
|
||||
try {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (e) {
|
||||
if (!localStorage.getItem(STORAGE_KEY)) {
|
||||
setTheme(e.matches ? 'dark' : 'light');
|
||||
}
|
||||
});
|
||||
} catch(e) {}
|
||||
} catch (e) { }
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
@@ -108,7 +121,7 @@
|
||||
name: '网易云音乐 API Enhanced',
|
||||
repo: 'https://github.com/neteasecloudmusicapienhanced/api-enhanced',
|
||||
coverpage: true,
|
||||
homepage: 'home.md',
|
||||
homepage: 'https://cdn.jsdelivr.net/gh/NeteaseCloudMusicApiEnhanced/api-enhanced@main/public/docs/home.md',
|
||||
}
|
||||
</script>
|
||||
<script src="https://unpkg.com/docsify@4.11.3/lib/docsify.min.js"></script>
|
||||
@@ -126,4 +139,5 @@
|
||||
|
||||
gtag('config', 'G-BPRGR23JEG');
|
||||
</script>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
Vendored
-2564
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const tmpPath = require('os').tmpdir()
|
||||
const { cookieToJson } = require('./util')
|
||||
|
||||
const anonymousTokenPath = path.resolve(tmpPath, 'anonymous_token')
|
||||
if (!fs.existsSync(anonymousTokenPath)) {
|
||||
fs.writeFileSync(anonymousTokenPath, '', 'utf-8')
|
||||
}
|
||||
|
||||
/** @type {Record<string, any>} */
|
||||
let obj = {}
|
||||
|
||||
const modulePath = path.join(__dirname, 'module')
|
||||
const moduleFiles = fs.readdirSync(modulePath).reverse()
|
||||
|
||||
let requestModule = null
|
||||
|
||||
moduleFiles.forEach((file) => {
|
||||
if (!file.endsWith('.js')) return
|
||||
|
||||
const filePath = path.join(modulePath, file)
|
||||
let fileModule = require(filePath)
|
||||
let fn = file.split('.').shift() || ''
|
||||
|
||||
obj[fn] = function (data = {}) {
|
||||
const cookie =
|
||||
typeof data.cookie === 'string'
|
||||
? cookieToJson(data.cookie)
|
||||
: data.cookie || {}
|
||||
|
||||
return fileModule(
|
||||
{
|
||||
...data,
|
||||
cookie,
|
||||
},
|
||||
async (...args) => {
|
||||
if (!requestModule) {
|
||||
requestModule = require('./util/request')
|
||||
}
|
||||
|
||||
return requestModule(...args)
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
let serverModule = null
|
||||
|
||||
/**
|
||||
* @type {Record<string, any> & import("./server")}
|
||||
*/
|
||||
module.exports = {
|
||||
get server() {
|
||||
if (!serverModule) {
|
||||
serverModule = require('./server')
|
||||
}
|
||||
return serverModule
|
||||
},
|
||||
...obj,
|
||||
}
|
||||
|
||||
Object.assign(module.exports, require('./server'))
|
||||
@@ -1,18 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const main = require('./main')
|
||||
|
||||
describe('methods in server.js', () => {
|
||||
it('has serveNcmApi', () => {
|
||||
assert.strictEqual(typeof main.serveNcmApi, 'function')
|
||||
})
|
||||
|
||||
it('has getModulesDefinitions', () => {
|
||||
assert.strictEqual(typeof main.getModulesDefinitions, 'function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('methods in module', () => {
|
||||
it('has activate_init_profile', () => {
|
||||
assert.strictEqual(typeof main.activate_init_profile, 'function')
|
||||
})
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
// 初始化名字
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
nickname: query.nickname,
|
||||
}
|
||||
return request(`/api/activate/initProfile`, data, createOption(query))
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// 私人 DJ
|
||||
|
||||
// 实际请求参数如下, 部分内容省略, 敏感信息已进行混淆
|
||||
// 可按需修改此 API 的代码
|
||||
/* {"extInfo":"{\"lastRequestTimestamp\":1692358373509,\"lbsInfoList\":[{\"lat\":40.23076381,\"lon\":129.07545186,\"time\":1692358543},{\"lat\":40.23076381,\"lon\":129.07545186,\"time\":1692055283}],\"listenedTs\":false,\"noAidjToAidj\":true}","header":"{}"} */
|
||||
|
||||
const logger = require('../util/logger.js')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
var extInfo = {}
|
||||
if (query.latitude != undefined) {
|
||||
extInfo.lbsInfoList = [
|
||||
{
|
||||
lat: query.latitude,
|
||||
lon: query.longitude,
|
||||
time: Date.parse(new Date()) / 1000,
|
||||
},
|
||||
]
|
||||
}
|
||||
extInfo.noAidjToAidj = false
|
||||
extInfo.lastRequestTimestamp = new Date().getTime()
|
||||
extInfo.listenedTs = false
|
||||
const data = {
|
||||
extInfo: JSON.stringify(extInfo),
|
||||
}
|
||||
// logger.info(data)
|
||||
return request(`/api/aidj/content/rcmd/info`, data, createOption(query))
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// 专辑内容
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
return request(`/api/v1/album/${query.id}`, {}, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 数字专辑详情
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(
|
||||
`/api/vipmall/albumproduct/detail`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 专辑动态信息
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(
|
||||
`/api/album/detail/dynamic`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 数字专辑-新碟上架
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 30,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
area: query.area || 'ALL', //ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本
|
||||
type: query.type,
|
||||
}
|
||||
return request(
|
||||
`/api/vipmall/albumproduct/list`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// 数字专辑-语种风格馆
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 10,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
area: query.area || 'Z_H', //Z_H:华语,E_A:欧美,KR:韩国,JP:日本
|
||||
}
|
||||
return request(
|
||||
`/api/vipmall/appalbum/album/style`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 全部新碟
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 30,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
area: query.area || 'ALL', //ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本
|
||||
}
|
||||
return request(`/api/album/new`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// 最新专辑
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
return request(`/api/discovery/newAlbum`, {}, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// 获取专辑歌曲的音质
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(`/api/album/privilege`, data, createOption(query))
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// 数字专辑&数字单曲-榜单
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
let data = {
|
||||
albumType: query.albumType || 0, //0为数字专辑,1为数字单曲
|
||||
}
|
||||
const type = query.type || 'daily' // daily,week,year,total
|
||||
if (type === 'year') {
|
||||
data = {
|
||||
...data,
|
||||
year: query.year,
|
||||
}
|
||||
}
|
||||
return request(
|
||||
`/api/feealbum/songsaleboard/${type}/type`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// 收藏/取消收藏专辑
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.t = query.t == 1 ? 'sub' : 'unsub'
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(`/api/album/${query.t}`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 已收藏专辑列表
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 25,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
}
|
||||
return request(`/api/album/sublist`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
const { cookieToJson } = require('../util/index')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const uri = query.uri
|
||||
let data = {}
|
||||
try {
|
||||
data =
|
||||
typeof query.data === 'string' ? JSON.parse(query.data) : query.data || {}
|
||||
if (typeof data.cookie === 'string') {
|
||||
data.cookie = cookieToJson(data.cookie)
|
||||
query.cookie = data.cookie
|
||||
}
|
||||
} catch (e) {
|
||||
data = {}
|
||||
}
|
||||
|
||||
const crypto = query.crypto || ''
|
||||
|
||||
const res = request(uri, data, createOption(query, crypto))
|
||||
return res
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// 歌手专辑列表
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 30,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
}
|
||||
return request(
|
||||
`/api/artist/albums/${query.id}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// 歌手介绍
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(`/api/artist/introduction`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
return request(
|
||||
`/api/artist/head/info/get`,
|
||||
{
|
||||
id: query.id,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// 歌手动态信息
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(`/api/artist/detail/dynamic`, data, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 歌手粉丝
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
limit: query.limit || 20,
|
||||
offset: query.offset || 0,
|
||||
}
|
||||
return request(`/api/artist/fans/get`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// 歌手粉丝数量
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(
|
||||
`/api/artist/follow/count/get`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// 歌手分类
|
||||
|
||||
/*
|
||||
type 取值
|
||||
1:男歌手
|
||||
2:女歌手
|
||||
3:乐队
|
||||
|
||||
area 取值
|
||||
-1:全部
|
||||
7华语
|
||||
96欧美
|
||||
8:日本
|
||||
16韩国
|
||||
0:其他
|
||||
|
||||
initial 取值 a-z/A-Z
|
||||
*/
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
initial: isNaN(query.initial)
|
||||
? (query.initial || '').toUpperCase().charCodeAt() || undefined
|
||||
: query.initial,
|
||||
offset: query.offset || 0,
|
||||
limit: query.limit || 30,
|
||||
total: true,
|
||||
type: query.type || '1',
|
||||
area: query.area,
|
||||
}
|
||||
return request(`/api/v1/artist/list`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 歌手相关MV
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
artistId: query.id,
|
||||
limit: query.limit,
|
||||
offset: query.offset,
|
||||
total: true,
|
||||
}
|
||||
return request(`/api/artist/mvs`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 20,
|
||||
startTimestamp: query.before || Date.now(),
|
||||
}
|
||||
return request(
|
||||
`/api/sub/artist/new/works/mv/list`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 20,
|
||||
startTimestamp: query.before || Date.now(),
|
||||
}
|
||||
return request(
|
||||
`/api/sub/artist/new/works/song/list`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
private_cloud: 'true',
|
||||
work_type: 1,
|
||||
order: query.order || 'hot', //hot,time
|
||||
offset: query.offset || 0,
|
||||
limit: query.limit || 100,
|
||||
}
|
||||
return request(`/api/v1/artist/songs`, data, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 收藏与取消收藏歌手
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.t = query.t == 1 ? 'sub' : 'unsub'
|
||||
const data = {
|
||||
artistId: query.id,
|
||||
artistIds: '[' + query.id + ']',
|
||||
}
|
||||
return request(`/api/artist/${query.t}`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 关注歌手列表
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 25,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
}
|
||||
return request(`/api/artist/sublist`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// 歌手热门 50 首歌曲
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
id: query.id,
|
||||
}
|
||||
return request(`/api/artist/top/song`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// 歌手相关视频
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
artistId: query.id,
|
||||
page: JSON.stringify({
|
||||
size: query.size || 10,
|
||||
cursor: query.cursor || 0,
|
||||
}),
|
||||
tab: 0,
|
||||
order: query.order || 0,
|
||||
}
|
||||
return request(`/api/mlog/artist/video`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// 歌手单曲
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
return request(`/api/v1/artist/${query.id}`, {}, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
const { default: axios } = require('axios')
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = async (query, request) => {
|
||||
const res = await axios({
|
||||
method: 'get',
|
||||
url: `https://interface.music.163.com/api/music/audio/match?sessionId=0123456789abcdef&algorithmCode=shazam_v2&duration=${
|
||||
query.duration
|
||||
}&rawdata=${encodeURIComponent(query.audioFP)}×=1&decrypt=1`,
|
||||
data: null,
|
||||
})
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: res.data.data,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
const uploadPlugin = require('../plugins/upload')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = async (query, request) => {
|
||||
const uploadInfo = await uploadPlugin(query, request)
|
||||
const res = await request(
|
||||
`/api/user/avatar/upload/v1`,
|
||||
{
|
||||
imgid: uploadInfo.imgId,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
...uploadInfo,
|
||||
...res.body,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 首页轮播图
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const type =
|
||||
{
|
||||
0: 'pc',
|
||||
1: 'android',
|
||||
2: 'iphone',
|
||||
3: 'ipad',
|
||||
}[query.type || 0] || 'pc'
|
||||
return request(
|
||||
`/api/v2/banner/get`,
|
||||
{ clientType: type },
|
||||
createOption(query),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 批量请求接口
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {}
|
||||
Object.keys(query).forEach((i) => {
|
||||
if (/^\/api\//.test(i)) {
|
||||
data[i] = query[i]
|
||||
}
|
||||
})
|
||||
return request(`/api/batch`, data, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 广播电台 - 分类/地区信息
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {}
|
||||
return request(
|
||||
`/api/voice/broadcast/category/region/get`,
|
||||
data,
|
||||
createOption(query),
|
||||
)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 广播电台 - 我的收藏
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
contentType: 'BROADCAST',
|
||||
limit: query.limit || '99999',
|
||||
timeReverseOrder: 'true',
|
||||
startDate: '4762584922000',
|
||||
}
|
||||
return request(`/api/content/channel/collect/list`, data, createOption(query))
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// 广播电台 - 电台信息
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
channelId: query.id,
|
||||
}
|
||||
return request(
|
||||
`/api/voice/broadcast/channel/currentinfo`,
|
||||
data,
|
||||
createOption(query),
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// 广播电台 - 全部电台
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
categoryId: query.categoryId || '0',
|
||||
regionId: query.regionId || '0',
|
||||
limit: query.limit || '20',
|
||||
lastId: query.lastId || '0',
|
||||
score: query.score || '-1',
|
||||
}
|
||||
return request(`/api/voice/broadcast/channel/list`, data, createOption(query))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// 广播电台 - 收藏/取消收藏电台
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.t = query.t == 1 ? 'false' : 'true'
|
||||
const data = {
|
||||
contentType: 'BROADCAST',
|
||||
contentId: query.id,
|
||||
cancelCollect: query.t,
|
||||
}
|
||||
return request(`/api/content/interact/collect`, data, createOption(query))
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
startTime: query.startTime || Date.now(),
|
||||
endTime: query.endTime || Date.now(),
|
||||
}
|
||||
return request(`/api/mcalendar/detail`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 发送验证码
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
ctcode: query.ctcode || '86',
|
||||
secrete: 'music_middleuser_pclogin',
|
||||
cellphone: query.phone,
|
||||
}
|
||||
return request(`/api/sms/captcha/sent`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 校验验证码
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
ctcode: query.ctcode || '86',
|
||||
cellphone: query.phone,
|
||||
captcha: query.captcha,
|
||||
}
|
||||
return request(`/api/sms/captcha/verify`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// 检测手机号码是否已注册
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
cellphone: query.phone,
|
||||
countrycode: query.countrycode,
|
||||
}
|
||||
return request(`/api/cellphone/existence/check`, data, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 获取指定维度音乐排行榜详情
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
chartCode: query.chartCode,
|
||||
targetId: query.targetId,
|
||||
targetType: query.targetType,
|
||||
}
|
||||
return request(`/api/chart/detail`, data, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 获取指定维度音乐排行榜列表
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
chartCode: query.chartCode,
|
||||
targetId: query.targetId,
|
||||
targetType: query.targetType,
|
||||
}
|
||||
return request(`/api/chart/song/detail`, data, createOption(query))
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// 歌曲可用性
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
ids: '[' + parseInt(query.id) + ']',
|
||||
br: parseInt(query.br || 999000),
|
||||
}
|
||||
return request(
|
||||
`/api/song/enhance/player/url`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
).then((response) => {
|
||||
let playable = false
|
||||
if (response.body.code == 200) {
|
||||
if (response.body.data[0].code == 200) {
|
||||
playable = true
|
||||
}
|
||||
}
|
||||
if (playable) {
|
||||
response.body = { code: 200, success: true, message: 'ok' }
|
||||
return response
|
||||
} else {
|
||||
// response.status = 404
|
||||
response.body = { code: 200, success: false, message: '亲爱的,暂无版权' }
|
||||
return response
|
||||
// return Promise.reject(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
const uploadPlugin = require('../plugins/songUpload')
|
||||
const createOption = require('../util/option.js')
|
||||
const logger = require('../util/logger.js')
|
||||
const {
|
||||
isTempFile,
|
||||
getFileSize,
|
||||
getFileMd5,
|
||||
cleanupTempFile,
|
||||
getFileExtension,
|
||||
sanitizeFilename,
|
||||
} = require('../util/fileHelper')
|
||||
|
||||
let mm
|
||||
module.exports = async (query, request) => {
|
||||
mm = require('music-metadata')
|
||||
|
||||
query.songFile.name = Buffer.from(query.songFile.name, 'latin1').toString(
|
||||
'utf-8',
|
||||
)
|
||||
const ext = getFileExtension(query.songFile.name)
|
||||
const filename = sanitizeFilename(query.songFile.name)
|
||||
const bitrate = 999000
|
||||
|
||||
if (!query.songFile) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
body: {
|
||||
msg: '请上传音乐文件',
|
||||
code: 500,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const useTemp = isTempFile(query.songFile)
|
||||
let fileSize = await getFileSize(query.songFile)
|
||||
let fileMd5 = await getFileMd5(query.songFile)
|
||||
|
||||
query.songFile.md5 = fileMd5
|
||||
query.songFile.size = fileSize
|
||||
|
||||
try {
|
||||
const res = await request(
|
||||
`/api/cloud/upload/check`,
|
||||
{
|
||||
bitrate: String(bitrate),
|
||||
ext: '',
|
||||
length: fileSize,
|
||||
md5: fileMd5,
|
||||
songId: '0',
|
||||
version: 1,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
let artist = ''
|
||||
let album = ''
|
||||
let songName = ''
|
||||
|
||||
try {
|
||||
let metadata
|
||||
if (useTemp) {
|
||||
metadata = await mm.parseFile(query.songFile.tempFilePath)
|
||||
} else {
|
||||
metadata = await mm.parseBuffer(
|
||||
query.songFile.data,
|
||||
query.songFile.mimetype,
|
||||
)
|
||||
}
|
||||
const info = metadata.common
|
||||
if (info.title) songName = info.title
|
||||
if (info.album) album = info.album
|
||||
if (info.artist) artist = info.artist
|
||||
} catch (error) {
|
||||
logger.info('元数据解析错误:', error.message)
|
||||
}
|
||||
|
||||
const tokenRes = await request(
|
||||
`/api/nos/token/alloc`,
|
||||
{
|
||||
bucket: '',
|
||||
ext: ext,
|
||||
filename: filename,
|
||||
local: false,
|
||||
nos_product: 3,
|
||||
type: 'audio',
|
||||
md5: fileMd5,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
if (!tokenRes.body.result || !tokenRes.body.result.resourceId) {
|
||||
logger.error('Token分配失败:', tokenRes.body)
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
body: {
|
||||
code: 500,
|
||||
msg: '获取上传token失败',
|
||||
detail: tokenRes.body,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (res.body.needUpload) {
|
||||
logger.info('需要上传,开始上传流程...')
|
||||
try {
|
||||
const uploadInfo = await uploadPlugin(query, request)
|
||||
logger.info('上传完成:', uploadInfo?.body?.result?.resourceId)
|
||||
} catch (uploadError) {
|
||||
logger.error('上传失败:', uploadError)
|
||||
return Promise.reject(uploadError)
|
||||
}
|
||||
} else {
|
||||
logger.info('文件已存在,跳过上传')
|
||||
}
|
||||
|
||||
const res2 = await request(
|
||||
`/api/upload/cloud/info/v2`,
|
||||
{
|
||||
md5: fileMd5,
|
||||
songid: res.body.songId,
|
||||
filename: query.songFile.name,
|
||||
song: songName || filename,
|
||||
album: album || '未知专辑',
|
||||
artist: artist || '未知艺术家',
|
||||
bitrate: String(bitrate),
|
||||
resourceId: tokenRes.body.result.resourceId,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
if (res2.body.code !== 200) {
|
||||
logger.error('云盘信息上传失败:', res2.body)
|
||||
return Promise.reject({
|
||||
status: res2.status || 500,
|
||||
body: {
|
||||
code: res2.body.code || 500,
|
||||
msg: res2.body.msg || '上传云盘信息失败',
|
||||
detail: res2.body,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const res3 = await request(
|
||||
`/api/cloud/pub/v2`,
|
||||
{
|
||||
songid: res2.body.songId,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
...res.body,
|
||||
...res3.body,
|
||||
},
|
||||
cookie: res.cookie,
|
||||
}
|
||||
} finally {
|
||||
if (useTemp) {
|
||||
await cleanupTempFile(query.songFile.tempFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// 云盘导入歌曲
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = async (query, request) => {
|
||||
query.id = query.id || -2
|
||||
query.artist = query.artist || '未知'
|
||||
query.album = query.album || '未知'
|
||||
const checkData = {
|
||||
uploadType: 0,
|
||||
songs: JSON.stringify([
|
||||
{
|
||||
md5: query.md5,
|
||||
songId: query.id,
|
||||
bitrate: query.bitrate,
|
||||
fileSize: query.fileSize,
|
||||
},
|
||||
]),
|
||||
}
|
||||
const res = await request(
|
||||
`/api/cloud/upload/check/v2`,
|
||||
checkData,
|
||||
createOption(query),
|
||||
)
|
||||
//res.body.data[0].upload 0:文件可导入,1:文件已在云盘,2:不能导入
|
||||
//只能用song决定云盘文件名,且上传后的文件名后缀固定为mp3
|
||||
const importData = {
|
||||
uploadType: 0,
|
||||
songs: JSON.stringify([
|
||||
{
|
||||
songId: res.body.data[0].songId,
|
||||
bitrate: query.bitrate,
|
||||
song: query.song,
|
||||
artist: query.artist,
|
||||
album: query.album,
|
||||
fileName: query.song + '.' + query.fileType,
|
||||
},
|
||||
]),
|
||||
}
|
||||
return request(`/api/cloud/user/song/import`, importData, createOption(query))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 获取云盘歌词
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
userId: query.uid,
|
||||
songId: query.sid,
|
||||
lv: -1,
|
||||
kv: -1,
|
||||
}
|
||||
return request(`/api/cloud/lyric/get`, data, createOption(query, 'eapi'))
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
userId: query.uid,
|
||||
songId: query.sid,
|
||||
adjustSongId: query.asid,
|
||||
}
|
||||
return request(
|
||||
`/api/cloud/user/song/match`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
const {
|
||||
songId,
|
||||
resourceId,
|
||||
md5,
|
||||
filename,
|
||||
song,
|
||||
artist,
|
||||
album,
|
||||
bitrate = 999000,
|
||||
} = query
|
||||
|
||||
if (!songId || !resourceId || !md5 || !filename) {
|
||||
return Promise.reject({
|
||||
status: 400,
|
||||
body: {
|
||||
code: 400,
|
||||
msg: '缺少必要参数: songId, resourceId, md5, filename',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const songName = song || filename.replace(/\.[^.]+$/, '')
|
||||
|
||||
const res2 = await request(
|
||||
`/api/upload/cloud/info/v2`,
|
||||
{
|
||||
md5: md5,
|
||||
songid: songId,
|
||||
filename: filename,
|
||||
song: songName,
|
||||
album: album || '未知专辑',
|
||||
artist: artist || '未知艺术家',
|
||||
bitrate: String(bitrate),
|
||||
resourceId: resourceId,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
if (res2.body.code !== 200) {
|
||||
return Promise.reject({
|
||||
status: res2.status || 500,
|
||||
body: {
|
||||
code: res2.body.code || 500,
|
||||
msg: res2.body.msg || '上传云盘信息失败',
|
||||
detail: res2.body,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const res3 = await request(
|
||||
`/api/cloud/pub/v2`,
|
||||
{
|
||||
songid: res2.body.songId,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
songId: res2.body.songId,
|
||||
...res3.body,
|
||||
},
|
||||
},
|
||||
cookie: res2.cookie,
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
const { default: axios } = require('axios')
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
const { md5, fileSize, filename, bitrate = 999000 } = query
|
||||
|
||||
if (!md5 || !fileSize || !filename) {
|
||||
return Promise.reject({
|
||||
status: 400,
|
||||
body: {
|
||||
code: 400,
|
||||
msg: '缺少必要参数: md5, fileSize, filename',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const ext = filename.includes('.') ? filename.split('.').pop() : 'mp3'
|
||||
|
||||
const checkRes = await request(
|
||||
`/api/cloud/upload/check`,
|
||||
{
|
||||
bitrate: String(bitrate),
|
||||
ext: '',
|
||||
length: fileSize,
|
||||
md5: md5,
|
||||
songId: '0',
|
||||
version: 1,
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
|
||||
const bucket = 'jd-musicrep-privatecloud-audio-public'
|
||||
const tokenRes = await request(
|
||||
`/api/nos/token/alloc`,
|
||||
{
|
||||
bucket: bucket,
|
||||
ext: ext,
|
||||
filename: filename
|
||||
.replace(/\.[^.]+$/, '')
|
||||
.replace(/\s/g, '')
|
||||
.replace(/\./g, '_'),
|
||||
local: false,
|
||||
nos_product: 3,
|
||||
type: 'audio',
|
||||
md5: md5,
|
||||
},
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
|
||||
if (!tokenRes.body.result || !tokenRes.body.result.objectKey) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
body: {
|
||||
code: 500,
|
||||
msg: '获取上传token失败',
|
||||
detail: tokenRes.body,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let lbs
|
||||
try {
|
||||
lbs = (
|
||||
await axios({
|
||||
method: 'get',
|
||||
url: `https://wanproxy.127.net/lbs?version=1.0&bucketname=${bucket}`,
|
||||
timeout: 10000,
|
||||
})
|
||||
).data
|
||||
} catch (error) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
body: {
|
||||
code: 500,
|
||||
msg: '获取上传服务器地址失败',
|
||||
detail: error.message,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!lbs || !lbs.upload || !lbs.upload[0]) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
body: {
|
||||
code: 500,
|
||||
msg: '获取上传服务器地址无效',
|
||||
detail: lbs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
needUpload: checkRes.body.needUpload,
|
||||
songId: checkRes.body.songId,
|
||||
uploadToken: tokenRes.body.result.token,
|
||||
objectKey: tokenRes.body.result.objectKey,
|
||||
resourceId: tokenRes.body.result.resourceId,
|
||||
uploadUrl: `${lbs.upload[0]}/${bucket}/${tokenRes.body.result.objectKey.replace(/\//g, '%2F')}?offset=0&complete=true&version=1.0`,
|
||||
bucket: bucket,
|
||||
md5: md5,
|
||||
fileSize: fileSize,
|
||||
filename: filename,
|
||||
},
|
||||
},
|
||||
cookie: checkRes.cookie,
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// 搜索
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
s: query.keywords,
|
||||
type: query.type || 1, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频
|
||||
limit: query.limit || 30,
|
||||
offset: query.offset || 0,
|
||||
total: true,
|
||||
}
|
||||
return request(`/api/cloudsearch/pc`, data, createOption(query))
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
const { resourceTypeMap } = require('../util/config.json')
|
||||
// 发送与删除评论
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.t = {
|
||||
1: 'add',
|
||||
0: 'delete',
|
||||
2: 'reply',
|
||||
}[query.t]
|
||||
query.type = resourceTypeMap[query.type]
|
||||
const data = {
|
||||
threadId: query.type + query.id,
|
||||
}
|
||||
|
||||
if (query.type == 'A_EV_2_') {
|
||||
data.threadId = query.threadId
|
||||
}
|
||||
if (query.t == 'add') data.content = query.content
|
||||
else if (query.t == 'delete') data.commentId = query.commentId
|
||||
else if (query.t == 'reply') {
|
||||
data.commentId = query.commentId
|
||||
data.content = query.content
|
||||
}
|
||||
return request(
|
||||
`/api/resource/comments/${query.t}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 专辑评论
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
rid: query.id,
|
||||
limit: query.limit || 20,
|
||||
offset: query.offset || 0,
|
||||
beforeTime: query.before || 0,
|
||||
}
|
||||
return request(
|
||||
`/api/v1/resource/comments/R_AL_3_${query.id}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 电台评论
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
rid: query.id,
|
||||
limit: query.limit || 20,
|
||||
offset: query.offset || 0,
|
||||
beforeTime: query.before || 0,
|
||||
}
|
||||
return request(
|
||||
`/api/v1/resource/comments/A_DJ_1_${query.id}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// 获取动态评论
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
limit: query.limit || 20,
|
||||
offset: query.offset || 0,
|
||||
beforeTime: query.before || 0,
|
||||
}
|
||||
return request(
|
||||
`/api/v1/resource/comments/${query.threadId}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
const { resourceTypeMap } = require('../util/config.json')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.type = resourceTypeMap[query.type]
|
||||
const data = {
|
||||
parentCommentId: query.parentCommentId,
|
||||
threadId: query.type + query.id,
|
||||
time: query.time || -1,
|
||||
limit: query.limit || 20,
|
||||
}
|
||||
return request(
|
||||
`/api/resource/comment/floor/get`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
const { resourceTypeMap } = require('../util/config.json')
|
||||
// 热门评论
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.type = resourceTypeMap[query.type]
|
||||
const data = {
|
||||
rid: query.id,
|
||||
limit: query.limit || 20,
|
||||
offset: query.offset || 0,
|
||||
beforeTime: query.before || 0,
|
||||
}
|
||||
return request(
|
||||
`/api/v1/resource/hotcomments/${query.type}${query.id}`,
|
||||
data,
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
const { resourceTypeMap } = require('../util/config.json')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
query.type = resourceTypeMap[query.type || 0]
|
||||
const threadId = query.type + query.sid
|
||||
const data = {
|
||||
targetUserId: query.uid,
|
||||
commentId: query.cid,
|
||||
cursor: query.cursor || '-1',
|
||||
threadId: threadId,
|
||||
pageNo: query.page || 1,
|
||||
idCursor: query.idCursor || -1,
|
||||
pageSize: query.pageSize || 100,
|
||||
}
|
||||
return request(
|
||||
`/api/v2/resource/comments/hug/list`,
|
||||
data,
|
||||
createOption(query),
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// 评论统计数据
|
||||
// type: 0=歌曲 1=MV 2=歌单 3=专辑 4=电台节目 5=视频 6=动态 7=电台
|
||||
// ids: 资源 ID 列表,多个用逗号分隔,如 "123,456"
|
||||
const { resourceTypeMap } = require('../util/config.json')
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
// 从 resourceTypeMap 的前缀值中提取网易云内部资源类型编号
|
||||
// 例如 "R_SO_4_" -> "4", "A_DR_14_" -> "14"
|
||||
const resourceTypeIdMap = Object.fromEntries(
|
||||
Object.entries(resourceTypeMap).map(([key, prefix]) => [
|
||||
key,
|
||||
prefix.replace(/_$/, '').split('_').pop(),
|
||||
]),
|
||||
)
|
||||
|
||||
module.exports = (query, request) => {
|
||||
const ids = String(query.ids || query.id || '')
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
return request(
|
||||
`/api/resource/commentInfo/list`,
|
||||
{
|
||||
resourceType: resourceTypeIdMap[String(query.type || 0)],
|
||||
resourceIds: JSON.stringify(ids),
|
||||
},
|
||||
createOption(query, 'weapi'),
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user