Skip to content

Commit 22589b4

Browse files
committed
✨ feat: 新增全屏封面
1 parent fe7a421 commit 22589b4

13 files changed

Lines changed: 149 additions & 817 deletions

File tree

electron/server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const initAppServer = async () => {
6161
server.register(initQQMusicAPI, { prefix: "/api" });
6262
// 启动端口
6363
const port = Number(process.env["VITE_SERVER_PORT"] || 25884);
64-
await server.listen({ port });
64+
await server.listen({ port, host: "127.0.0.1" });
6565
serverLog.info(`🌐 Starting AppServer on port ${port}`);
6666
return server;
6767
} catch (error) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "splayer",
33
"productName": "SPlayer",
4-
"version": "3.0.0-rc.1",
4+
"version": "3.0.0-beta.9",
55
"description": "A minimalist music player",
66
"main": "./out/main/index.js",
77
"author": "imsyy",

src/components/Player/FullPlayer.vue

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
</Transition>
2929
<!-- 菜单 -->
3030
<PlayerMenu @mouseenter.stop="stopHide" @mouseleave.stop="playerMove" />
31+
<!-- 全屏封面 -->
32+
<PlayerCover v-if="settingStore.playerType === 'fullscreen' && !pureLyricMode" />
3133
<!-- 主内容 -->
3234
<Transition name="zoom" mode="out-in">
3335
<div
@@ -36,20 +38,18 @@
3638
'player-content',
3739
{
3840
'no-lrc': noLrc,
39-
pure: statusStore.pureLyricMode && musicStore.isHasLrc,
41+
'full-screen': settingStore.playerType === 'fullscreen',
42+
pure: pureLyricMode && musicStore.isHasLrc,
4043
},
4144
]"
4245
@mousemove="playerMove"
4346
>
4447
<Transition name="zoom">
4548
<div
46-
v-if="!pureLyricMode"
49+
v-if="!pureLyricMode && settingStore.playerType !== 'fullscreen'"
4750
:key="musicStore.playSong.id"
4851
class="content-left"
49-
:style="{
50-
width: `${settingStore.playerStyleRatio}%`,
51-
minWidth: `${settingStore.playerStyleRatio}%`,
52-
}"
52+
:style="layoutStyles.left"
5353
>
5454
<!-- 封面 -->
5555
<PlayerCover />
@@ -58,21 +58,18 @@
5858
</div>
5959
</Transition>
6060
<!-- 歌词 -->
61-
<div
62-
class="content-right"
63-
:style="{
64-
width: `${100 - settingStore.playerStyleRatio}%`,
65-
maxWidth: `${100 - settingStore.playerStyleRatio}%`,
66-
}"
67-
>
61+
<div class="content-right" :style="layoutStyles.right">
6862
<!-- 数据 -->
6963
<PlayerData
70-
v-if="statusStore.pureLyricMode && musicStore.isHasLrc"
71-
:center="statusStore.pureLyricMode"
72-
:light="pureLyricMode"
64+
v-if="
65+
(pureLyricMode && musicStore.isHasLrc) ||
66+
settingStore.playerType === 'fullscreen'
67+
"
68+
:center="pureLyricMode || noLrc"
69+
:light="!(settingStore.playerType === 'fullscreen' && noLrc)"
7370
/>
7471
<!-- 歌词 -->
75-
<PlayerLyric />
72+
<PlayerLyric v-if="!noLrc" />
7673
</div>
7774
</div>
7875
</Transition>
@@ -118,8 +115,6 @@ const isShowComment = computed<boolean>(
118115
const noLrc = computed<boolean>(() => {
119116
const noNormalLrc = !musicStore.isHasLrc;
120117
const noYrcAvailable = !musicStore.isHasYrc || !settingStore.showYrc;
121-
// const notLoading = !statusStore.lyricLoading;
122-
123118
return noNormalLrc && noYrcAvailable;
124119
});
125120
@@ -131,6 +126,21 @@ const pureLyricMode = computed<boolean>(
131126
// 主内容 key
132127
const playerContentKey = computed(() => `${musicStore.playSong.id}-${statusStore.pureLyricMode}`);
133128
129+
// 左右布局样式
130+
const layoutStyles = computed(() => {
131+
const ratio = settingStore.playerType === "fullscreen" ? 50 : settingStore.playerStyleRatio;
132+
return {
133+
left: {
134+
width: `${ratio}%`,
135+
minWidth: `${ratio}%`,
136+
},
137+
right: {
138+
width: `${100 - ratio}%`,
139+
maxWidth: `${100 - ratio}%`,
140+
},
141+
};
142+
});
143+
134144
// 数据是否居中
135145
const playerDataCenter = computed<boolean>(
136146
() =>
@@ -295,13 +305,24 @@ onBeforeUnmount(() => {
295305
}
296306
// 无歌词
297307
&.no-lrc {
298-
.content-left {
299-
width: 50% !important;
300-
transform: translateX(50%);
308+
&:not(.full-screen) {
309+
.content-left {
310+
width: 50% !important;
311+
transform: translateX(50%);
312+
}
313+
.content-right {
314+
opacity: 0;
315+
pointer-events: none;
316+
}
301317
}
302-
.content-right {
303-
opacity: 0;
304-
pointer-events: none;
318+
&.full-screen {
319+
.content-right {
320+
.player-data {
321+
width: 100%;
322+
max-width: 100%;
323+
transform: translateY(30vh);
324+
}
325+
}
305326
}
306327
}
307328
}

src/components/Player/PlayerControl.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ const { timeDisplay, toggleTimeFormat } = useTimeFormat();
234234
--n-color-hover: rgba(var(--main-cover-color), 0.2);
235235
--n-color-focus: rgba(var(--main-cover-color), 0.2);
236236
--n-color-pressed: rgba(var(--main-cover-color), 0.12);
237-
backdrop-filter: blur(10px);
238237
margin: 0 12px;
239238
transition:
240239
background-color 0.3s,

src/components/Player/PlayerMeta/PlayerCover.vue

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
<template>
2-
<div :class="['player-cover', settingStore.playerType, { playing: statusStore.playStatus }]">
2+
<!-- 全屏封面 -->
3+
<div
4+
v-if="settingStore.playerType === 'fullscreen' && !isTablet"
5+
class="full-screen"
6+
:style="{ '--gradient-percent': settingStore.playerFullscreenGradient + '%' }"
7+
>
8+
<s-image
9+
:src="musicStore.getSongCover('xl')"
10+
:alt="musicStore.playSong.name"
11+
:title="musicStore.playSong.name"
12+
:lazy="false"
13+
:width="'100%'"
14+
:height="'100%'"
15+
/>
16+
</div>
17+
<!-- 普通封面 -->
18+
<div
19+
v-else
20+
:class="['player-cover', settingStore.playerType, { playing: statusStore.playStatus }]"
21+
>
322
<!-- 指针 -->
423
<img
524
v-if="settingStore.playerType === 'record'"
@@ -33,6 +52,7 @@
3352

3453
<script setup lang="ts">
3554
import { songDynamicCover } from "@/api/song";
55+
import { useMobile } from "@/composables/useMobile";
3656
import { useSettingStore, useStatusStore, useMusicStore } from "@/stores";
3757
import { isLogin } from "@/utils/auth";
3858
import { isEmpty } from "lodash-es";
@@ -41,6 +61,8 @@ const musicStore = useMusicStore();
4161
const statusStore = useStatusStore();
4262
const settingStore = useSettingStore();
4363
64+
const { isTablet } = useMobile();
65+
4466
// 动态封面
4567
const dynamicCover = ref<string>("");
4668
const dynamicCoverLoaded = ref<boolean>(false);
@@ -273,4 +295,19 @@ onBeforeUnmount(() => {
273295
}
274296
}
275297
}
298+
.full-screen {
299+
position: fixed;
300+
left: 0;
301+
top: 0;
302+
height: 100vh;
303+
width: 60vw;
304+
z-index: 0;
305+
mask-image: linear-gradient(to right, #000 var(--gradient-percent), transparent 100%);
306+
-webkit-mask-image: linear-gradient(to right, #000 var(--gradient-percent), transparent 100%);
307+
:deep(img) {
308+
object-fit: cover;
309+
width: 100%;
310+
height: 100%;
311+
}
312+
}
276313
</style>

src/components/Player/PlayerMeta/PlayerData.vue

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<template>
2-
<div :class="['player-data', settingStore.playerType, { center, light }]">
2+
<div
3+
:class="['player-data', settingStore.playerType, { center, light }]"
4+
:style="{ marginLeft: leftMargin }"
5+
>
36
<!-- 名称 -->
47
<div class="name">
58
<span class="name-text text-hidden">
@@ -124,9 +127,10 @@ import { debounce, isObject } from "lodash-es";
124127
import { removeBrackets } from "@/utils/format";
125128
import { SongUnlockServer } from "@/core/player/SongManager";
126129
127-
defineProps<{
130+
const props = defineProps<{
131+
/** 数据居中 */
128132
center?: boolean;
129-
// 少量数据模式
133+
/** 少量数据模式 */
130134
light?: boolean;
131135
}>();
132136
@@ -147,6 +151,13 @@ const lyricMode = computed(() => {
147151
return musicStore.isHasLrc ? "LRC" : "NO-LRC";
148152
});
149153
154+
// 左侧外边距
155+
const leftMargin = computed(() => {
156+
if (props.center) return "0px";
157+
const offset = settingStore.lyricHorizontalOffset;
158+
return settingStore.useAMLyrics ? `${offset + 40}px` : `${offset + 10}px`;
159+
});
160+
150161
/** 歌曲解锁服务器名称映射 */
151162
const sourceMap: Record<string, string> = {
152163
[SongUnlockServer.NETEASE]: "Netease",

src/components/Setting/config/general.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export const useGeneralSettings = (): SettingConfig => {
148148
description: "注入自定义 CSS 和 JavaScript 代码",
149149
buttonLabel: "配置",
150150
action: openCustomCode,
151+
show: statusStore.isDeveloperMode,
151152
},
152153
],
153154
},

src/components/Setting/config/play.ts

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ export const usePlaySettings = (): SettingConfig => {
295295
key: "autoPlay",
296296
label: "自动播放",
297297
type: "switch",
298-
description: isElectron ? "启动时是否自动播放" : "网页端不支持该功能",
298+
description: "启动软件时是否自动播放",
299+
show: isElectron,
299300
value: computed({
300301
get: () => settingStore.autoPlay,
301302
set: (v) => (settingStore.autoPlay = v),
@@ -466,7 +467,6 @@ export const usePlaySettings = (): SettingConfig => {
466467
disabled: computed(
467468
() => settingStore.playbackEngine !== "mpv" && settingStore.audioEngine === "ffmpeg",
468469
),
469-
470470
value: computed({
471471
get: () => settingStore.playDevice,
472472
set: (v) => playDeviceChange(v),
@@ -477,6 +477,7 @@ export const usePlaySettings = (): SettingConfig => {
477477
{
478478
title: "音乐解锁",
479479
tags: [{ text: "Beta", type: "warning" }],
480+
show: isElectron,
480481
items: [
481482
{
482483
key: "useSongUnlock",
@@ -487,7 +488,6 @@ export const usePlaySettings = (): SettingConfig => {
487488
get: () => settingStore.useSongUnlock,
488489
set: (v) => (settingStore.useSongUnlock = v),
489490
}),
490-
show: isElectron,
491491
},
492492
{
493493
key: "songUnlockConfig",
@@ -497,7 +497,6 @@ export const usePlaySettings = (): SettingConfig => {
497497
buttonLabel: "配置",
498498
action: openSongUnlockManager,
499499
disabled: computed(() => !settingStore.useSongUnlock),
500-
show: isElectron,
501500
},
502501
],
503502
},
@@ -526,26 +525,47 @@ export const usePlaySettings = (): SettingConfig => {
526525
options: [
527526
{ label: "封面模式", value: "cover" },
528527
{ label: "唱片模式", value: "record" },
528+
{ label: "全屏封面", value: "fullscreen" },
529529
],
530530
value: computed({
531531
get: () => settingStore.playerType,
532532
set: (v) => (settingStore.playerType = v),
533533
}),
534-
},
535-
{
536-
key: "playerStyleRatio",
537-
label: "封面/歌词占比",
538-
type: "slider",
539-
description: "调整全屏模式下封面与歌词的宽度比例",
540-
min: 30,
541-
max: 70,
542-
step: 1,
543-
marks: { 50: "默认" },
544-
formatTooltip: (v) => `${v}%`,
545-
value: computed({
546-
get: () => settingStore.playerStyleRatio,
547-
set: (v) => (settingStore.playerStyleRatio = v),
548-
}),
534+
condition: () => true,
535+
children: [
536+
{
537+
key: "playerStyleRatio",
538+
label: "封面 / 歌词占比",
539+
type: "slider",
540+
description: "调整全屏播放器的封面与歌词的宽度比例",
541+
min: 30,
542+
max: 70,
543+
step: 1,
544+
marks: { 50: "默认" },
545+
show: () => settingStore.playerType !== "fullscreen",
546+
formatTooltip: (v) => `${v}%`,
547+
value: computed({
548+
get: () => settingStore.playerStyleRatio,
549+
set: (v) => (settingStore.playerStyleRatio = v),
550+
}),
551+
},
552+
{
553+
key: "playerFullscreenGradient",
554+
label: "封面过渡位置",
555+
type: "slider",
556+
description: "调整全屏封面右侧的渐变过渡位置",
557+
show: () => settingStore.playerType === "fullscreen",
558+
min: 0,
559+
max: 100,
560+
step: 1,
561+
marks: { 15: "默认" },
562+
formatTooltip: (v) => `${v}%`,
563+
value: computed({
564+
get: () => settingStore.playerFullscreenGradient,
565+
set: (v) => (settingStore.playerFullscreenGradient = v),
566+
}),
567+
},
568+
],
549569
},
550570
{
551571
key: "playerBackgroundType",

src/components/Setting/config/third.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,11 @@ export const useThirdSettings = (): SettingConfig => {
192192
items: [
193193
{
194194
key: "smtcOpen",
195-
label: "开启系统音频集成",
195+
label: isElectron ? "开启系统音频集成" : "开启浏览器媒体会话",
196196
type: "switch",
197-
description: "与系统集成以显示媒体元数据,支持高清封面显示",
197+
description: isElectron
198+
? "与系统集成以显示媒体元数据,支持高清封面显示"
199+
: "向浏览器发送 Media Session 媒体元数据",
198200
value: computed({
199201
get: () => settingStore.smtcOpen,
200202
set: (v) => (settingStore.smtcOpen = v),

0 commit comments

Comments
 (0)