Skip to content

Commit 62722d7

Browse files
committed
✨ feat: 添加歌曲音质和特权标签显示选项
1 parent 33ab167 commit 62722d7

12 files changed

Lines changed: 242 additions & 126 deletions

File tree

electron/main/ipc/ipc-file.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { parseFile } from "music-metadata";
55
import { getFileID, getFileMD5, metaDataLyricsArrayToLrc } from "../utils/helper";
66
import { File, Picture, Id3v2Settings } from "node-taglib-sharp";
77
import { ipcLog } from "../logger";
8-
import FastGlob from "fast-glob";
98
import { download } from "electron-dl";
9+
import FastGlob from "fast-glob";
1010

1111
/**
1212
* 文件相关 IPC
@@ -35,15 +35,6 @@ const initFileIpc = (): void => {
3535
const { common, format } = await parseFile(filePath);
3636
// 获取文件大小
3737
const { size } = await stat(filePath);
38-
// 判断音质等级
39-
let quality: string;
40-
if ((format.sampleRate || 0) >= 96000 || (format.bitsPerSample || 0) > 16) {
41-
quality = "Hi-Res";
42-
} else if ((format.sampleRate || 0) >= 44100) {
43-
quality = "HQ";
44-
} else {
45-
quality = "SQ";
46-
}
4738
return {
4839
id: getFileID(filePath),
4940
name: common.title || basename(filePath),
@@ -53,7 +44,7 @@ const initFileIpc = (): void => {
5344
duration: (format?.duration ?? 0) * 1000,
5445
size: (size / (1024 * 1024)).toFixed(2),
5546
path: filePath,
56-
quality,
47+
quality: format.bitrate ?? 0,
5748
};
5849
});
5950
const metadataArray = await Promise.all(metadataPromises);

src/components/Card/SongCard.vue

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,34 @@
4343
</n-ellipsis>
4444
<!-- 音质 -->
4545
<n-tag
46-
v-if="song?.path && song?.quality"
46+
v-if="song?.quality && settingStore.showSongQuality"
4747
:bordered="false"
48-
:type="song.quality === 'Hi-Res' ? 'warning' : 'info'"
48+
:type="qualityColor"
4949
class="quality"
5050
round
5151
>
5252
{{ song.quality }}
5353
</n-tag>
54+
<!-- 原唱翻唱 -->
55+
<template>
56+
<n-tag v-if="song.originCoverType === 1" :bordered="false" type="primary" round>
57+
58+
</n-tag>
59+
<n-tag v-if="song.originCoverType === 2" :bordered="false" type="info" round>
60+
翻唱
61+
</n-tag>
62+
</template>
5463
<!-- 特权 -->
55-
<n-tag v-if="song.originCoverType === 1" :bordered="false" type="primary" round>
56-
57-
</n-tag>
58-
<n-tag v-if="song.originCoverType === 2" :bordered="false" type="info" round>
59-
翻唱
60-
</n-tag>
61-
<n-tag v-if="song.free === 1" :bordered="false" type="error" round> VIP </n-tag>
62-
<n-tag v-if="song.free === 4" :bordered="false" type="error" round> EP </n-tag>
63-
<!-- 云盘 -->
64-
<n-tag v-if="song?.pc" :bordered="false" class="cloud" type="info" round>
65-
<template #icon>
66-
<SvgIcon name="Cloud" />
67-
</template>
68-
</n-tag>
64+
<template v-if="settingStore.showSongPrivilegeTag">
65+
<n-tag v-if="song.free === 1" :bordered="false" type="error" round> VIP </n-tag>
66+
<n-tag v-if="song.free === 4" :bordered="false" type="error" round> EP </n-tag>
67+
<!-- 云盘 -->
68+
<n-tag v-if="song?.pc" :bordered="false" class="cloud" type="info" round>
69+
<template #icon>
70+
<SvgIcon name="Cloud" />
71+
</template>
72+
</n-tag>
73+
</template>
6974
<!-- MV -->
7075
<n-tag
7176
v-if="song?.mv"
@@ -151,16 +156,16 @@
151156
</template>
152157

153158
<script setup lang="ts">
154-
import type { SongType } from "@/types/main";
155-
import { useStatusStore, useMusicStore, useDataStore } from "@/stores";
159+
import { QualityType, type SongType } from "@/types/main";
160+
import { useStatusStore, useMusicStore, useDataStore, useSettingStore } from "@/stores";
156161
import { formatNumber } from "@/utils/helper";
157162
import { openJumpArtist } from "@/utils/modal";
158163
import { toLikeSong } from "@/utils/auth";
159164
import { isObject } from "lodash-es";
160165
import { formatTimestamp, msToTime } from "@/utils/time";
161166
import { usePlayer } from "@/utils/player";
162-
import blob from "@/utils/blob";
163167
import { isElectron } from "@/utils/env";
168+
import blob from "@/utils/blob";
164169
165170
const props = defineProps<{
166171
// 歌曲
@@ -178,10 +183,19 @@ const player = usePlayer();
178183
const dataStore = useDataStore();
179184
const musicStore = useMusicStore();
180185
const statusStore = useStatusStore();
186+
const settingStore = useSettingStore();
181187
182188
// 歌曲数据
183189
const song = toRef(props, "song");
184190
191+
// 音质颜色
192+
const qualityColor = computed(() => {
193+
if (song.value.quality === QualityType.HiRes) return "warning";
194+
if (song.value.quality === QualityType.SQ) return "warning";
195+
if (song.value.quality === QualityType.HQ) return "info";
196+
return "primary";
197+
});
198+
185199
// 加载本地歌曲封面
186200
const localCover = async (show: boolean) => {
187201
if (!isElectron || !show || !song.value.path) return;

src/components/Player/PlayerData.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
size="small"
4040
align="center"
4141
>
42+
<!-- 音质 -->
43+
<span class="meta-item">{{ statusStore.playUblock ? "未知音质" : statusStore.songQuality }}</span>
4244
<!-- 歌词模式 -->
4345
<span class="meta-item">{{ lyricMode }}</span>
4446
<!-- 是否在线 -->

src/components/Setting/GeneralSetting.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@
9595
</div>
9696
<n-switch class="set" v-model:value="settingStore.menuShowCover" :round="false" />
9797
</n-card>
98+
<n-card class="set-item">
99+
<div class="label">
100+
<n-text class="name">显示歌曲音质</n-text>
101+
<n-text class="tip" :depth="3">是否列表中显示歌曲音质</n-text>
102+
</div>
103+
<n-switch class="set" v-model:value="settingStore.showSongQuality" :round="false" />
104+
</n-card>
105+
<n-card class="set-item">
106+
<div class="label">
107+
<n-text class="name">显示特权标签</n-text>
108+
<n-text class="tip" :depth="3">是否显示如 VIP、EP 等特权标签</n-text>
109+
</div>
110+
<n-switch class="set" v-model:value="settingStore.showSongPrivilegeTag" :round="false" />
111+
</n-card>
112+
<n-card class="set-item">
113+
<div class="label">
114+
<n-text class="name">显示原唱翻唱标签</n-text>
115+
<n-text class="tip" :depth="3">是否显示歌曲原唱翻唱标签</n-text>
116+
</div>
117+
<n-switch class="set" v-model:value="settingStore.showSongOriginalTag" :round="false" />
118+
</n-card>
98119
<n-card class="set-item">
99120
<div class="label">
100121
<n-text class="name">开启页面缓存</n-text>

src/stores/setting.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ export interface SettingState {
174174
showDefaultLocalPath: boolean;
175175
/** 展示当前歌曲歌词状态信息 */
176176
showPlayMeta: boolean;
177+
/** 显示歌曲音质 */
178+
showSongQuality: boolean;
179+
/** 显示歌曲特权标签 */
180+
showSongPrivilegeTag: boolean;
181+
/** 显示原唱翻唱标签 */
182+
showSongOriginalTag: boolean;
177183
}
178184

179185
export const useSettingStore = defineStore("setting", {
@@ -258,6 +264,9 @@ export const useSettingStore = defineStore("setting", {
258264
useRealIP: false,
259265
realIP: "",
260266
showPlayMeta: false,
267+
showSongQuality: true,
268+
showSongPrivilegeTag: true,
269+
showSongOriginalTag: true,
261270
}),
262271
getters: {
263272
/**

src/stores/status.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineStore } from "pinia";
2-
import type { SortType } from "@/types/main";
2+
import { QualityType,type SortType } from "@/types/main";
33
import type { PlayModeType, RGB, ColorScheme } from "@/types/main";
44

55
interface StatusState {
@@ -48,6 +48,8 @@ interface StatusState {
4848
pureLyricMode: boolean;
4949
/** 是否使用 TTML 歌词 */
5050
usingTTMLLyric: boolean;
51+
/** 当前歌曲音质 */
52+
songQuality: QualityType | undefined;
5153
/** 当前播放索引 */
5254
playIndex: number;
5355
/** 歌词播放索引 */
@@ -115,6 +117,7 @@ export const useStatusStore = defineStore("status", {
115117
songCoverTheme: {},
116118
pureLyricMode: false,
117119
usingTTMLLyric: false,
120+
songQuality: undefined,
118121
spectrumsData: [],
119122
playIndex: -1,
120123
lyricIndex: -1,
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ export type CoverSize = {
2020
xl: string;
2121
};
2222

23+
/** 音质 */
24+
export enum QualityType {
25+
/** Hi-Res */
26+
HiRes = "Hi-Res", // hr
27+
/** 无损 */
28+
SQ = "SQ", // sq / flac
29+
/** 高质量 */
30+
HQ = "HQ", // h: 320kbps
31+
/** 中质量 */
32+
MQ = "MQ", // m: 192kbps
33+
/** 低质量 */
34+
LQ = "LQ", // l: 128kbps
35+
}
36+
2337
export type UserType = {
2438
id: number;
2539
name: string;
@@ -48,7 +62,7 @@ export type SongType = {
4862
path?: string;
4963
pc?: boolean;
5064
size?: number;
51-
quality?: "Hi-Res" | "HQ" | "SQ";
65+
quality?: QualityType;
5266
createTime?: number;
5367
updateTime?: number;
5468
playCount?: number;
@@ -68,15 +82,13 @@ export type CoverType = {
6882
count?: number;
6983
tags?: string[];
7084
userId?: number | null;
71-
count?: number;
7285
privacy?: number;
7386
playCount?: number;
7487
liked?: boolean;
7588
likedCount?: number;
7689
commentCount?: number;
7790
shareCount?: number;
7891
subCount?: number;
79-
playCount?: number;
8092
createTime?: number;
8193
updateTime?: number;
8294
loading?: boolean;
@@ -241,14 +253,14 @@ export type UpdateLogType = {
241253
};
242254

243255
// 文件信息
244-
interface FileInfoType {
256+
export interface FileInfoType {
245257
url: string;
246258
sha512: string;
247259
size: number;
248260
}
249261

250262
// 更新信息
251-
interface UpdateInfoType {
263+
export interface UpdateInfoType {
252264
tag: string;
253265
version: string;
254266
files: FileInfoType[];

src/utils/format.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import type { SongType, CoverType, ArtistType, CommentType, MetaData, CatType } from "@/types/main";
1+
import { SongType, CoverType, ArtistType, CommentType, MetaData, CatType } from "@/types/main";
22
import { msToTime } from "./time";
33
import { flatMap, isArray, uniqBy } from "lodash-es";
4+
import { handleSongQuality } from "./helper";
45

56
type CoverDataType = {
67
cover: string;
@@ -67,7 +68,9 @@ export const formatSongsList = (data: any[]): SongType[] => {
6768
size: Number(item.size || 0),
6869
path: item.path,
6970
pc: !!item.pc,
70-
quality: item?.quality,
71+
quality: item?.path
72+
? handleSongQuality(item.quality, "local")
73+
: handleSongQuality(item, "online"),
7174
playCount: Number(item.playCount || item.listenerCount || 0),
7275
createTime: Number(item.createTime || item.publishTime) || undefined,
7376
updateTime: Number(item.lastProgramCreateTime || item.scheduledPublishTime) || undefined,

src/utils/helper.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SongType, UpdateLogType } from "@/types/main";
1+
import { QualityType, SongType, UpdateLogType } from "@/types/main";
22
import { NTooltip, SelectOption } from "naive-ui";
33
import { h, VNode } from "vue";
44
import { useClipboard } from "@vueuse/core";
@@ -419,3 +419,44 @@ export const runIdle = (task: () => void) => {
419419
}, 0);
420420
}
421421
};
422+
423+
/**
424+
* 处理歌曲音质
425+
* @param song 歌曲数据
426+
* @param type 歌曲类型
427+
* @returns 歌曲音质
428+
*/
429+
export const handleSongQuality = (
430+
song: AnyObject | number,
431+
type: "local" | "online" = "local",
432+
): QualityType | undefined => {
433+
if (type === "local" && typeof song === "number") {
434+
if (song >= 960000) return QualityType.HiRes;
435+
if (song >= 441000) return QualityType.SQ;
436+
if (song >= 320000) return QualityType.HQ;
437+
if (song >= 160000) return QualityType.MQ;
438+
return QualityType.LQ;
439+
}
440+
// 含有 level 特殊处理
441+
if( typeof song === "object" && "level" in song){
442+
if(song.level === "hires") return QualityType.HiRes;
443+
if(song.level === "lossless") return QualityType.SQ;
444+
if(song.level === "exhigh") return QualityType.HQ;
445+
if(song.level === "higher") return QualityType.MQ;
446+
if(song.level === "standard") return QualityType.LQ;
447+
return undefined;
448+
}
449+
const order = [
450+
{ key: "hr", type: QualityType.HiRes },
451+
{ key: "sq", type: QualityType.SQ },
452+
{ key: "h", type: QualityType.HQ },
453+
{ key: "m", type: QualityType.MQ },
454+
{ key: "l", type: QualityType.LQ },
455+
];
456+
for (const itemKey of order) {
457+
if (song[itemKey.key] && Number(song[itemKey.key].br) > 0) {
458+
return itemKey.type;
459+
}
460+
}
461+
return undefined;
462+
};

0 commit comments

Comments
 (0)