diff --git a/electron/server/port.ts b/electron/server/port.ts new file mode 100644 index 00000000..315ece92 --- /dev/null +++ b/electron/server/port.ts @@ -0,0 +1,14 @@ +import getPort from "get-port"; + +// 默认端口 +let webPort: number; +let servePort: number; + +const getSafePort = async () => { + if (webPort && servePort) return { webPort, servePort }; + webPort = await getPort({ port: 14558 }); + servePort = await getPort({ port: 25884 }); + return { webPort, servePort }; +}; + +export default getSafePort; diff --git a/package.json b/package.json index 2688f863..13da14a3 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "electron-updater": "^6.3.9", "file-saver": "^2.0.5", "font-list": "^1.5.1", + "get-port": "^7.1.0", "github-markdown-css": "^5.8.1", "howler": "^2.2.4", "js-cookie": "^3.0.5", @@ -101,6 +102,7 @@ "prettier": "^3.4.1", "sass": "^1.81.0", "terser": "^5.36.0", + "typescript": "5.6.2", "unplugin-auto-import": "^0.18.6", "unplugin-vue-components": "^0.27.5", "vite": "^5.4.11", @@ -108,7 +110,6 @@ "vite-plugin-wasm": "^3.3.0", "vue": "^3.5.13", "vue-router": "^4.5.0", - "typescript": "5.6.2", "vue-tsc": "2.0.29" }, "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc09ec7f..16857938 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,9 @@ importers: font-list: specifier: ^1.5.1 version: 1.5.1 + get-port: + specifier: ^7.1.0 + version: 7.1.0 github-markdown-css: specifier: ^5.8.1 version: 5.8.1 @@ -2337,6 +2340,10 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + get-stream@4.1.0: resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} engines: {node: '>=6'} @@ -6478,6 +6485,8 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-port@7.1.0: {} + get-stream@4.1.0: dependencies: pump: 3.0.2 diff --git a/src/api/song.ts b/src/api/song.ts index 97c50c01..5f105a17 100644 --- a/src/api/song.ts +++ b/src/api/song.ts @@ -114,10 +114,20 @@ export const matchSong = ( * 歌曲动态封面 * @param {number} id - 歌曲 id */ - export const songDynamicCover = (id: number) => { return request({ url: "/song/dynamic/cover", params: { id }, }); }; + +/** + * 副歌时间 + * @param {number} id - 歌曲 id + */ +export const songChorus = (id: number) => { + return request({ + url: "/song/chorus", + params: { id }, + }); +}; diff --git a/src/components/Player/MainPlayer.vue b/src/components/Player/MainPlayer.vue index 898fbd88..42dd6a31 100644 --- a/src/components/Player/MainPlayer.vue +++ b/src/components/Player/MainPlayer.vue @@ -15,6 +15,11 @@ :max="100" :tooltip="false" :keyboard="false" + :marks=" + statusStore.chorus && statusStore.progress <= statusStore.chorus + ? { [statusStore.chorus]: '' } + : undefined + " class="player-slider" @dragstart="player.pause(false)" @dragend="sliderDragend" @@ -380,6 +385,7 @@ const changeVolume = (e: WheelEvent) => { height: 16px; top: -8px; left: 0; + margin: 0; --n-rail-height: 3px; --n-handle-size: 14px; } diff --git a/src/stores/status.ts b/src/stores/status.ts index 3336567c..1e17f5d6 100644 --- a/src/stores/status.ts +++ b/src/stores/status.ts @@ -29,6 +29,7 @@ interface StatusState { lyricIndex: number; currentTime: number; duration: number; + chorus: number; progress: number; currentTimeOffset: number; playUblock: boolean; @@ -65,6 +66,8 @@ export const useStatusStore = defineStore({ currentTime: 0, duration: 0, progress: 0, + // 副歌时间 + chorus: 0, // 进度偏移 currentTimeOffset: 0, // 封面主题 diff --git a/src/utils/player.ts b/src/utils/player.ts index a0ad1925..4ed66c1c 100644 --- a/src/utils/player.ts +++ b/src/utils/player.ts @@ -4,7 +4,7 @@ import { Howl, Howler } from "howler"; import { cloneDeep } from "lodash-es"; import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores"; import { parsedLyricsData, resetSongLyric, parseLocalLyric } from "./lyric"; -import { songUrl, unlockSongUrl, songLyric } from "@/api/song"; +import { songUrl, unlockSongUrl, songLyric, songChorus } from "@/api/song"; import { getCoverColorData } from "@/utils/color"; import { calculateProgress } from "./time"; import { isElectron, isDev } from "./helper"; @@ -52,6 +52,7 @@ class Player { currentTime: 0, duration: 0, progress: 0, + chorus: 0, currentTimeOffset: 0, lyricIndex: -1, playStatus: false, @@ -113,12 +114,7 @@ class Player { // 歌词跨界处理 const lyricIndex = index === -1 ? lyrics.length - 1 : index - 1; // 更新状态 - statusStore.$patch({ - currentTime, - duration, - progress, - lyricIndex, - }); + statusStore.$patch({ currentTime, duration, progress, lyricIndex }); // 客户端事件 if (isElectron) { // 歌词变化 @@ -217,9 +213,11 @@ class Player { if (!settingStore.showSpectrums) this.toggleOutputDevice(); // 自动播放 if (autoPlay) this.play(); - // 获取歌词数据 - 非电台和本地 - if (type !== "radio" && !path) this.getLyricData(id); - else resetSongLyric(); + // 获取歌曲附加信息 - 非电台和本地 + if (type !== "radio" && !path) { + this.getLyricData(id); + this.getChorus(id); + } else resetSongLyric(); // 定时获取状态 if (!this.playerInterval) this.handlePlayStatus(); // 新增播放历史 @@ -402,6 +400,22 @@ class Player { const lyricRes = await songLyric(id); parsedLyricsData(lyricRes); } + /** + * 获取副歌时间 + * @param id 歌曲id + */ + private async getChorus(id: number) { + const statusStore = useStatusStore(); + const result = await songChorus(id); + if (result?.code !== 200 || result?.chorus?.length === 0) { + statusStore.chorus = 0; + return; + } + // 计算并保存 + const chorus = result?.chorus?.[0]?.startTime; + const time = ((chorus / 1000 / statusStore.duration) * 100).toFixed(2); + statusStore.chorus = Number(time); + } /** * 播放错误 * 在播放错误时,播放下一首 @@ -590,8 +604,8 @@ class Player { const statusStore = useStatusStore(); // 播放器未加载完成 - if (this.player.state() !== "loaded"){ - return + if (this.player.state() !== "loaded") { + return; } // 淡出 @@ -880,7 +894,7 @@ class Player { const songIndex = await dataStore.setNextPlaySong(song, statusStore.playIndex); // 播放歌曲 if (songIndex < 0) return; - if (play) this.togglePlayIndex(songIndex,true); + if (play) this.togglePlayIndex(songIndex, true); else window.$message.success("已添加至下一首播放"); } /** @@ -888,7 +902,7 @@ class Player { * @param index 播放索引 * @param play 是否立即播放 */ - async togglePlayIndex(index: number,play:boolean = false) { + async togglePlayIndex(index: number, play: boolean = false) { const dataStore = useDataStore(); const statusStore = useStatusStore(); // 获取数据