Skip to content

Commit 2b00699

Browse files
committed
Merge branch 'dev' of https://github.com/flystar233/SPlayer into dev
2 parents 859aec6 + 8d11bdf commit 2b00699

56 files changed

Lines changed: 1362 additions & 567 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ on:
2222

2323
env:
2424
NODE_VERSION: 22.x
25-
PNPM_VERSION: 8
25+
PNPM_VERSION: 10
2626

2727
jobs:
2828
# ===================================================================

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
- ⏭️ 音乐渐入渐出
7676
- 🔄 支持 PWA
7777
- 💬 支持评论区
78+
- 🎵 支持 Last.fm Scrobble(播放记录上报)
7879
- ~~📱 移动端基础适配~~
7980

8081
## 🖼️ screenshots

auto-imports.d.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ declare global {
4848
const isRef: typeof import('vue').isRef
4949
const isShallow: typeof import('vue').isShallow
5050
const makeDestructurable: typeof import('@vueuse/core').makeDestructurable
51-
const manualResetRef: typeof import('@vueuse/core').manualResetRef
5251
const markRaw: typeof import('vue').markRaw
5352
const nextTick: typeof import('vue').nextTick
5453
const onActivated: typeof import('vue').onActivated
@@ -86,7 +85,6 @@ declare global {
8685
const refAutoReset: typeof import('@vueuse/core').refAutoReset
8786
const refDebounced: typeof import('@vueuse/core').refDebounced
8887
const refDefault: typeof import('@vueuse/core').refDefault
89-
const refManualReset: typeof import('@vueuse/core').refManualReset
9088
const refThrottled: typeof import('@vueuse/core').refThrottled
9189
const refWithControl: typeof import('@vueuse/core').refWithControl
9290
const resolveComponent: typeof import('vue').resolveComponent

components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ declare module 'vue' {
157157
SongUnlockManager: typeof import('./src/components/Modal/Setting/SongUnlockManager.vue')['default']
158158
SvgIcon: typeof import('./src/components/Global/SvgIcon.vue')['default']
159159
TextContainer: typeof import('./src/components/Global/TextContainer.vue')['default']
160+
ThirdSetting: typeof import('./src/components/Setting/ThirdSetting.vue')['default']
160161
UpdateApp: typeof import('./src/components/Modal/UpdateApp.vue')['default']
161162
UpdatePlaylist: typeof import('./src/components/Modal/UpdatePlaylist.vue')['default']
162163
User: typeof import('./src/components/Layout/User.vue')['default']

electron-builder.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const config: Configuration = {
8484
// 可执行文件名
8585
executableName: "SPlayer",
8686
// 应用程序的图标文件路径
87-
icon: "public/icons/favicon-512x512.png",
87+
icon: "public/icons/icon.icns",
8888
// 权限继承的文件路径
8989
entitlementsInherit: "build/entitlements.mac.plist",
9090
// macOS 平台全局文件名模板

electron.vite.config.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { resolve } from "path";
22
import { MainEnv } from "./env";
3-
import { defineConfig, externalizeDepsPlugin, loadEnv } from "electron-vite";
3+
import { defineConfig, loadEnv } from "electron-vite";
44
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
55
import vue from "@vitejs/plugin-vue";
66
import AutoImport from "unplugin-auto-import/vite";
@@ -9,20 +9,18 @@ import viteCompression from "vite-plugin-compression";
99
// import VueDevTools from "vite-plugin-vue-devtools";
1010
import wasm from "vite-plugin-wasm";
1111

12-
export default defineConfig(({ command, mode }) => {
12+
export default defineConfig(({ mode }) => {
1313
// 读取环境变量
1414
const getEnv = (name: keyof MainEnv): string => {
1515
return loadEnv(mode, process.cwd())[name];
1616
};
17-
console.log(command);
1817
// 获取端口
1918
const webPort: number = Number(getEnv("VITE_WEB_PORT") || 14558);
2019
const servePort: number = Number(getEnv("VITE_SERVER_PORT") || 25884);
2120
// 返回配置
2221
return {
2322
// 主进程
2423
main: {
25-
plugins: [externalizeDepsPlugin()],
2624
build: {
2725
publicDir: resolve(__dirname, "public"),
2826
rollupOptions: {
@@ -34,7 +32,6 @@ export default defineConfig(({ command, mode }) => {
3432
},
3533
// 预加载
3634
preload: {
37-
plugins: [externalizeDepsPlugin()],
3835
build: {
3936
rollupOptions: {
4037
input: {

electron/main/services/CacheService.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,21 @@ export class CacheService {
130130

131131
for (const type of Object.keys(this.CACHE_SUB_DIR) as CacheResourceType[]) {
132132
const dir = join(basePath, this.CACHE_SUB_DIR[type]);
133-
if (!existsSync(dir)) await mkdir(dir, { recursive: true });
133+
if (!existsSync(dir)) {
134+
await mkdir(dir, { recursive: true });
135+
} else {
136+
// 清理可能残留的临时文件 (.tmp)
137+
try {
138+
const files = await readdir(dir);
139+
for (const file of files) {
140+
if (file.endsWith(".tmp")) {
141+
await rm(join(dir, file), { force: true });
142+
}
143+
}
144+
} catch (e) {
145+
cacheLog.warn(`⚠️ 无法清理目录中的临时文件: ${dir}`, e);
146+
}
147+
}
134148
// 计算初始大小
135149
this.sizes[type] = await this.calculateDirSize(dir);
136150
}

electron/main/services/LocalMusicService.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { nativeImage } from "electron";
12
import { join, basename } from "path";
23
import { readFile, mkdir } from "fs/promises";
34
import { CacheService } from "./CacheService";
@@ -7,7 +8,6 @@ import { useStore } from "../store";
78
import { type IAudioMetadata, parseFile } from "music-metadata";
89
import FastGlob, { type Entry } from "fast-glob";
910
import pLimit from "p-limit";
10-
import sharp from "sharp";
1111

1212
/** 当前本地音乐库 DB 版本,用于控制缓存结构升级 */
1313
const CURRENT_DB_VERSION = 2;
@@ -138,18 +138,34 @@ export class LocalMusicService {
138138
const { coverDir } = this.paths;
139139
const picture = metadata.common.picture?.[0];
140140
if (!picture) return undefined;
141-
const fileName = `${fileId}.webp`;
141+
142+
// 使用 jpg 格式,兼容性更好且无需外部依赖
143+
const fileName = `${fileId}.jpg`;
142144
const savePath = join(coverDir, fileName);
145+
143146
// 已存在
144147
if (existsSync(savePath)) return fileName;
145-
// 压缩封面处理
146-
const cacheService = CacheService.getInstance();
147-
const buffer = await sharp(picture.data)
148-
.resize(256, 256, { fit: "cover", position: "centre" })
149-
.webp({ quality: 80 })
150-
.toBuffer();
151-
await cacheService.put("local-data", `covers/${fileName}`, buffer);
152-
return fileName;
148+
149+
try {
150+
const img = nativeImage.createFromBuffer(Buffer.from(picture.data));
151+
if (img.isEmpty()) return undefined;
152+
153+
// 调整大小并压缩
154+
const buffer = img
155+
.resize({
156+
width: 256,
157+
height: 256,
158+
quality: "better",
159+
})
160+
.toJPEG(80);
161+
162+
const cacheService = CacheService.getInstance();
163+
await cacheService.put("local-data", `covers/${fileName}`, buffer);
164+
return fileName;
165+
} catch (e) {
166+
console.error("Failed to extract cover using nativeImage:", e);
167+
return undefined;
168+
}
153169
}
154170

155171
/**

electron/main/services/MusicCacheService.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { existsSync, createWriteStream } from "fs";
2-
import { unlink } from "fs/promises";
2+
import { unlink, rename } from "fs/promises";
33
import { pipeline } from "stream/promises";
44
import { CacheService } from "./CacheService";
55
import { useStore } from "../store";
@@ -52,9 +52,11 @@ export class MusicCacheService {
5252
// 模糊查找 (API请求失败时,只要有缓存就用)
5353
try {
5454
const items = await this.cacheService.list("music");
55-
// 查找以 id_ 开头的文件
55+
// 查找以 id_ 开头且以 .sc 结尾的文件(排除 .tmp 文件)
5656
const prefix = `${id}_`;
57-
const match = items.find((item) => item.key.startsWith(prefix));
57+
const match = items.find(
58+
(item) => item.key.startsWith(prefix) && item.key.endsWith(".sc"),
59+
);
5860
if (match) {
5961
return this.cacheService.getFilePath("music", match.key);
6062
}
@@ -73,7 +75,7 @@ export class MusicCacheService {
7375
*/
7476
public async cacheMusic(id: number | string, url: string, quality: string): Promise<string> {
7577
const store = useStore();
76-
const limitSizeGB = store.get("cacheLimit") || 10;
78+
const limitSizeGB = store.get("cacheLimit") ?? 10;
7779
const limitSizeBytes = limitSizeGB * 1024 * 1024 * 1024;
7880

7981
// 如果设置为 0,则不限制
@@ -91,25 +93,29 @@ export class MusicCacheService {
9193

9294
const key = this.getCacheKey(id, quality);
9395
const filePath = this.cacheService.getFilePath("music", key);
96+
const tempPath = `${filePath}.tmp`;
9497

9598
// 确保目录存在
9699
await this.cacheService.init();
97100

98101
// 下载并写入
99102
try {
100103
const downloadStream = got.stream(url);
101-
const fileStream = createWriteStream(filePath);
104+
const fileStream = createWriteStream(tempPath);
102105

103106
await pipeline(downloadStream, fileStream);
104107

108+
// 下载成功后,将临时文件重命名为正式缓存文件
109+
await rename(tempPath, filePath);
110+
105111
// 更新 CacheService 的大小记录
106112
await this.cacheService.notifyFileChange("music", key);
107113

108114
return filePath;
109115
} catch (error) {
110-
// 下载失败,清理残余
111-
if (existsSync(filePath)) {
112-
await unlink(filePath);
116+
// 下载失败,清理残余的临时文件
117+
if (existsSync(tempPath)) {
118+
await unlink(tempPath).catch(() => {});
113119
}
114120
cacheLog.error("Music download failed:", error);
115121
throw error;

package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "splayer",
33
"productName": "SPlayer",
4-
"version": "3.0.0-beta.7",
4+
"version": "3.0.0-beta.8",
55
"description": "A minimalist music player",
66
"main": "./out/main/index.js",
77
"author": "imsyy",
@@ -40,7 +40,7 @@
4040
"@electron-toolkit/utils": "^4.0.0",
4141
"@imsyy/color-utils": "^1.0.2",
4242
"@material/material-color-utilities": "^0.3.0",
43-
"@neteasecloudmusicapienhanced/api": "^4.29.17",
43+
"@neteasecloudmusicapienhanced/api": "^4.29.18",
4444
"@pixi/app": "^7.4.3",
4545
"@pixi/core": "^7.4.3",
4646
"@pixi/display": "^7.4.3",
@@ -75,7 +75,6 @@
7575
"pinia": "^3.0.4",
7676
"pinia-plugin-persistedstate": "^4.7.1",
7777
"plyr": "^3.8.3",
78-
"sharp": "^0.34.5",
7978
"sortablejs": "^1.15.6",
8079
"vue-virtual-scroller": "2.0.0-beta.8"
8180
},
@@ -99,7 +98,7 @@
9998
"electron": "38.2.2",
10099
"electron-builder": "^26.0.12",
101100
"electron-log": "^5.4.3",
102-
"electron-vite": "^4.0.1",
101+
"electron-vite": "^5.0.0",
103102
"eslint": "^9.39.1",
104103
"eslint-plugin-vue": "^10.6.2",
105104
"fast-glob": "^3.3.3",
@@ -112,7 +111,7 @@
112111
"typescript": "^5.9.3",
113112
"unplugin-auto-import": "^20.3.0",
114113
"unplugin-vue-components": "^29.2.0",
115-
"vite": "^7.2.6",
114+
"vite": "^7.3.0",
116115
"vite-plugin-compression": "^0.5.1",
117116
"vite-plugin-vue-devtools": "^8.0.5",
118117
"vite-plugin-wasm": "^3.5.0",
@@ -134,7 +133,6 @@
134133
"electron",
135134
"electron-winstaller",
136135
"esbuild",
137-
"sharp",
138136
"vue-demi"
139137
]
140138
}

0 commit comments

Comments
 (0)