Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion publish/utils/compileAssets.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = () => new Promise((resolve, reject) => {
resolve()
} else {
console.log(chalk.red('Asset compilation failed.'))
reject()
reject(new Error('Asset compilation failed'))
}
})
})
Expand Down
5 changes: 4 additions & 1 deletion src/lang/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@
"songlist__import_input_tip_1": "Cross-service playlists are not supported. Please confirm whether the playlist to be opened corresponds to the current chosen service",
"songlist__import_input_tip_2": "If you encounter a playlist link that cannot be opened. Please send us your feedback",
"songlist__import_input_tip_3": "Unable to open Kugou playlist with playlist ID or link from lite edition, but support to open it with Kugou code or link from main edition",
"songlist__import_input_tip_5": "Captcha needed to view videos of uploader if not logged in, so only public Favlist is supported",
"songlist__import_input_tip_4": "NetEase Cloud Music's \"I Like\" playlist requires a token to open. For details, see ",
"songlist__import_input_title": "Open shared playlist",
"songlist__open_list": "Open \"{name}\" playlist",
Expand All @@ -624,6 +625,7 @@
"source_alias_mg": "MG Music",
"source_alias_tx": "TX Music",
"source_alias_wy": "WY Music",
"source_alias_bili": "Bilibili",
"source_alias_xm": "XM Music",
"source_all": "Aggregated",
"source_bd": "Baidu",
Expand All @@ -632,6 +634,7 @@
"source_mg": "Migu",
"source_tx": "Tencent",
"source_wy": "NetEase",
"source_bili": "Bilibili",
"source_xm": "Xiami",
"sync__auth_code_input_tip": "Please enter the connection code",
"sync__auth_code_title": "Need to enter the connection code",
Expand Down Expand Up @@ -745,4 +748,4 @@
"user_api_import_online__input_loading": "Importing...",
"user_api_import_online__input_tip": "Please enter an HTTP link",
"user_api_import_online__title": "Import Music API from network."
}
}
5 changes: 4 additions & 1 deletion src/lang/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@
"songlist__import_input_tip_2": "若遇到无法打开的歌单链接,欢迎反馈",
"songlist__import_input_tip_3": "酷狗源歌单不支持用歌单 ID 或概念版链接打开,但支持用普通版链接或酷狗码打开",
"songlist__import_input_tip_4": "网易源的「我喜欢」歌单需要 Token 才能打开,详情看 ",
"songlist__import_input_tip_5": "哔哩哔哩由于未登录状态需过人机验证才能获取 UP 主的上传视频,所以只支持导入公开的收藏夹链接",
"songlist__import_input_title": "打开分享的歌单",
"songlist__open_list": "打开「{name}」歌单",
"songlist__tag_info_hot_tag": "热门标签",
Expand All @@ -624,6 +625,7 @@
"source_alias_mg": "小蜜音乐",
"source_alias_tx": "小秋音乐",
"source_alias_wy": "小芸音乐",
"source_alias_bili": "哔哩哔哩",
"source_alias_xm": "小霞音乐",
"source_all": "聚合搜索",
"source_bd": "百度音乐",
Expand All @@ -632,6 +634,7 @@
"source_mg": "咪咕音乐",
"source_tx": "企鹅音乐",
"source_wy": "网易音乐",
"source_bili": "哔哩哔哩",
Comment thread
HittyGubby marked this conversation as resolved.
"source_xm": "虾米音乐",
"sync__auth_code_input_tip": "请输入连接码",
"sync__auth_code_title": "需要输入连接码",
Expand Down Expand Up @@ -745,4 +748,4 @@
"user_api_import_online__input_loading": "导入中...",
"user_api_import_online__input_tip": "请输入 HTTP 链接",
"user_api_import_online__title": "在线导入自定义源"
}
}
5 changes: 4 additions & 1 deletion src/lang/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@
"songlist__import_input_tip_1": "不支援跨平台開啟歌單,請確認要開啟的歌單與目前歌單的來源平台是否對應",
"songlist__import_input_tip_2": "若遇到無法開啟的歌單連結,歡迎回報",
"songlist__import_input_tip_3": "酷狗音樂歌單不支援用歌單 ID 與概念版連結開啟,但支援用普通版連結與酷狗碼開啟",
"songlist__import_input_tip_5": "嗶哩嗶哩由於未登入狀態需過人機驗證才能獲取 UP 主的上傳影片,所以只支援匯入公開的收藏夾連結",
"songlist__import_input_tip_4": "網易雲音樂的「我喜歡」歌單需要 Token 才能開啟,詳情看 ",
"songlist__import_input_title": "開啟分享的歌單",
"songlist__open_list": "開啟「{name}」歌單",
Expand All @@ -624,6 +625,7 @@
"source_alias_mg": "小蜜音樂",
"source_alias_tx": "小秋音樂",
"source_alias_wy": "小芸音樂",
"source_alias_bili": "嗶哩嗶哩",
"source_alias_xm": "小霞音樂",
"source_all": "聚合搜尋",
"source_bd": "百度音樂",
Expand All @@ -632,6 +634,7 @@
"source_mg": "咪咕音樂",
"source_tx": "企鵝音樂",
"source_wy": "網易音樂",
"source_bili": "嗶哩嗶哩",
"source_xm": "蝦米音樂",
"sync__auth_code_input_tip": "請輸入連線碼",
"sync__auth_code_title": "需要輸入連線碼",
Expand Down Expand Up @@ -745,4 +748,4 @@
"user_api_import_online__input_loading": "匯入中...",
"user_api_import_online__input_tip": "請輸入 HTTP 連結",
"user_api_import_online__title": "從線上匯入自訂來源 API"
}
}
6 changes: 4 additions & 2 deletions src/main/modules/userApi/renderer/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ const eventNames = Object.values(EVENT_NAMES)
const events = {
request: null,
}
const allSources = ['kw', 'kg', 'tx', 'wy', 'mg', 'local']
const allSources = ['kw', 'kg', 'tx', 'wy', 'mg', 'bili', 'local']
const supportQualitys = {
kw: ['128k', '320k', 'flac', 'flac24bit'],
kg: ['128k', '320k', 'flac', 'flac24bit'],
tx: ['128k', '320k', 'flac', 'flac24bit'],
wy: ['128k', '320k', 'flac', 'flac24bit'],
mg: ['128k', '320k', 'flac', 'flac24bit'],
bili: [],
local: [],
}
const supportActions = {
Expand All @@ -41,6 +42,7 @@ const supportActions = {
wy: ['musicUrl'],
mg: ['musicUrl'],
xm: ['musicUrl'],
bili: ['musicUrl'],
local: ['musicUrl', 'lyric', 'pic'],
}

Expand Down Expand Up @@ -219,7 +221,7 @@ const initEnv = (userApi) => {
body = resp.body = resp.raw.toString()
try {
resp.body = JSON.parse(resp.body)
} catch (_) {}
} catch (_) { }
body = resp.body
callback.call(this, err, {
statusCode: resp.statusCode,
Expand Down
8 changes: 8 additions & 0 deletions src/main/modules/winMain/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ export const createWindow = () => {
const proxy = getProxy()
setSesProxy(ses, proxy?.host, proxy?.port)

const filter = {
urls: ['*://*/*bili*/*']
}
ses.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders.Referer = 'https://www.bilibili.com'
callback({ requestHeaders: details.requestHeaders })
})

/**
* Initial window options
*/
Expand Down
78 changes: 78 additions & 0 deletions src/renderer/utils/musicSdk/bili/comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { httpFetch } from '../../request'
import { dateFormat2 } from '../../index'

export default {
async getComment(mInfo, page = 1, limit = 20) {
const bvid = mInfo.songmid
const url = `https://api.bilibili.com/x/v2/reply?type=1&oid=${bvid}&sort=2&ps=${limit}&pn=${page}`
const { body, statusCode } = await httpFetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.34 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.34',
Referer: `https://www.bilibili.com/video/${bvid}/`,
},
}).promise

if (statusCode !== 200 || body.code !== 0) throw new Error('获取评论失败')

return {
source: 'bili',
comments: this.filterComment(body.data.replies),
total: body.data.page.count,
page,
limit,
maxPage: body.data.page.num,
}// seems that sorting by time requires state validation but why, this consumes more server resource?
}, // you know what, this actually returns the exact same as getHotComment, but consider this scenario
// user turns on comment and see pitch blank or simply the same content, then they not even notice
// even if they notice, they may just think "lucky me, what are the odds of that", and move on

async getHotComment(mInfo, page = 1, limit = 20) {
const bvid = mInfo.songmid
const url = `https://api.bilibili.com/x/v2/reply?type=1&oid=${bvid}&sort=1&ps=${limit}&pn=${page}`
const { body, statusCode } = await httpFetch(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.34 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.34',
Referer: `https://www.bilibili.com/video/${bvid}/`,
},
}).promise

if (statusCode !== 200 || body.code !== 0) throw new Error('获取热门评论失败')

return {
source: 'bili',
comments: this.filterComment(body.data.replies),
total: body.data.page.count,
page,
limit,
maxPage: body.data.page.num,
}
},

filterComment(rawList) {
if (!rawList) return []

return rawList.map(item => {
return {
id: item.rpid,
rootId: item.rpid,
text: item.content.message.replace(/\\n/g, '\n'),
time: item.ctime * 1000,
timeStr: dateFormat2(item.ctime * 1000),
userName: item.member.uname,
avatar: '//wsrv.nl/?url=' + item.member.avatar + '&il',
userId: item.member.mid,
likedCount: item.like,
reply: item.replies ? item.replies.map(reply => ({
id: reply.rpid,
text: reply.content.message.replace(/\\n/g, '\n'),
time: reply.ctime * 1000,
timeStr: dateFormat2(reply.ctime * 1000),
userName: reply.member.uname,
avatar: '//wsrv.nl/?url=' + reply.member.avatar + '&il',
userId: reply.member.mid,
likedCount: reply.like,
})) : [],
}
})
},
}
29 changes: 29 additions & 0 deletions src/renderer/utils/musicSdk/bili/hotSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { httpFetch } from '../../request'

export default {
_requestObj: null,

async getList(retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))

const _requestObj = httpFetch('https://s.search.bilibili.com/main/hotword', {
headers: {
'User-Agent': 'Mozilla/5.0',
Referer: 'https://www.bilibili.com/',
},
})

const { body, statusCode } = await _requestObj.promise
if (statusCode != 200 || body.code !== 0) throw new Error('获取热搜词失败')

return {
source: 'bili',
list: this.filterList(body.list),
}
},

filterList(rawList) {
return rawList.map(item => item.keyword).slice(0, 20)
},
}
27 changes: 27 additions & 0 deletions src/renderer/utils/musicSdk/bili/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import songList from './songList'
import musicSearch from './musicSearch'
import { apis } from '../api-source'
import hotSearch from './hotSearch'
import comment from './comment'

const bili = {
songList,
musicSearch,
hotSearch,
comment,

getMusicUrl(songInfo, type) {
return apis('bili').getMusicUrl(songInfo, type)
},
getLyric(songInfo) {
throw new Error('Bilibili暂无歌词接口')
},
getPic(songInfo) {
return apis('bili').getPic(songInfo)
},
getMusicDetailPageUrl(songInfo) {
return `https://www.bilibili.com/video/${songInfo.songmid}`
},
}

export default bili
77 changes: 77 additions & 0 deletions src/renderer/utils/musicSdk/bili/musicSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { httpFetch } from '../../request'
import { sizeFormate } from '../../index'

export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
successCode: 0,

async musicSearch(str, page, limit, retryNum = 0) {
if (retryNum > 2) throw new Error('搜索失败')

const searchRequest = httpFetch(`https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(str)}&page=${page}&page_size=${limit}`, {
headers: {
'User-Agent': 'Mozilla/5.0',
Referer: 'https://www.bilibili.com/',
Cookie: 'SESSDATA=xxx',
},
})

return searchRequest.promise.then(({ body }) => {
console.log(body)
if (body.code !== this.successCode) return this.musicSearch(str, page, limit, ++retryNum)
return body
})
},

handleResult(rawList) {
const list = []
if (!rawList) return list
rawList.forEach(video => {
if (!video.bvid) return
const parts = video.duration.split(':')
const minutes = String(parts[0]).padStart(2, '0')
const seconds = String(parts[1]).padStart(2, '0')
const paddedDuration = `${minutes}:${seconds}`
list.push({
singer: video.author,
name: video.title.replace(/<[^>]+>/g, ''),
albumName: video.typename,
albumId: video.tid,
source: 'bili',
interval: paddedDuration,
songId: video.bvid,
songmid: video.bvid,
img: '//wsrv.nl/?url=' + video.pic + '&il',
types: [{ type: 'video', size: sizeFormate(video.play), url: `https://www.bilibili.com/video/${video.bvid}` }],
_types: { video: { size: sizeFormate(video.play) } },
typeUrl: {},
})
})
return list
},

async search(str, page = 1, limit) {
if (limit == null) limit = this.limit

const body = await this.musicSearch(str, page, limit)
if (!body.data.result || body.data.result.length === 0) {
return Promise.reject(new Error('EMPTY_RESULT'))
}
const list = this.handleResult(body.data.result)

this.total = body.data.numResults
this.page = page
this.allPage = Math.ceil(this.total / limit)

return {
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'bili',
}
},
}
Loading