feat: 支持GitHub Actions和一堆其他更新
|
|
@ -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/**/*',
|
||||
|
|
|
|||
90
.github/workflows/build.yaml
vendored
Normal file
|
|
@ -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
|
||||
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 36 KiB |
BIN
build/icons/1024x1024.png
Normal file
|
After Width: | Height: | Size: 523 KiB |
BIN
build/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
build/icons/16x16.png
Normal file
|
After Width: | Height: | Size: 474 B |
BIN
build/icons/24x24.png
Normal file
|
After Width: | Height: | Size: 750 B |
BIN
build/icons/256x256.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
build/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 965 B |
BIN
build/icons/48x48.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
build/icons/512x512.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
build/icons/64x64.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
build/icons/icon.icns
Normal file
BIN
build/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 353 KiB |
BIN
build/icons/icon.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
build/icons/menu@88.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ if (argv.watch) {
|
|||
spinner.start()
|
||||
build({
|
||||
...options,
|
||||
define: {
|
||||
...options.define,
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
},
|
||||
minify: true,
|
||||
})
|
||||
.then(() => {
|
||||
|
|
|
|||
|
|
@ -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}`)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
document.getElementById('root')
|
||||
</StrictMode>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default defineConfig({
|
|||
],
|
||||
}),
|
||||
],
|
||||
base: './',
|
||||
base: '/',
|
||||
build: {
|
||||
target: process.env.IS_ELECTRON ? 'esnext' : 'modules',
|
||||
sourcemap: process.env.NODE_ENV === 'debug',
|
||||
|
|
|
|||