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 ff3fd3b..4caf481 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -17,7 +17,7 @@ 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 @@ -36,7 +36,7 @@ jobs: 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) @@ -81,6 +81,7 @@ jobs: - 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 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/Dockerfile b/Dockerfile index 19d3817..2fb8f95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ 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 COPY --from=build /app/package.json /usr/local/lib/ -RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main libuv \ +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 $(awk -F \" '{if($2=="NeteaseCloudMusicApi") print $2"@"$4}' /usr/local/lib/package.json) \ && rm -f /usr/local/lib/package.json @@ -19,4 +22,4 @@ RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/m 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/README.md b/README.md index 34efc41..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修复外,不会再更新新功能。 ## ✨ 特性 @@ -120,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 @@ -157,7 +172,7 @@ bash <(curl -s -L https://raw.githubusercontent.com/qier222/YesPlayMusic/main/in 4. 由于 replit 个人版限制内存为 1G(教育版为 3G),构建过程中可能会失败,请再次运行上述命令或运行以下命令: ```sh -cd /home/runner/${REPL_SLUG}/music && yarn installl && yarn run build +cd /home/runner/${REPL_SLUG}/music && yarn install && yarn run build ``` ## 👷‍♂️ 打包客户端 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 221b66d..0930218 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,33 @@ services: 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/package.json b/package.json index 3bf4042..bb87fdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yesplaymusic", - "version": "0.4.7", + "version": "0.4.9", "private": true, "description": "A third party music player for Netease Music", "author": "qier222", @@ -22,9 +22,12 @@ "netease_api:run": "npx NeteaseCloudMusicApi" }, "main": "background.js", + "engines": { + "node": "14 || 16" + }, "dependencies": { "@unblockneteasemusic/rust-napi": "^0.4.0", - "NeteaseCloudMusicApi": "^4.8.7", + "NeteaseCloudMusicApi": "^4.23.3", "axios": "^0.26.1", "change-case": "^4.1.2", "cli-color": "^2.0.0", @@ -44,6 +47,8 @@ "electron-log": "^4.3.0", "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", @@ -59,7 +64,6 @@ "nprogress": "^0.2.0", "pac-proxy-agent": "^4.1.0", "plyr": "^3.6.2", - "prettier": "2.5.1", "qrcode": "^1.4.4", "register-service-worker": "^1.7.1", "svg-sprite-loader": "^6.0.11", @@ -87,6 +91,7 @@ "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.1.1", 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/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/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/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 1d4fdba..94dbbc0 100644 --- a/src/background.js +++ b/src/background.js @@ -31,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}`); @@ -420,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/Cover.vue b/src/components/Cover.vue index 163022a..7cb2e94 100644 --- a/src/components/Cover.vue +++ b/src/components/Cover.vue @@ -135,7 +135,7 @@ img { cursor: default; transition: 0.2s; .svg-icon { - height: 44%; + width: 50%; margin: { left: 4px; } diff --git a/src/components/CoverRow.vue b/src/components/CoverRow.vue index d0e2163..76c1e1b 100644 --- a/src/components/CoverRow.vue +++ b/src/components/CoverRow.vue @@ -50,7 +50,7 @@ export default { props: { items: { type: Array, required: true }, type: { type: String, required: true }, - subText: { type: String, default: 'null' }, + subText: { type: String, default: 'none' }, subTextFontSize: { type: String, default: '16px' }, showPlayCount: { type: Boolean, default: false }, columnNumber: { type: Number, default: 5 }, @@ -75,9 +75,9 @@ export default { return new Date(item.publishTime).getFullYear(); if (this.subText === 'artist') { if (item.artist !== undefined) - return `${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/MvRow.vue b/src/components/MvRow.vue index f6ee06f..a06fa6a 100644 --- a/src/components/MvRow.vue +++ b/src/components/MvRow.vue @@ -73,7 +73,7 @@ export default { artistName = mv.creator[0].userName; artistID = mv.creator[0].userId; } - return `${artistName}`; + return `${artistName}`; } else if (this.subtitle === 'publishTime') { return mv.publishTime; } diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue index 34d1df7..dce629b 100644 --- a/src/components/Navbar.vue +++ b/src/components/Navbar.vue @@ -194,7 +194,7 @@ nav { @media (max-width: 1336px) { nav { - padding: 0 5vw; + padding: 0 max(5vw, 90px); } } diff --git a/src/components/Player.vue b/src/components/Player.vue index 0bb0194..0b98ab6 100644 --- a/src/components/Player.vue +++ b/src/components/Player.vue @@ -187,6 +187,7 @@ import '@/assets/css/slider.css'; import ButtonIcon from '@/components/ButtonIcon.vue'; import VueSlider from 'vue-slider-component'; import { goToListSource, hasListSource } from '@/utils/playList'; +import { formatTrackTime } from '@/utils/common'; export default { name: 'Player', @@ -239,10 +240,7 @@ export default { : this.$router.push({ name: 'next' }); }, formatTrackTime(value) { - if (!value) return ''; - let min = ~~((value / 60) % 60); - let sec = (~~(value % 60)).toString().padStart(2, '0'); - return `${min}:${sec}`; + return formatTrackTime(value); }, hasList() { return hasListSource(); diff --git a/src/components/TrackList.vue b/src/components/TrackList.vue index 6743f3f..ebb5046 100644 --- a/src/components/TrackList.vue +++ b/src/components/TrackList.vue @@ -65,6 +65,7 @@ v-for="(track, index) in tracks" :key="itemKey === 'id' ? track.id : `${track.id}${index}`" :track-prop="track" + :track-no="index + 1" :highlight-playing-track="highlightPlayingTrack" @dblclick.native="playThisList(track.id || track.songId)" @click.right.native="openMenu($event, track, index)" diff --git a/src/components/TrackListItem.vue b/src/components/TrackListItem.vue index 907e884..a3a6238 100644 --- a/src/components/TrackListItem.vue +++ b/src/components/TrackListItem.vue @@ -21,7 +21,7 @@ style="height: 14px; width: 14px" > - {{ track.no }} + {{ trackNo }}