From f3f5868bd1629c56eb622a4aa2215fe9117d0516 Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Wed, 8 Sep 2021 22:50:04 +0800 Subject: [PATCH 1/2] main: support music cache limit --- src/main/api/cache.js | 32 +++++++++++++++++++++++++++++++- src/main/api/index.js | 18 +++++++++++++++++- src/main/api/musicServer.js | 2 +- src/main/settings.js | 32 +++++++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/main/api/cache.js b/src/main/api/cache.js index 0e32c1ec..354d670f 100644 --- a/src/main/api/cache.js +++ b/src/main/api/cache.js @@ -8,8 +8,9 @@ class Cache { /** * @param {string} path cache directory path * @param {string} configPath config directory path + * @param {number} cacheLimitcache size limit in bytes. 0 means unlimited */ - constructor(path, configPath) { + constructor(path, configPath, cacheLimit) { if (typeof path === 'string') { if (!fs.existsSync(path)) { fs.mkdirSync(path); @@ -34,6 +35,7 @@ class Cache { } else { throw new Error('Cache path unvalid'); } + this.cacheLimit = cacheLimit; } attachExternalCache(fileName, filePath) { @@ -56,7 +58,35 @@ class Cache { return path.join(this.path, String(fileName)); } + async setCacheLimit(newSize) { + this.cacheLimit = newSize; + this.spareSpace(); + } + + /** + * delete cache until cache size <= limit + */ + spareSpace() { + if (this.cacheLimit === 0) return; + + const stats = fs.readdirSync(this.path) + .map(f => ({name: f, attr: fs.statSync(path.join(this.path, f))})) + .filter(f => f.attr.isFile()); + let diskUsage = stats.reduce((x, y)=> x + y.attr.size, 0); + if (diskUsage <= this.cacheLimit) + return; + stats.sort((a, b)=> a.attr.atime < b.attr.atime); + for (const st of stats) { + const size = st.attr.size; + diskUsage -= size; + fs.unlinkSync(path.join(this.path, st.name)); + if (diskUsage <= this.cacheLimit) + break; + } + } + writeStream(fileName) { + this.spareSpace(); return fs.createWriteStream(this.internalPath(fileName)); } diff --git a/src/main/api/index.js b/src/main/api/index.js index 9a0fff3e..6fe79581 100644 --- a/src/main/api/index.js +++ b/src/main/api/index.js @@ -24,7 +24,7 @@ const CachePath = { all: dataPath, music: path.join(dataPath, 'musicCache') }; -const musicCache = new Cache(CachePath.music, dataPath); +const musicCache = new Cache(CachePath.music, dataPath, Settings.getSync().musicCacheLimit*1024*1024); migrate(); const musicServer = new MusicServer(musicCache); @@ -530,6 +530,22 @@ export async function clearCache(type) { return { ok: true }; } +/** + * @param {Types.CacheType} size cache limit in bytes + * @returns {Promise<{ok: boolean; msg?: string}>} + */ +export async function setMusicCacheLimit(size) { + try { + await musicServer.cache.setCacheLimit(size); + } catch (e) { + return { + ok: false, + msg: e.stack + }; + } + return { ok: true }; +} + /** * @returns {Promise} */ diff --git a/src/main/api/musicServer.js b/src/main/api/musicServer.js index a73ec8f4..d0e8d177 100644 --- a/src/main/api/musicServer.js +++ b/src/main/api/musicServer.js @@ -153,7 +153,7 @@ class MusicServer { d('Got URL for music id=%d', id); const musicRes = await this.cache.fetch(music.url.replace(/^http:/, 'https:')); // TODO: write file only md5 matches - musicRes.body.pipe(fs.createWriteStream(this.cache.internalPath(fileName))); + musicRes.body.pipe(this.cache.writeStream(fileName)); const range = MusicServer.getRange(req, +musicRes.headers.get('content-length')); res.writeHead(206, MusicServer.buildHeaders(range)); diff --git a/src/main/settings.js b/src/main/settings.js index 44cd9e37..407cba29 100644 --- a/src/main/settings.js +++ b/src/main/settings.js @@ -24,7 +24,8 @@ export const defaultSettings = { themePrimaryColor: '#7e57c2', themeSecondaryColor: '#ff4081', themeVariety: 'auto', - autoReplacePlaylist: false + autoReplacePlaylist: false, + musicCacheLimit: 512, }; /** @@ -44,10 +45,18 @@ function writeFile(target) { return fsp.writeFile(configPath, JSON.stringify(target, null, 4), 'utf8'); } +function writeFileSync(target) { + return fs.writeFileSync(configPath, JSON.stringify(target, null, 4), 'utf8'); +} + function readFile() { return fsp.readFile(configPath, 'utf8'); } +function readFileSync() { + return fs.readFileSync(configPath, 'utf8'); +} + export async function set(target) { try { await fsp.access(configDir); @@ -57,6 +66,15 @@ export async function set(target) { return writeFile(target); } +export function setSync(target) { + try { + fs.accessSync(configDir); + } catch (e) { + fs.mkdirSync(configDir); + } + return writeFileSync(target); +} + export async function get() { let settings = defaultSettings; try { @@ -68,3 +86,15 @@ export async function get() { } return settings; } + +export function getSync() { + let settings = defaultSettings; + try { + fs.accessAsync(configPath); + const json = JSON.parse(readFileSync()); + settings = trimSettings(json); + } catch (e) { + setSync(defaultSettings); + } + return settings; +} From f2804ab9c0a26a8cc847fbe947d392111b4be6cf Mon Sep 17 00:00:00 2001 From: Yujia Qiao Date: Wed, 8 Sep 2021 22:50:18 +0800 Subject: [PATCH 2/2] renderer: support music cache limit --- src/renderer/page/Settings/Settings.vue | 7 +++++++ src/renderer/page/Settings/entries.js | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/renderer/page/Settings/Settings.vue b/src/renderer/page/Settings/Settings.vue index a11a5ce8..b4f381e1 100644 --- a/src/renderer/page/Settings/Settings.vue +++ b/src/renderer/page/Settings/Settings.vue @@ -81,6 +81,10 @@ export default { Api.getDataSize('all').then(s => this.dataSize = humanSize(s.size)); Api.getDataSize('music').then(s => this.musicSize = humanSize(s.size)); }, + setMusicCacheLimit(size) { + Api.setMusicCacheLimit(size); + Api.getDataSize('music').then(s => this.musicSize = humanSize(s.size)); + }, initData() { this.refreshSize(); Api.getVersionName().then(v => this.versionName = v); @@ -201,6 +205,9 @@ export default { this.$toast.message('实际下载码率取决于歌曲最高码率和帐号最高可播放码率'); } break; + case 'musicCacheLimit': + this.setMusicCacheLimit(val*1024*1024); + break; } } }); diff --git a/src/renderer/page/Settings/entries.js b/src/renderer/page/Settings/entries.js index 241447f2..3209797f 100644 --- a/src/renderer/page/Settings/entries.js +++ b/src/renderer/page/Settings/entries.js @@ -163,6 +163,18 @@ export const Entries = [ data: 'musicSize', handler: 'promptClearMusicCache' }, + { + type: 'select', + title: '歌曲缓存空间限制', + prop: 'musicCacheLimit', + options: [ + { label: '无限制', value: 0 }, + { label: '128 MiB', value: 128 }, + { label: '256 MiB', value: 256 }, + { label: '512 MiB', value: 512 }, + { label: '1 GiB', value: 1024 }, + ] + }, { type: 'plain', title: '所有应用数据',