高颜值的第三方网易云播放器YesPlayMusic(续)

“把麻烦留给自己,把方便留给别人”,这才是我们折腾的终极目标。在上一篇中 YesPlayMusic 虽然能用,但是需要安装两个镜像,总归还是不算方便,更何况还有不完美的公网访问。经过老苏的不懈努力,v2 版实现了将播放器和 API 集成在一个镜像中,成功解决了:

  • v1 版不能刷新页面的问题
  • 公网反代的问题
  • 可以连接到 Last.me
  • 支持上传云盘

构建镜像

如果你不想自己构建,可以跳过,直接阅读下一章节

折腾 bypy 时学习的 supervisor 让老苏有了新的想法,那就是把前端播放器、后端 APInginx 整到一个容器中,把群晖上没完成的事情尝试在容器中搞定,所以就有了本文

  • 前端继续还是采用占位符的方式,老苏还是保留了变量,原本是可以将 VUE_APP_NETEASE_API_URL 写死为 /api
  • 参考了 网易 API 官方的 Dockerfile ,所以没有像第一版使用 nginx:1.12-alpine 做基础镜像,而是用了 node:lts-alpine,并在其中安装了一个 nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
FROM node:16.5 as build-deps
LABEL maintainer=laosu<wbsu2003@gmail.com>

# environment
ENV VUE_APP_NETEASE_API_URL netease_vanau
ENV VUE_APP_ELECTRON_API_URL netease_vaeaux
ENV VUE_APP_ELECTRON_API_URL_DEV netease_vaeaud
ENV VUE_APP_LASTFM_API_KEY netease_valak
ENV VUE_APP_LASTFM_API_SHARED_SECRET netease_valass
ENV DEV_SERVER_PORT netease_dsp

WORKDIR /app
#COPY package.json yarn.lock ./
#RUN yarn
RUN TMPDIR=/tmp yarn install
#RUN yarn config set registry "https://registry.npm.taobao.org/" \
# && TMPDIR=/tmp yarn install
COPY . ./
RUN yarn build

FROM node:lts-alpine
RUN apk update \
&& apk add --no-cache supervisor openssh nginx
COPY --from=build-deps /app/dist /usr/share/nginx/html
COPY --from=build-deps /app/supervisord.conf /etc/supervisord.conf

# nginx
COPY --from=build-deps /app/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build-deps /app/nginx.conf /etc/nginx/nginx.conf
RUN mkdir -p /usr/local/nginx/logs

# api server
WORKDIR /api
COPY --from=build-deps /app/netease_api/. /api
COPY --from=build-deps /app/replace_api_url.sh /api

RUN chmod +x replace_api_url.sh
RUN npm config set registry "https://registry.npm.taobao.org/" \
&& npm install --production

# CMD ["sh", "replace_api_url.sh"]
ENTRYPOINT ["supervisord","-c","/etc/supervisord.conf"]

EXPOSE 80

replace_api_url.sh 用于替换 ENV 设置的占位符

这个和第一版是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env sh

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_vanau,'"$VUE_APP_NETEASE_API_URL"',g' {} \;

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_vaeaux,'"$VUE_APP_ELECTRON_API_URL"',g' {} \;

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_vaeaud,'"$VUE_APP_ELECTRON_API_URL_DEV"',g' {} \;

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_valak,'"$VUE_APP_LASTFM_API_KEY"',g' {} \;

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_valass,'"$VUE_APP_LASTFM_API_SHARED_SECRET"',g' {} \;

find '/usr/share/nginx/html' -name '*.js' -exec sed -i -e 's,netease_dsp,'"$DEV_SERVER_PORT"',g' {} \;

nginx -g "daemon off;"

supervisord.conf 文件是新增用来控制进程的,前端 app 是运行在 nginx 上的静态页面,后端 api 基于 node.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[include]
files = /etc/supervisor/conf.d/*.conf

[program:app]
command=./replace_api_url.sh

[program:api]
command=node app.js

#directory will be any folder where you wnat supervisor to cd before executing.
#directory=/project
autostart=true
autorestart=false
startretries=3

#user will be anyone you want but make sure that user will have the enough privilage.
user=root

[supervisord]
nodaemon=true
logfile=/tmp/supervisord.log
pidfile=/tmp/supervisord.pid
loglevel=debug
logfile_maxbytes=10MB

[supervisorctl]

nginx 设置文件 nginx.conf

镜像中的位置 /etc/nginx/nginx.conf,比默认的 nginx.conf 文件多一行 pid /usr/local/nginx/logs/nginx.pid;,如果没有会出现错误: nginx: [emerg] open() "/run/nginx/nginx.pid" failed (2: No such file or directory)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# /etc/nginx/nginx.conf

user nginx;

pid /usr/local/nginx/logs/nginx.pid;

# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;

# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;

# Configures default error logger.
error_log /var/log/nginx/error.log warn;

# Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf;


events {
# The maximum number of simultaneous connections that can be opened by
# a worker process.
worker_connections 1024;
}

http {
# Includes mapping of file name extensions to MIME types of responses
# and defines the default type.
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Name servers used to resolve names of upstream servers into addresses.
# It's also needed when using tcpsocket and udpsocket in Lua modules.
#resolver 208.67.222.222 208.67.220.220;

# Don't tell nginx version to clients.
server_tokens off;

# Specifies the maximum accepted body size of a client request, as
# indicated by the request header Content-Length. If the stated content
# length is greater than this size, then the client receives the HTTP
# error code 413. Set to 0 to disable.
client_max_body_size 1m;

# Timeout for keep-alive connections. Server will close connections after
# this time.
keepalive_timeout 65;

# Sendfile copies data between one FD and other from within the kernel,
# which is more efficient than read() + write().
sendfile on;

# Don't buffer data-sends (disable Nagle algorithm).
# Good for sending frequent small bursts of data in real time.
tcp_nodelay on;

# Causes nginx to attempt to send its HTTP response head in one packet,
# instead of using partial frames.
#tcp_nopush on;


# Path of the file with Diffie-Hellman parameters for EDH ciphers.
#ssl_dhparam /etc/ssl/nginx/dh2048.pem;

# Specifies that our cipher suits should be preferred over client ciphers.
ssl_prefer_server_ciphers on;

# Enables a shared SSL cache with size that can hold around 8000 sessions.
ssl_session_cache shared:SSL:2m;


# Enable gzipping of responses.
#gzip on;

# Set the Vary HTTP header as defined in the RFC 2616.
gzip_vary on;

# Enable checking the existence of precompressed files.
#gzip_static on;


# Specifies the main log format.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

# Sets the path, format, and configuration for a buffered log write.
access_log /var/log/nginx/access.log main;


# Includes virtual hosts configs.
include /etc/nginx/conf.d/*.conf;
}

nginx 设置文件 default.conf

镜像中的位置/etc/nginx/conf.d/default.conf,将 /api 转发给后端处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server
{
listen 80;
server_name default_serve; # 配置项目域名
root /usr/share/nginx/html;
index index.html index.htm;

# 1.转给前端处理
location / {
# 前端打包后的静态目录
alias /usr/share/nginx/html/;
# 解决页面刷新404问题
try_files $uri $uri/ /index.html;
# 处理上传文件大小
client_max_body_size 1000m;
}

# 2.转给后端处理
location ^~ /api/ {
proxy_pass http://127.0.0.1:3000/;
# 处理上传文件大小
client_max_body_size 1000m;
}
}

构建镜像和容器运行的基本命令如下👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 下载代码,当前对应的是 0.4.1
git clone https://github.com/qier222/YesPlayMusic.git

# 或者镜像站点
git clone https://hub.fastgit.org/qier222/YesPlayMusic.git

# 进入目录
cd YesPlayMusic

# 将 Dockerfile 、 replace_api_url.sh 、supervisord.conf、nginx.conf 、default.conf 五个文件放进代码目录中

# 构建镜像
docker build -t wbsu2003/yesplaymusic:v2 .

# 生成容器
docker run -d \
--name=yesplaymusic \
-p 3310:80 \
-e VUE_APP_NETEASE_API_URL=/api \
-e VUE_APP_LASTFM_API_KEY=<your Last.fm API KEY> \
-e VUE_APP_LASTFM_API_SHARED_SECRET=<your Last.fm API SHARED SECRET> \
wbsu2003/yesplaymusic:v2

# --------调试-------------
# 进入容器
docker exec --user root -it yesplaymusic /bin/sh

# 将生成的静态文件拷贝到容器外
docker cp yesplaymusic:/usr/share/nginx/html/. ./dist

# 将生成的静态文件拷贝到容器内
docker cp /dist/. yesplaymusic:/usr/share/nginx/html
# ------------------------

安装

注册表中搜索 wbsu2003 ,找到 wbsu2003/yesplaymusic,版本选 latest

如果你之前已经安装过 YesPlayMusic,建议先删除老版本的容器和镜像再进行安装

端口

端口不冲突就行

本地端口 容器端口
3310 80

环境

可变
VUE_APP_NETEASE_API_URL 使用 /api 不要修改
VUE_APP_LASTFM_API_KEY Last.fm 获取的 API Key
VUE_APP_LASTFM_API_SHARED_SECRET Last.fm 获取的 Shared Secret

如果你还没有 Last.fm 账号,可以去 https://www.last.fm/api/account/create 创建一个 API 帐户。

老苏没有填 Callback URL

如果你已经创建好了,需要查询可以去 https://www.last.fm/api/accounts 找到你的 API Applications

运行

在浏览器中输入 http://群晖IP:3310 就能看到主界面

第三方授权

设置 –> 第三方 –> 连接 Last.fm –> 授权连接

在跳转页面选择 YES ALLOW ACCESS 进行授权

授权之后会显示成功

完成,能看到连接的 Last.fm 账号

反代

反代已经没问题了

说明

文中提到的文件,都可以在 https://github.com/wbsu2003/Dockerfile 找到

个人精力有限,很难做全面的测试,如果你发现有什么问题,请反馈给老苏,反馈方式:

  • 给公众号发消息。虽然老苏的公众号并不支持留言,并且还有很多限制,但聊胜于无
  • 点『 阅读原文 』 去老苏的博客 (https://laosu.ml) 留言
  • 发邮件:wbsu2003@gmail.comwbsu2003@hotmail.com

参考文档

qier222/YesPlayMusic: 高颜值的第三方网易云播放器,支持 Windows / macOS / Linux
地址:https://github.com/qier222/YesPlayMusic

Binaryify/NeteaseCloudMusicApi: 网易云音乐 Node.js API service
地址:https://github.com/Binaryify/NeteaseCloudMusicApi

Bug. Yarn couldn’t build package phantomjs. npm could. · Issue #1016 · yarnpkg/yarn
地址:https://github.com/yarnpkg/yarn/issues/1016

单机,前后端分离,Nginx反向代理 - 简书
地址:https://www.jianshu.com/p/3c2c3f64f1ad

Nginx代理同域名前后端分离项目 | Laravel China 社区
地址:https://learnku.com/articles/50668