diff --git a/src/electron/ipcMain.js b/src/electron/ipcMain.js index a64847e..0075301 100644 --- a/src/electron/ipcMain.js +++ b/src/electron/ipcMain.js @@ -88,10 +88,10 @@ function toBuffer(data) { } /** - * Get the file URI from bilivideo. + * Get the file base64 data from bilivideo. * * @param {string} url The URL to fetch. - * @returns {Promise} The file URI. + * @returns {Promise} The file base64 data. */ async function getBiliVideoFile(url) { const axios = await import('axios').then(m => m.default); @@ -106,7 +106,7 @@ async function getBiliVideoFile(url) { const buffer = toBuffer(response.data); const encodedData = buffer.toString('base64'); - return `data:application/octet-stream;base64,${encodedData}`; + return encodedData; } /** diff --git a/src/utils/Player.js b/src/utils/Player.js index 2394d30..0274dac 100644 --- a/src/utils/Player.js +++ b/src/utils/Player.js @@ -10,6 +10,7 @@ import { cacheTrackSource, getTrackSource } from '@/utils/db'; import { isCreateMpris, isCreateTray } from '@/utils/platform'; import { Howl, Howler } from 'howler'; import shuffle from 'lodash/shuffle'; +import { decode as base642Buffer } from '@/utils/base64'; const PLAY_PAUSE_FADE_DURATION = 200; @@ -329,25 +330,27 @@ export default class { } this.setOutputDevice(); } - _getAudioSourceFromCache(id) { - return getTrackSource(id).then(t => { - if (!t) return null; - - // Create a new object URL. - const source = URL.createObjectURL(new Blob([t.source])); + _getAudioSourceBlobURL(data) { + // Create a new object URL. + const source = URL.createObjectURL(new Blob([data])); - // Clean up the previous object URLs since we've created a new one. - // Revoke object URLs can release the memory taken by a Blob, - // which occupied a large proportion of memory. - for (const url in this.createdBlobRecords) { - URL.revokeObjectURL(url); - } + // Clean up the previous object URLs since we've created a new one. + // Revoke object URLs can release the memory taken by a Blob, + // which occupied a large proportion of memory. + for (const url in this.createdBlobRecords) { + URL.revokeObjectURL(url); + } - // Then, we replace the createBlobRecords with new one with - // our newly created object URL. - this.createdBlobRecords = [source]; + // Then, we replace the createBlobRecords with new one with + // our newly created object URL. + this.createdBlobRecords = [source]; - return source; + return source; + } + _getAudioSourceFromCache(id) { + return getTrackSource(id).then(t => { + if (!t) return null; + return this._getAudioSourceBlobURL(t.source); }); } _getAudioSourceFromNetease(track) { @@ -416,15 +419,26 @@ export default class { ); if (store.state.settings.automaticallyCacheSongs && retrieveSongInfo?.url) { - cacheTrackSource( - track, - retrieveSongInfo.url, - 128000, - `unm:${retrieveSongInfo.source}` - ); + // 对于来自 bilibili 的音源 + // retrieveSongInfo.url 是音频数据的base64编码 + // 其他音源为实际url + const url = + retrieveSongInfo.source === 'bilibili' + ? `data:application/octet-stream;base64,${retrieveSongInfo.url}` + : retrieveSongInfo.url; + cacheTrackSource(track, url, 128000, `unm:${retrieveSongInfo.source}`); + } + + if (!retrieveSongInfo) { + return null; + } + + if (retrieveSongInfo.source !== 'bilibili') { + return retrieveSongInfo.url; } - return retrieveSongInfo?.url; + const buffer = base642Buffer(retrieveSongInfo.url); + return this._getAudioSourceBlobURL(buffer); } _getAudioSource(track) { return this._getAudioSourceFromCache(String(track.id)) diff --git a/src/utils/base64.js b/src/utils/base64.js new file mode 100644 index 0000000..99ac23c --- /dev/null +++ b/src/utils/base64.js @@ -0,0 +1,67 @@ +// https://github.com/niklasvh/base64-arraybuffer/blob/master/src/index.ts +// Copyright (c) 2012 Niklas von Hertzen Licensed under the MIT license. + +const chars = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +// Use a lookup table to find the index. +const lookup = typeof Uint8Array === 'undefined' ? [] : new Uint8Array(256); +for (let i = 0; i < chars.length; i++) { + lookup[chars.charCodeAt(i)] = i; +} + +export const encode = arraybuffer => { + let bytes = new Uint8Array(arraybuffer), + i, + len = bytes.length, + base64 = ''; + + for (i = 0; i < len; i += 3) { + base64 += chars[bytes[i] >> 2]; + base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; + base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; + base64 += chars[bytes[i + 2] & 63]; + } + + if (len % 3 === 2) { + base64 = base64.substring(0, base64.length - 1) + '='; + } else if (len % 3 === 1) { + base64 = base64.substring(0, base64.length - 2) + '=='; + } + + return base64; +}; + +export const decode = base64 => { + let bufferLength = base64.length * 0.75, + len = base64.length, + i, + p = 0, + encoded1, + encoded2, + encoded3, + encoded4; + + if (base64[base64.length - 1] === '=') { + bufferLength--; + if (base64[base64.length - 2] === '=') { + bufferLength--; + } + } + + const arraybuffer = new ArrayBuffer(bufferLength), + bytes = new Uint8Array(arraybuffer); + + for (i = 0; i < len; i += 4) { + encoded1 = lookup[base64.charCodeAt(i)]; + encoded2 = lookup[base64.charCodeAt(i + 1)]; + encoded3 = lookup[base64.charCodeAt(i + 2)]; + encoded4 = lookup[base64.charCodeAt(i + 3)]; + + bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); + bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); + bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); + } + + return arraybuffer; +}; diff --git a/src/views/settings.vue b/src/views/settings.vue index 6bb9e7e..f96c847 100644 --- a/src/views/settings.vue +++ b/src/views/settings.vue @@ -301,23 +301,6 @@ -
-
-
请求用代理服务器 (Proxy)
-
- 请求如 YouTube 音源服务时要使用的代理服务器。
- 留空则不进行相关设置。 -
-
-
- -
-
-
Joox 引擎的 Cookie