diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..fd24c66 --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/82c0147677e510b247d8b9165c54f73d32dfd899/direnvrc" "sha256-7u4iDd1nZpxL4tCzmPG0dQgC5V+/44Ba+tHkPob1v2k=" + +export NIXPKGS_ALLOW_INSECURE=1 +use devenv diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b1f90dd..4caf481 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,12 +1,15 @@ name: Release +env: + YARN_INSTALL_NOPT: yarn add --ignore-platform --ignore-optional + on: push: branches: - master - - "ci/*" tags: - v* + workflow_dispatch: jobs: release: @@ -14,25 +17,26 @@ jobs: strategy: matrix: - os: [macos-latest, windows-latest, ubuntu-18.04] + os: [macos-latest, windows-latest, ubuntu-22.04] steps: - name: Check out Git repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: "recursive" - name: Install Node.js, NPM and Yarn - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: - node-version: 14.16.0 + node-version: 16 + cache: 'yarn' - name: Install RPM & Pacman (on Ubuntu) if: runner.os == 'Linux' run: | sudo apt-get update && sudo apt-get install --no-install-recommends -y rpm && - sudo apt-get install --no-install-recommends -y bsdtar && + sudo apt-get install --no-install-recommends -y libarchive-tools && sudo apt-get install --no-install-recommends -y libopenjp2-tools - name: Install Snapcraft (on Ubuntu) @@ -41,9 +45,43 @@ jobs: with: snapcraft_token: ${{ secrets.snapcraft_token }} + - id: get_unm_version + name: Get the installed UNM version + run: | + yarn --ignore-optional + unm_version=$(node -e "console.log(require('./node_modules/@unblockneteasemusic/rust-napi/package.json').version)") + echo "::set-output name=unmver::${unm_version}" + shell: bash + + - name: Install UNM dependencies for Windows + if: runner.os == 'Windows' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-win32-x64-msvc@${{steps.get_unm_version.outputs.unmver}} + shell: bash + + - name: Install UNM dependencies for macOS + if: runner.os == 'macOS' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-darwin-x64@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-darwin-arm64@${{steps.get_unm_version.outputs.unmver}} \ + dmg-license + shell: bash + + - name: Install UNM dependencies for Linux + if: runner.os == 'Linux' + run: | + ${{ env.YARN_INSTALL_NOPT }} \ + @unblockneteasemusic/rust-napi-linux-x64-gnu@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-linux-arm64-gnu@${{steps.get_unm_version.outputs.unmver}} \ + @unblockneteasemusic/rust-napi-linux-arm-gnueabihf@${{steps.get_unm_version.outputs.unmver}} + shell: bash + - name: Build/release Electron app uses: samuelmeuli/action-electron-builder@v1.6.0 env: + VUE_APP_NETEASE_API_URL: /api VUE_APP_ELECTRON_API_URL: /api VUE_APP_ELECTRON_API_URL_DEV: http://127.0.0.1:10754 VUE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c @@ -59,19 +97,19 @@ jobs: use_vue_cli: true - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: YesPlayMusic-mac path: dist_electron/*-universal.dmg if-no-files-found: ignore - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: YesPlayMusic-win path: dist_electron/*Setup*.exe if-no-files-found: ignore - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: YesPlayMusic-linux path: dist_electron/*.AppImage diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000..f8e0be8 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,48 @@ +name: Upstream Sync + +permissions: + contents: write + issues: write + actions: write + +on: + schedule: + - cron: '0 * * * *' # every hour + workflow_dispatch: + +jobs: + sync_latest_from_upstream: + name: Sync latest commits from upstream repo + runs-on: ubuntu-latest + if: ${{ github.event.repository.fork }} + + steps: + - uses: actions/checkout@v4 + + - name: Clean issue notice + uses: actions-cool/issues-helper@v3 + with: + actions: 'close-issues' + labels: '🚨 Sync Fail' + + - name: Sync upstream changes + id: sync + uses: aormsby/Fork-Sync-With-Upstream-action@v3.4 + with: + upstream_sync_repo: qier222/YesPlayMusic + upstream_sync_branch: master + target_sync_branch: master + target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set + test_mode: false + + - name: Sync check + if: failure() + uses: actions-cool/issues-helper@v3 + with: + actions: 'create-issue' + title: '🚨 同步失败 | Sync Fail' + labels: '🚨 Sync Fail' + body: | + 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. + + 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次。 diff --git a/.gitignore b/.gitignore index b96fe0f..d8dd8fc 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,12 @@ NeteaseCloudMusicApi-master.zip # Local Netlify folder .netlify vercel.json +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 231b211..38800a3 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,7 +5,6 @@ "semi": true, "singleQuote": true, "jsxSingleQuote": true, - "jsxBracketSameLine": false, "arrowParens": "avoid", "endOfLine": "lf", "bracketSpacing": true, diff --git a/Dockerfile b/Dockerfile index 21db7b8..2fb8f95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,43 +1,25 @@ FROM node:16.13.1-alpine as build ENV VUE_APP_NETEASE_API_URL=/api WORKDIR /app -RUN apk add --no-cache python3 make g++ git +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\ + apk add --no-cache python3 make g++ git COPY package.json yarn.lock ./ RUN yarn install COPY . . -RUN yarn build +RUN yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ && \ + yarn build FROM nginx:1.20.2-alpine as app -RUN echo $'server { \n\ - gzip on;\n\ - listen 80; \n\ - listen [::]:80; \n\ - server_name localhost; \n\ - \n\ - location / { \n\ - root /usr/share/nginx/html; \n\ - index index.html; \n\ - try_files $uri $uri/ /index.html; \n\ - } \n\ - \n\ - location @rewrites { \n\ - rewrite ^(.*)$ /index.html last; \n\ - } \n\ - \n\ - location /api/ { \n\ - proxy_set_header Host $host; \n\ - proxy_set_header X-Real-IP $remote_addr; \n\ - proxy_set_header X-Forwarded-For $remote_addr; \n\ - proxy_set_header X-Forwarded-Host $remote_addr; \n\ - proxy_set_header X-NginX-Proxy true; \n\ - proxy_pass http://localhost:3000/; \n\ - } \n\ - }' > /etc/nginx/conf.d/default.conf -RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \ +COPY --from=build /app/package.json /usr/local/lib/ + +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories &&\ + apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \ && apk add --no-cache --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main nodejs npm \ - && npm i -g NeteaseCloudMusicApi + && npm i -g $(awk -F \" '{if($2=="NeteaseCloudMusicApi") print $2"@"$4}' /usr/local/lib/package.json) \ + && rm -f /usr/local/lib/package.json +COPY --from=build /app/docker/nginx.conf.example /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html -CMD nginx ; exec npx NeteaseCloudMusicApi +CMD nginx ; exec npx NeteaseCloudMusicApi \ No newline at end of file diff --git a/LICENSE b/LICENSE index d8911ac..7385fd2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 qier222 +Copyright (c) 2020-2023 qier222 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ 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. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 2ee5854..146f524 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

高颜值的第三方网易云播放器
- 🌎 访问DEMO  |   + 🌎 访问DEMO  |   📦️ 下载安装包  |   💬 加入交流群
@@ -16,7 +16,12 @@

-[![Library][library-screenshot]](https://music.qier222.com) +[![Library][library-screenshot]](https://music.ineko.cc) + + +## 全新版本 +全新2.0 Alpha测试版已发布,欢迎前往 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载。 +当前版本将会进入维护模式,除重大bug修复外,不会再更新新功能。 ## ✨ 特性 @@ -37,7 +42,7 @@ - 🟥 支持 Last.fm Scrobble - ☁️ 支持音乐云盘 - ⌨️ 自定义快捷键和全局快捷键 -- 🎧 支持Mpris +- 🎧 支持 Mpris - 🛠 更多特性开发中 ## 📦️ 安装 @@ -47,12 +52,18 @@ Electron 版本由 [@hawtim](https://github.com/hawtim) 和 [@qier222](https://g 访问本项目的 [Releases](https://github.com/qier222/YesPlayMusic/releases) 页面下载安装包。 -macOS 用户也可以通过 `brew install --cask yesplaymusic` 来安装。 +- macOS 用户可以通过 Homebrew 来安装:`brew install --cask yesplaymusic` + +- Windows 用户可以通过 Scoop 来安装:`scoop install extras/yesplaymusic` ## ⚙️ 部署至 Vercel 除了下载安装包使用,你还可以将本项目部署到 Vercel 或你的服务器上。下面是部署到 Vercel 的方法。 +本项目的 Demo (https://music.qier222.com) 就是部署在 Vercel 上的网站。 + +[![Powered by Vercel](https://www.datocms-assets.com/31049/1618983297-powered-by-vercel.svg)](https://vercel.com/?utm_source=ohmusic&utm_campaign=oss) + 1. 部署网易云 API,详情参见 [Binaryify/NeteaseCloudMusicApi](https://neteasecloudmusicapi.vercel.app/#/?id=%e5%ae%89%e8%a3%85) 。你也可以将 API 部署到 Vercel。 @@ -114,6 +125,16 @@ yarn run build 7. 将 `/dist` 目录下的文件上传到你的 Web 服务器 +## ⚙️ 宝塔面板 docker应用商店 部署 + +1. 安装宝塔面板,前往[宝塔面板官网](https://www.bt.cn/new/download.html) ,选择正式版的脚本下载安装。 + +2. 安装后登录宝塔面板,在左侧导航栏中点击 Docker,首次进入会提示安装Docker服务,点击立即安装,按提示完成安装 + +3. 安装完成后在应用商店中找到YesPlayMusic,点击安装,配置域名、端口等基本信息即可完成安装。 + +4. 安装后在浏览器输入上一步骤设置的域名即可访问。 + ## ⚙️ Docker 部署 1. 构建 Docker Image @@ -136,6 +157,24 @@ docker-compose up -d YesPlayMusic 地址为 `http://localhost` +## ⚙️ 部署至 Replit + +1. 新建 Repl,选择 Bash 模板 + +2. 在 Replit shell 中运行以下命令 + +```sh +bash <(curl -s -L https://raw.githubusercontent.com/qier222/YesPlayMusic/main/install-replit.sh) +``` + +3. 首次运行成功后,只需点击绿色按钮 `Run` 即可再次运行 + +4. 由于 replit 个人版限制内存为 1G(教育版为 3G),构建过程中可能会失败,请再次运行上述命令或运行以下命令: + +```sh +cd /home/runner/${REPL_SLUG}/music && yarn install && yarn run build +``` + ## 👷‍♂️ 打包客户端 如果在 Release 页面没有找到适合你的设备的安装包的话,你可以根据下面的步骤来打包自己的客户端。 diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..9250350 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,132 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1730412360, + "owner": "cachix", + "repo": "devenv", + "rev": "45847cb1f14a6d8cfa86ea943703c54a8798ae7e", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1730272153, + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1730327045, + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "080166c15633801df010977d9d7474b4a6c549d7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nodejs16": { + "locked": { + "lastModified": 1700230496, + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a71323f68d4377d12c04a5410e214495ec598d4c", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a71323f68d4377d12c04a5410e214495ec598d4c", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1730302582, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs", + "nodejs16": "nodejs16", + "pre-commit-hooks": "pre-commit-hooks" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..fd6b9ce --- /dev/null +++ b/devenv.nix @@ -0,0 +1,53 @@ +{ pkgs, lib, config, inputs, ... }: + +let + nodejs16 = import inputs.nodejs16 { system = pkgs.stdenv.system; }; +in +{ + # https://devenv.sh/basics/ + env.GREET = "devenv"; + + # https://devenv.sh/packages/ + packages = [ pkgs.git ] ++ lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk; [ + frameworks.AppKit + ]); + + # https://devenv.sh/languages/ + languages.javascript.enable = true; + languages.javascript.package = nodejs16.pkgs.nodejs_16; + languages.javascript.corepack.enable = true; + # languages.rust.enable = true; + + # https://devenv.sh/processes/ + # processes.cargo-watch.exec = "cargo-watch"; + + # https://devenv.sh/services/ + # services.postgres.enable = true; + + # https://devenv.sh/scripts/ + scripts.hello.exec = '' + echo hello from $GREET + ''; + + enterShell = '' + hello + git --version + ''; + + # https://devenv.sh/tasks/ + # tasks = { + # "myproj:setup".exec = "mytool build"; + # "devenv:enterShell".after = [ "myproj:setup" ]; + # }; + + # https://devenv.sh/tests/ + enterTest = '' + echo "Running tests" + git --version | grep --color=auto "${pkgs.git.version}" + ''; + + # https://devenv.sh/pre-commit-hooks/ + # pre-commit.hooks.shellcheck.enable = true; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..7019c5c --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:nixos/nixpkgs/nixpkgs-unstable + nodejs16: + url: github:nixos/nixpkgs/a71323f68d4377d12c04a5410e214495ec598d4c + +# https://github.com/cachix/devenv/issues/792#issuecomment-2043166453 +impure: true +# If you're using non-OSS software, you can set allowUnfree to true. +# allowUnfree: true + +# If you're willing to use a package that's vulnerable +# permittedInsecurePackages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend diff --git a/docker-compose.yml b/docker-compose.yml index efc2826..0930218 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,36 @@ services: context: . image: yesplaymusic container_name: YesPlayMusic + volumes: + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + - ./docker/nginx.conf.example:/etc/nginx/conf.d/default.conf:ro ports: - 80:80 restart: always + depends_on: + - UnblockNeteaseMusic + environment: + - NODE_TLS_REJECT_UNAUTHORIZED=0 + networks: + my_network: + + UnblockNeteaseMusic: + image: pan93412/unblock-netease-music-enhanced + command: -o kugou kuwo migu bilibili pyncmd -p 80:443 -f 45.127.129.53 -e - + # environment: + # JSON_LOG: true + # LOG_LEVEL: debug + networks: + my_network: + aliases: + - music.163.com + - interface.music.163.com + - interface3.music.163.com + - interface.music.163.com.163jiasu.com + - interface3.music.163.com.163jiasu.com + restart: always + +networks: + my_network: + driver: bridge diff --git a/docker/nginx.conf.example b/docker/nginx.conf.example new file mode 100644 index 0000000..cdab218 --- /dev/null +++ b/docker/nginx.conf.example @@ -0,0 +1,28 @@ +server { + gzip on; + listen 80; + listen [::]:80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } + + location @rewrites { + rewrite ^(.*)$ /index.html last; + } + + location /api/ { + proxy_buffers 16 32k; + proxy_buffer_size 128k; + proxy_busy_buffers_size 128k; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Host $remote_addr; + proxy_set_header X-NginX-Proxy true; + proxy_pass http://localhost:3000/; + } +} diff --git a/install-replit.sh b/install-replit.sh new file mode 100644 index 0000000..c17e438 --- /dev/null +++ b/install-replit.sh @@ -0,0 +1,28 @@ + #!/usr/bin/bash + +# 初始化 .replit 和 replit.nix +if [[ $1 == i ]];then + echo -e 'run = ["bash", "main.sh"]\n\nentrypoint = "main.sh"' >.replit + echo -e "{ pkgs }: {\n\t\tdeps = [\n\t\t\tpkgs.nodejs-16_x\n\t\t\tpkgs.yarn\n\t\t\tpkgs.bashInteractive\n\t\t];\n}" > replit.nix + exit +fi + +# 安装 +if [[ ! -d api ]];then + mkdir api + git clone https://github.com/Binaryify/NeteaseCloudMusicApi ./api && \ + cd api && npm install && cd .. +fi + +if [[ ! -d music ]];then + mkdir music + git clone https://github.com/qier222/YesPlayMusic ./music && \ + cd music && cp .env.example .env && npm install --force && npm run build && cd .. +fi + +# 启动 +PID=`ps -ef | grep npm | awk '{print $2}' | sed '$d'` + +if [[ ! -z ${PID} ]];then echo $PID | xargs kill;fi +nohup bash -c 'cd api && PORT=35216 node app.js' > api.log 2>&1 +nohup bash -c 'npx serve music/dist/' > music.log 2>&1 diff --git a/jsconfig.json b/jsconfig.json index 0d612a8..7787662 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -7,7 +7,8 @@ }, "target": "ES6", "module": "commonjs", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "jsx": "preserve" }, "include": ["src/**/*"], "exclude": ["node_modules"] diff --git a/package.json b/package.json index 60bbea0..bb87fdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yesplaymusic", - "version": "0.4.4", + "version": "0.4.9", "private": true, "description": "A third party music player for Netease Music", "author": "qier222", @@ -22,28 +22,33 @@ "netease_api:run": "npx NeteaseCloudMusicApi" }, "main": "background.js", + "engines": { + "node": "14 || 16" + }, "dependencies": { - "@unblockneteasemusic/server": "v0.27.0-rc.4", - "NeteaseCloudMusicApi": "^4.5.2", - "axios": "^0.21.0", + "@unblockneteasemusic/rust-napi": "^0.4.0", + "NeteaseCloudMusicApi": "^4.23.3", + "axios": "^0.26.1", "change-case": "^4.1.2", "cli-color": "^2.0.0", - "color": "^3.1.3", + "color": "^4.2.3", "core-js": "^3.6.5", "crypto-js": "^4.0.0", "dayjs": "^1.8.36", "dexie": "^3.0.3", "discord-rich-presence": "^0.0.8", "electron": "^13.6.7", - "electron-builder": "^22.10.5", - "electron-context-menu": "^2.3.0", + "electron-builder": "^23.0.0", + "electron-context-menu": "^3.1.2", "electron-debug": "^3.1.0", "electron-devtools-installer": "^3.2", - "electron-icon-builder": "^1.0.2", - "electron-is-dev": "^1.2.0", + "electron-icon-builder": "^2.0.1", + "electron-is-dev": "^2.0.0", "electron-log": "^4.3.0", - "electron-store": "^6.0.1", - "electron-updater": "^4.3.5", + "electron-store": "^8.0.1", + "electron-updater": "^5.0.1", + "esbuild": "^0.20.1", + "esbuild-loader": "^4.0.3", "express": "^4.17.1", "express-fileupload": "^1.2.0", "express-http-proxy": "^1.6.2", @@ -55,19 +60,18 @@ "md5": "^2.3.0", "mpris-service": "^2.1.2", "music-metadata": "^7.5.3", - "node-vibrant": "^3.1.6", + "node-vibrant": "^3.2.1-alpha.1", "nprogress": "^0.2.0", "pac-proxy-agent": "^4.1.0", "plyr": "^3.6.2", - "prettier": "2.1.2", "qrcode": "^1.4.4", "register-service-worker": "^1.7.1", - "svg-sprite-loader": "^5.0.0", + "svg-sprite-loader": "^6.0.11", "tunnel": "^0.0.6", - "vscode-codicons": "^0.0.14", + "vscode-codicons": "^0.0.17", "vue": "^2.6.11", - "vue-analytics": "^5.22.1", "vue-clipboard2": "^0.3.1", + "vue-gtag": "1", "vue-i18n": "^8.22.0", "vue-router": "^3.4.3", "vue-slider-component": "^3.2.5", @@ -87,14 +91,16 @@ "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-vue": "^7.9.0", "husky": "^4.3.0", + "prettier": "2.5.1", "sass": "^1.26.11", "sass-loader": "^10.0.2", - "vue-cli-plugin-electron-builder": "~2.0.0-rc.4", + "vue-cli-plugin-electron-builder": "~2.1.1", "vue-template-compiler": "^2.6.11" }, "resolutions": { "icon-gen": "3.0.0", - "degenerator": "2.2.0" + "degenerator": "2.2.0", + "electron-builder": "^23.0.0" }, "eslintConfig": { "root": true, diff --git a/public/img/icons/menu-dark@88.png b/public/img/icons/menu-dark@88.png new file mode 100644 index 0000000..a2feb00 Binary files /dev/null and b/public/img/icons/menu-dark@88.png differ diff --git a/public/img/icons/menu@88.png b/public/img/icons/menu-light@88.png similarity index 100% rename from public/img/icons/menu@88.png rename to public/img/icons/menu-light@88.png diff --git a/public/img/logos/yesplaymusic-white24x24.png b/public/img/logos/yesplaymusic-white24x24.png new file mode 100644 index 0000000..d8e4715 Binary files /dev/null and b/public/img/logos/yesplaymusic-white24x24.png differ diff --git a/public/robots.txt b/public/robots.txt index eb05362..1f53798 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,2 @@ User-agent: * -Disallow: +Disallow: / diff --git a/restyled.yml b/restyled.yml new file mode 100644 index 0000000..c31d3e8 --- /dev/null +++ b/restyled.yml @@ -0,0 +1,7 @@ +commit_template: 'style: with ${restyler.name}' +restylers: + - prettier + - prettier-json + - prettier-markdown + - prettier-yaml + - whitespace diff --git a/src/App.vue b/src/App.vue index b9d7d41..59392be 100644 --- a/src/App.vue +++ b/src/App.vue @@ -124,6 +124,7 @@ main { overflow: auto; padding: 64px 10vw 96px 10vw; box-sizing: border-box; + scrollbar-width: none; // firefox } @media (max-width: 1336px) { diff --git a/src/api/artist.js b/src/api/artist.js index 462f707..925df45 100644 --- a/src/api/artist.js +++ b/src/api/artist.js @@ -1,5 +1,7 @@ import request from '@/utils/request'; import { mapTrackPlayableStatus } from '@/utils/common'; +import { isAccountLoggedIn } from '@/utils/auth'; +import { getTrackDetail } from '@/api/track'; /** * 获取歌手单曲 @@ -14,7 +16,13 @@ export function getArtist(id) { id, timestamp: new Date().getTime(), }, - }).then(data => { + }).then(async data => { + if (!isAccountLoggedIn()) { + const trackIDs = data.hotSongs.map(t => t.id); + const tracks = await getTrackDetail(trackIDs.join(',')); + data.hotSongs = tracks.songs; + return data; + } data.hotSongs = mapTrackPlayableStatus(data.hotSongs); return data; }); diff --git a/src/api/playlist.js b/src/api/playlist.js index 58f6b98..08177ea 100644 --- a/src/api/playlist.js +++ b/src/api/playlist.js @@ -26,7 +26,10 @@ export function dailyRecommendPlaylist(params) { return request({ url: '/recommend/resource', method: 'get', - params, + params: { + params, + timestamp: Date.now(), + }, }); } /** diff --git a/src/api/track.js b/src/api/track.js index 472dc6a..490c91c 100644 --- a/src/api/track.js +++ b/src/api/track.js @@ -15,16 +15,18 @@ import { * @param {string} id - 音乐的 id,例如 id=405998841,33894312 */ export function getMP3(id) { - let br = - store.state.settings?.musicQuality !== undefined - ? store.state.settings.musicQuality - : 320000; + const getBr = () => { + // 当返回的 quality >= 400000时,就会优先返回 hi-res + const quality = store.state.settings?.musicQuality ?? '320000'; + return quality === 'flac' ? '350000' : quality; + }; + return request({ url: '/song/url', method: 'get', params: { id, - br, + br: getBr(), }, }); } diff --git a/src/assets/icons/fullscreen-exit.svg b/src/assets/icons/fullscreen-exit.svg new file mode 100644 index 0000000..f76f601 --- /dev/null +++ b/src/assets/icons/fullscreen-exit.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/fullscreen.svg b/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..e6128c0 --- /dev/null +++ b/src/assets/icons/fullscreen.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/background.js b/src/background.js index 8af41eb..94dbbc0 100644 --- a/src/background.js +++ b/src/background.js @@ -7,6 +7,7 @@ import { dialog, globalShortcut, nativeTheme, + screen, } from 'electron'; import { isWindows, @@ -30,7 +31,8 @@ import { EventEmitter } from 'events'; import express from 'express'; import expressProxy from 'express-http-proxy'; import Store from 'electron-store'; -import { createMpris } from '@/electron/mpris'; +import { createMpris, createDbus } from '@/electron/mpris'; +import { spawn } from 'child_process'; const clc = require('cli-color'); const log = text => { console.log(`${clc.blueBright('[background.js]')} ${text}`); @@ -180,7 +182,10 @@ class Background { minWidth: 1080, minHeight: 720, titleBarStyle: 'hiddenInset', - frame: !isWindows, + frame: !( + isWindows || + (isLinux && this.store.get('settings.linuxEnableCustomTitlebar')) + ), title: 'YesPlayMusic', show: false, webPreferences: { @@ -198,8 +203,42 @@ class Background { }; if (this.store.get('window.x') && this.store.get('window.y')) { - options.x = this.store.get('window.x'); - options.y = this.store.get('window.y'); + let x = this.store.get('window.x'); + let y = this.store.get('window.y'); + + let displays = screen.getAllDisplays(); + let isResetWindiw = false; + if (displays.length === 1) { + let { bounds } = displays[0]; + if ( + x < bounds.x || + x > bounds.x + bounds.width - 50 || + y < bounds.y || + y > bounds.y + bounds.height - 50 + ) { + isResetWindiw = true; + } + } else { + isResetWindiw = true; + for (let i = 0; i < displays.length; i++) { + let { bounds } = displays[i]; + if ( + x > bounds.x && + x < bounds.x + bounds.width && + y > bounds.y && + y < bounds.y - bounds.height + ) { + // 检测到APP窗口当前处于一个可用的屏幕里,break + isResetWindiw = false; + break; + } + } + } + + if (!isResetWindiw) { + options.x = x; + options.y = y; + } } this.window = new BrowserWindow(options); @@ -258,6 +297,7 @@ class Background { this.window.once('ready-to-show', () => { log('window ready-to-show event'); this.window.show(); + this.store.set('window', this.window.getBounds()); }); this.window.on('close', e => { @@ -293,6 +333,14 @@ class Background { this.store.set('window', this.window.getBounds()); }); + this.window.on('maximize', () => { + this.window.webContents.send('isMaximized', true); + }); + + this.window.on('unmaximize', () => { + this.window.webContents.send('isMaximized', false); + }); + this.window.webContents.on('new-window', function (e, url) { e.preventDefault(); log('open url'); @@ -373,6 +421,21 @@ class Background { registerGlobalShortcut(this.window, this.store); } + // try to start osdlyrics process on start + if (this.store.get('settings.enableOsdlyricsSupport')) { + await createDbus(this.window); + log('try to start osdlyrics process'); + const osdlyricsProcess = spawn('osdlyrics'); + + osdlyricsProcess.on('error', err => { + log(`failed to start osdlyrics: ${err.message}`); + }); + + osdlyricsProcess.on('exit', (code, signal) => { + log(`osdlyrics process exited with code ${code}, signal ${signal}`); + }); + } + // create mpris if (isCreateMpris) { createMpris(this.window); diff --git a/src/components/ArtistsInLine.vue b/src/components/ArtistsInLine.vue index cc13384..e2c619d 100644 --- a/src/components/ArtistsInLine.vue +++ b/src/components/ArtistsInLine.vue @@ -2,11 +2,13 @@ {{ computedPrefix }} - - {{ ar.name }} - + {{ + ar.name + }} {{ ar.name }} - , + , @@ -40,4 +42,12 @@ export default { }; - + diff --git a/src/components/ContextMenu.vue b/src/components/ContextMenu.vue index a290a47..186448f 100644 --- a/src/components/ContextMenu.vue +++ b/src/components/ContextMenu.vue @@ -80,11 +80,13 @@ export default { box-shadow: 0 6px 12px -4px rgba(0, 0, 0, 0.08); border: 1px solid rgba(0, 0, 0, 0.06); backdrop-filter: blur(12px); - border-radius: 8px; + border-radius: 12px; box-sizing: border-box; padding: 6px; z-index: 1000; -webkit-app-region: no-drag; + transition: background 125ms ease-out, opacity 125ms ease-out, + transform 125ms ease-out; &:focus { outline: none; @@ -94,8 +96,9 @@ export default { [data-theme='dark'] { .menu { background: rgba(36, 36, 36, 0.78); - backdrop-filter: blur(16px) contrast(120%); + backdrop-filter: blur(16px) contrast(120%) brightness(60%); border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 0 6px rgba(255, 255, 255, 0.08); } .menu .item:hover { color: var(--color-text); @@ -112,7 +115,7 @@ export default { font-weight: 600; font-size: 14px; padding: 10px 14px; - border-radius: 7px; + border-radius: 8px; cursor: default; color: var(--color-text); display: flex; @@ -120,6 +123,11 @@ export default { &:hover { color: var(--color-primary); background: var(--color-primary-bg-for-transparent); + transition: opacity 125ms ease-out, transform 125ms ease-out; + } + &:active { + opacity: 0.75; + transform: scale(0.95); } .svg-icon { @@ -149,7 +157,7 @@ hr { border-radius: 4px; } .info { - margin-left: 8px; + margin-left: 10px; } .title { font-size: 16px; diff --git a/src/components/Cover.vue b/src/components/Cover.vue index 654d217..7cb2e94 100644 --- a/src/components/Cover.vue +++ b/src/components/Cover.vue @@ -16,7 +16,7 @@ > - +
${item.artist.name}`; + return `${item.artist.name}`; if (item.artists !== undefined) - return `${item.artists[0].name}`; + return `${item.artists[0].name}`; } if (this.subText === 'albumType+releaseYear') { let albumType = item.type; @@ -96,7 +96,7 @@ export default { return this.type === 'playlist' && item.privacy === 10; }, isExplicit(item) { - return this.type === 'album' && item.mark === 1056768; + return this.type === 'album' && (item.mark & 1048576) === 1048576; }, getTitleLink(item) { return `/${this.type}/${item.id}`; diff --git a/src/components/DailyTracksCard.vue b/src/components/DailyTracksCard.vue index fd584f1..9b2bdb9 100644 --- a/src/components/DailyTracksCard.vue +++ b/src/components/DailyTracksCard.vue @@ -1,6 +1,6 @@