diff --git a/.electron-builder.config.js b/.electron-builder.config.js index 2741ff4..cbb6310 100644 --- a/.electron-builder.config.js +++ b/.electron-builder.config.js @@ -6,14 +6,23 @@ module.exports = { appId: 'com.qier222.yesplaymusic', productName: 'YesPlayMusic', copyright: 'Copyright © 2022 ${author}', - asar: false, + asar: true, directories: { - output: 'release/${version}', + output: 'release', buildResources: 'build', }, npmRebuild: false, buildDependenciesFromSource: true, - files: ['dist'], + files: ['./dist'], + publish: [ + { + provider: 'github', + owner: 'qier222', + repo: 'YesPlayMusic', + vPrefixedTagName: true, + releaseType: 'draft', + }, + ], win: { target: [ { @@ -28,8 +37,14 @@ module.exports = { target: 'nsis', arch: ['ia32'], }, + { + target: 'portable', + arch: ['x64'], + }, ], - artifactName: '${productName}-${version}-Setup.${ext}', + artifactName: '${productName}-${os}-${version}-${arch}-Setup.${ext}', + publisherName: 'qier222', + icon: 'build/icons/icon.ico', }, nsis: { oneClick: false, @@ -38,12 +53,49 @@ module.exports = { deleteAppDataOnUninstall: true, }, mac: { - target: ['dmg'], - artifactName: '${productName}-${version}-Installer.${ext}', + target: [ + { + target: 'dmg', + arch: ['x64', 'arm64', 'universal'], + }, + ], + artifactName: '${productName}-${os}-${version}-${arch}.${ext}', + darkModeSupport: true, + category: 'public.app-category.music', + }, + dmg: { + icon: 'build/icons/icon.icns', }, linux: { - target: ['AppImage'], - artifactName: '${productName}-${version}-Installer.${ext}', + target: [ + { + target: 'deb', + arch: ['x64', 'arm64', 'armv7l'], + }, + { + target: 'AppImage', + arch: ['x64'], + }, + { + target: 'snap', + arch: ['x64'], + }, + { + target: 'pacman', + arch: ['x64'], + }, + { + target: 'rpm', + arch: ['x64'], + }, + { + target: 'tar.gz', + arch: ['x64'], + }, + ], + artifactName: '${productName}-${os}-${version}.${ext}', + category: 'Music', + icon: './build/icon.icns', }, files: [ 'dist/main/**/*', diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..8b24e70 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,90 @@ +name: Build/Release + +on: + push: + branches: + - react + +jobs: + release: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-18.04] + + steps: + - name: Check out Git repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - uses: pnpm/action-setup@v2.0.1 + with: + version: 6.29.0 + + - name: Install Node.js 16 + uses: actions/setup-node@v2 + with: + node-version: 16 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile false + + - name: Electron rebuild + run: pnpm electron-rebuild + + - 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 libopenjp2-tools + + - name: Install Snapcraft (on Ubuntu) + uses: samuelmeuli/action-snapcraft@v1 + if: startsWith(matrix.os, 'ubuntu') + # with: + # Disable since the Snapcraft token is currently not working + # snapcraft_token: ${{ secrets.snapcraft_token }} + + - name: Build/Release Electron app + uses: njzydark/action-electron-builder-pnpm@v1.1.0-pnpm + env: + ELECTRON_WEB_SERVER_PORT: 42710 + VITE_APP_NETEASE_API_URL: /netease + VITE_APP_LASTFM_API_KEY: 09c55292403d961aa517ff7f5e8a3d9c + VITE_APP_LASTFM_API_SHARED_SECRET: 307c9fda32b3904e53654baff215cb67 + with: + # GitHub token, automatically provided to the action + # (No need to define this secret in the repo settings) + github_token: ${{ secrets.github_token }} + # If the commit is tagged with a version (e.g. "v1.0.0"), + # release the app after building + release: ${{ startsWith(github.ref, 'refs/tags/v') }} + args: --config .electron-builder.config.js + skip_package_manager_install: true + package_manager: pnpm + + - name: Upload Artifact (macOS) + uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-mac + path: release/*-universal.dmg + if-no-files-found: ignore + + - name: Upload Artifact (Windows) + uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-win + path: release/*-Setup.exe + if-no-files-found: ignore + + - name: Upload Artifact (Linux) + uses: actions/upload-artifact@v3 + with: + name: YesPlayMusic-linux + path: release/*.AppImage + if-no-files-found: ignore diff --git a/build/icon.icns b/build/icon.icns deleted file mode 100644 index 9a9c785..0000000 Binary files a/build/icon.icns and /dev/null differ diff --git a/build/icon.ico b/build/icon.ico deleted file mode 100644 index 318c972..0000000 Binary files a/build/icon.ico and /dev/null differ diff --git a/build/icons/1024x1024.png b/build/icons/1024x1024.png new file mode 100644 index 0000000..9fc1c62 Binary files /dev/null and b/build/icons/1024x1024.png differ diff --git a/build/icons/128x128.png b/build/icons/128x128.png new file mode 100644 index 0000000..af6310e Binary files /dev/null and b/build/icons/128x128.png differ diff --git a/build/icons/16x16.png b/build/icons/16x16.png new file mode 100644 index 0000000..25e80fe Binary files /dev/null and b/build/icons/16x16.png differ diff --git a/build/icons/24x24.png b/build/icons/24x24.png new file mode 100644 index 0000000..d8e4715 Binary files /dev/null and b/build/icons/24x24.png differ diff --git a/build/icons/256x256.png b/build/icons/256x256.png new file mode 100644 index 0000000..c511962 Binary files /dev/null and b/build/icons/256x256.png differ diff --git a/build/icons/32x32.png b/build/icons/32x32.png new file mode 100644 index 0000000..d70242b Binary files /dev/null and b/build/icons/32x32.png differ diff --git a/build/icons/48x48.png b/build/icons/48x48.png new file mode 100644 index 0000000..74fda19 Binary files /dev/null and b/build/icons/48x48.png differ diff --git a/build/icons/512x512.png b/build/icons/512x512.png new file mode 100644 index 0000000..c5cda24 Binary files /dev/null and b/build/icons/512x512.png differ diff --git a/build/icons/64x64.png b/build/icons/64x64.png new file mode 100644 index 0000000..5eee418 Binary files /dev/null and b/build/icons/64x64.png differ diff --git a/build/icons/icon.icns b/build/icons/icon.icns new file mode 100644 index 0000000..9c2b3f3 Binary files /dev/null and b/build/icons/icon.icns differ diff --git a/build/icons/icon.ico b/build/icons/icon.ico new file mode 100644 index 0000000..c3daede Binary files /dev/null and b/build/icons/icon.ico differ diff --git a/build/icons/icon.png b/build/icons/icon.png new file mode 100644 index 0000000..23f64d2 Binary files /dev/null and b/build/icons/icon.png differ diff --git a/build/icons/menu@88.png b/build/icons/menu@88.png new file mode 100644 index 0000000..cc14a82 Binary files /dev/null and b/build/icons/menu@88.png differ diff --git a/build/installerIcon.ico b/build/installerIcon.ico deleted file mode 100644 index 318c972..0000000 Binary files a/build/installerIcon.ico and /dev/null differ diff --git a/build/uninstallerIcon.ico b/build/uninstallerIcon.ico deleted file mode 100644 index 318c972..0000000 Binary files a/build/uninstallerIcon.ico and /dev/null differ diff --git a/package.json b/package.json index 4f6cc49..a4e7784 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,13 @@ "dev:renderer": "vite dev", "build:main": "node scripts/build.main.mjs", "build:renderer": "vite build", - "build": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js", - "build-dir": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main && electron-builder --config .electron-builder.config.js --dir", + "build": "npm run typecheck && cross-env-shell IS_ELECTRON=true npm run build:renderer && npm run build:main", + "build:app": "npm run build && electron-builder --config .electron-builder.config.js", + "build:app-dir": "npm run build && electron-builder --config .electron-builder.config.js --dir", "typecheck": "tsc --noEmit --project src/renderer/tsconfig.json", "debug": "cross-env-shell NODE_ENV=debug \"npm run typecheck && node scripts/build.mjs && vite ./src/renderer\"", "eslint": "eslint --ext .ts,.js ./", - "prettier": "prettier --write './**/*.{ts,js,tsx,jsx}'", - "postinstall": "electron-rebuild" + "prettier": "prettier --write './**/*.{ts,js,tsx,jsx}'" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" diff --git a/scripts/build.main.mjs b/scripts/build.main.mjs index 14c7574..58c6fbc 100644 --- a/scripts/build.main.mjs +++ b/scripts/build.main.mjs @@ -92,6 +92,10 @@ if (argv.watch) { spinner.start() build({ ...options, + define: { + ...options.define, + 'process.env.NODE_ENV': '"production"', + }, minify: true, }) .then(() => { diff --git a/src/main/server.ts b/src/main/server.ts index 806dbc0..e92f390 100644 --- a/src/main/server.ts +++ b/src/main/server.ts @@ -14,6 +14,7 @@ import path from 'path' logger.info('[server] starting http server') const isDev = process.env.NODE_ENV === 'development' +const isProd = process.env.NODE_ENV === 'production' // eslint-disable-next-line @typescript-eslint/no-var-requires const neteaseApi = require('NeteaseCloudMusicApi') as (params: any) => any[] @@ -87,14 +88,14 @@ app.post('/yesplaymusic/audio/:id', async (req: Request, res: Response) => { } }) -if (!isDev) { - app.use(express.static(path.join(__dirname, '../renderer'))) +if (isProd) { + app.use('/', express.static(path.join(__dirname, '../renderer/'))) } const port = Number( - isDev - ? process.env.ELECTRON_DEV_NETEASE_API_PORT ?? 3000 - : process.env.ELECTRON_WEB_SERVER_PORT ?? 42710 + isProd + ? process.env.ELECTRON_WEB_SERVER_PORT ?? 42710 + : process.env.ELECTRON_DEV_NETEASE_API_PORT ?? 3000 ) app.listen(port, () => { logger.info(`[server] API server listening on port ${port}`) diff --git a/src/renderer/components/FMCard.tsx b/src/renderer/components/FMCard.tsx index a453c3e..79d6aa1 100644 --- a/src/renderer/components/FMCard.tsx +++ b/src/renderer/components/FMCard.tsx @@ -69,8 +69,6 @@ const FMCard = () => { if (coverUrl) { average(coverUrl, { amount: 1, format: 'hex', sample: 1 }).then(color => { let c = colord(color as string) - console.log({ dark: c.isDark(), light: c.isLight() }) - if (c.isLight()) c = c.darken(0.15) else if (c.isDark()) c = c.lighten(0.1) const to = c.darken(0.15).rotate(-5).toHex() diff --git a/src/renderer/components/TracksList.tsx b/src/renderer/components/TracksList.tsx index 4c439bf..557ba2b 100644 --- a/src/renderer/components/TracksList.tsx +++ b/src/renderer/components/TracksList.tsx @@ -226,8 +226,7 @@ const TracksList = memo( track={track} isLiked={userLikedSongs?.ids?.includes(track.id) ?? false} isSkeleton={false} - isPlaying={track.id === playingTrack?.id} - subtitle={track.tns?.at(0) ?? track.alia?.at(0)} + isHighlight={track.id === playingTrack?.id} /> ))} diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx index 1e0d8b3..a7d5d18 100644 --- a/src/renderer/main.tsx +++ b/src/renderer/main.tsx @@ -1,5 +1,5 @@ import { StrictMode } from 'react' -import { render } from 'react-dom' +import * as ReactDOMClient from 'react-dom/client' import { BrowserRouter } from 'react-router-dom' import * as Sentry from '@sentry/react' import { BrowserTracing } from '@sentry/tracing' @@ -20,11 +20,13 @@ Sentry.init({ tracesSampleRate: 1.0, }) -render( +const container = document.getElementById('root') as HTMLElement +const root = ReactDOMClient.createRoot(container) + +root.render( - , - document.getElementById('root') + ) diff --git a/src/renderer/store.ts b/src/renderer/store.ts index bcc1f6b..6b55907 100644 --- a/src/renderer/store.ts +++ b/src/renderer/store.ts @@ -1,4 +1,3 @@ -import { RefObject } from 'react' import { proxy, subscribe } from 'valtio' import { devtools } from 'valtio/utils' import { player as PlayerCore } from '@/utils/player' @@ -33,7 +32,7 @@ export const player = proxy(PlayerCore) player.init() if (import.meta.env.DEV) { - window.player = player + ;(window as any).player = player } // Devtools diff --git a/src/renderer/utils/player.ts b/src/renderer/utils/player.ts index 3d92e2e..4d36bd5 100644 --- a/src/renderer/utils/player.ts +++ b/src/renderer/utils/player.ts @@ -154,7 +154,7 @@ export class Player { */ private async _fetchTrack(trackID: TrackID) { const response = await fetchTracksWithReactQuery({ ids: [trackID] }) - return response?.songs?.length ? response.songs[0] : null + return response?.songs.length ? response.songs[0] : null } /** @@ -173,8 +173,10 @@ export class Player { * Play a track based on this.trackID */ private async _playTrack() { + const id = this.trackID + if (!id) return this.state = State.LOADING - const track = await this._fetchTrack(this.trackID) + const track = await this._fetchTrack(id) if (!track) { toast('加载歌曲信息失败') return @@ -208,7 +210,7 @@ export class Player { this.play() this.state = State.PLAYING _howler.once('load', () => { - this._cacheAudio(_howler._src) + this._cacheAudio((_howler as any)._src) }) if (!this._progressInterval) { @@ -233,9 +235,6 @@ export class Player { } private async _nextFMTrack() { - this.fmTrackList.shift() - this._playTrack() - const loadMoreTracks = async () => { if (this.fmTrackList.length <= 5) { const response = await fetchPersonalFMWithReactQuery() @@ -248,7 +247,11 @@ export class Player { if (track?.al.picUrl) axios.get(resizeImage(track.al.picUrl, 'md')) } - loadMoreTracks() + if (this.fmTrackList.length === 0) await loadMoreTracks() + this.fmTrackList.shift() + this._playTrack() + + this.fmTrackList.length === 0 ? await loadMoreTracks() : loadMoreTracks() prefetchNextTrack() } @@ -405,6 +408,7 @@ export class Player { * Trash current PersonalFMTrack */ async fmTrash() { + this.mode = Mode.FM const trashTrackID = this.fmTrackList[0] fmTrash(trashTrackID) this._nextFMTrack() @@ -424,5 +428,5 @@ export class Player { export const player = new Player() if (import.meta.env.DEV) { - window.howler = _howler + ;(window as any).howler = _howler } diff --git a/vite.config.ts b/vite.config.ts index c93ea7f..ca8843a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -40,7 +40,7 @@ export default defineConfig({ ], }), ], - base: './', + base: '/', build: { target: process.env.IS_ELECTRON ? 'esnext' : 'modules', sourcemap: process.env.NODE_ENV === 'debug',