mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Add preferred url type config (fix #3234)
This commit is contained in:
parent
7d0d91140b
commit
d5f9c67413
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -19,6 +19,7 @@
|
||||
"akari",
|
||||
"BALH",
|
||||
"Bangumi",
|
||||
"bcache",
|
||||
"bevo",
|
||||
"bili",
|
||||
"bilibili",
|
||||
@ -68,6 +69,7 @@
|
||||
"Liveroom",
|
||||
"materialdesignicons",
|
||||
"matroska",
|
||||
"MCDN",
|
||||
"medialist",
|
||||
"mimetype",
|
||||
"minmax",
|
||||
|
||||
@ -46,11 +46,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<div class="download-video-config-item">
|
||||
<div class="download-video-config-title">使用备用下载地址:</div>
|
||||
<SwitchBox v-model="useBackupUrls" />
|
||||
<div class="download-video-config-title">下载地址偏好:</div>
|
||||
<VDropdown v-model="preferredUrlType" :items="urlTypeOptions" />
|
||||
</div>
|
||||
<div class="download-video-config-description">
|
||||
若默认下载地址速度缓慢, 可以尝试更换备用下载地址.
|
||||
当视频有多个源时, 配置优先使用的源, 各类视频源区别请参考
|
||||
<a
|
||||
href="https://github.com/the1812/Bilibili-Evolved/issues/3234#issuecomment-1504764774"
|
||||
target="_blank"
|
||||
>此处</a
|
||||
>
|
||||
</div>
|
||||
<component
|
||||
:is="a.component"
|
||||
@ -111,6 +116,7 @@ import {
|
||||
DownloadVideoInputItem,
|
||||
DownloadVideoOutput,
|
||||
} from './types'
|
||||
import { DownloadVideoUrlType, parseVideoUrlType, sortVideoUrlByType } from './url-type'
|
||||
|
||||
const [inputs] = registerAndGetData('downloadVideo.inputs', [
|
||||
videoSingleInput,
|
||||
@ -134,13 +140,20 @@ const { basicConfig } = getComponentSettings('downloadVideo').options as {
|
||||
api: string
|
||||
quality: number
|
||||
output: string
|
||||
useBackupUrls: boolean
|
||||
preferredUrlType: DownloadVideoUrlType
|
||||
}
|
||||
}
|
||||
const filterData = <T extends { match?: TestPattern }>(items: T[]) => {
|
||||
const matchedItems = items.filter(it => it.match?.some(p => matchUrlPattern(p)) ?? true)
|
||||
return matchedItems
|
||||
}
|
||||
const urlTypeOptions: { displayName: string; name: DownloadVideoUrlType }[] = [
|
||||
{ displayName: 'Mirror', name: DownloadVideoUrlType.Mirror },
|
||||
{ displayName: 'UPOS', name: DownloadVideoUrlType.UPOS },
|
||||
{ displayName: 'BCache', name: DownloadVideoUrlType.BCache },
|
||||
{ displayName: 'MCDN', name: DownloadVideoUrlType.MCDN },
|
||||
]
|
||||
|
||||
const getFallbackTestVideoInfo = () =>
|
||||
({
|
||||
aid: unsafeWindow.aid,
|
||||
@ -163,7 +176,11 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
const lastOutput = basicConfig.output
|
||||
const lastUseBackupUrls = basicConfig.useBackupUrls
|
||||
const lastPreferredUrlType = basicConfig.preferredUrlType
|
||||
console.log({
|
||||
lastPreferredUrlType,
|
||||
optionValue: lastPreferredUrlType ?? DownloadVideoUrlType.Mirror,
|
||||
})
|
||||
return {
|
||||
open: false,
|
||||
busy: false,
|
||||
@ -183,7 +200,9 @@ export default Vue.extend({
|
||||
selectedApi: undefined,
|
||||
outputs,
|
||||
selectedOutput: outputs.find(it => it.name === lastOutput) || outputs[0],
|
||||
useBackupUrls: lastUseBackupUrls || false,
|
||||
urlTypeOptions,
|
||||
preferredUrlType:
|
||||
lastPreferredUrlType ?? urlTypeOptions.find(it => it.name === DownloadVideoUrlType.Mirror),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -229,11 +248,11 @@ export default Vue.extend({
|
||||
}
|
||||
basicConfig.output = output.name
|
||||
},
|
||||
useBackupUrls(useBackupUrls: boolean) {
|
||||
if (useBackupUrls === undefined) {
|
||||
preferredUrlType(urlType: DownloadVideoUrlType) {
|
||||
if (urlType === undefined) {
|
||||
return
|
||||
}
|
||||
basicConfig.useBackupUrls = useBackupUrls
|
||||
basicConfig.preferredUrlType = urlType
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
@ -299,6 +318,7 @@ export default Vue.extend({
|
||||
testItem.quality = this.selectedQuality
|
||||
const qualityVideoInfo = await api.downloadVideoInfo(testItem)
|
||||
this.testData.videoInfo = qualityVideoInfo
|
||||
console.log('[qualityVideoInfo]', qualityVideoInfo)
|
||||
} catch (error) {
|
||||
console.error('[updateTestVideoInfo] failed', error)
|
||||
this.testData.videoInfo = undefined
|
||||
@ -322,16 +342,20 @@ export default Vue.extend({
|
||||
Toast.info('未接收到可下载数据, 请检查输入源和格式是否适用于当前视频.', '下载视频', 3000)
|
||||
return
|
||||
}
|
||||
if (this.useBackupUrls) {
|
||||
videoInfos.forEach(videoInfo => {
|
||||
videoInfo.fragments.forEach(fragment => {
|
||||
fragment.url =
|
||||
fragment.backupUrls && fragment.backupUrls.length !== 0
|
||||
? fragment.backupUrls.at(0)
|
||||
: fragment.url
|
||||
})
|
||||
videoInfos.forEach(videoInfo => {
|
||||
videoInfo.fragments.forEach(fragment => {
|
||||
if (this.preferredUrlType !== undefined) {
|
||||
const preferredUrl = fragment.allUrls.find(
|
||||
url => parseVideoUrlType(url) === this.preferredUrlType,
|
||||
)
|
||||
if (preferredUrl !== undefined) {
|
||||
fragment.url = preferredUrl
|
||||
} else {
|
||||
fragment.url = sortVideoUrlByType(fragment.allUrls)[0]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
const action = new DownloadVideoAction(videoInfos)
|
||||
assets.forEach(a => {
|
||||
const assetsType = a?.getUrls ? action.extraOnlineAssets : action.extraAssets
|
||||
|
||||
@ -67,7 +67,7 @@ const getDashExtensions = (type: keyof typeof DefaultDashExtensions): string =>
|
||||
}
|
||||
const dashToFragment = (dash: Dash): DownloadVideoFragment => ({
|
||||
url: dash.downloadUrl,
|
||||
backupUrls: dash.backupUrls,
|
||||
allUrls: [dash.downloadUrl, ...(dash.backupUrls ?? [])],
|
||||
length: dash.duration,
|
||||
size: Math.trunc((dash.bandWidth * dash.duration) / 8),
|
||||
extension: getDashExtensions(dash.type),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { bilibiliApi, getJsonWithCredentials } from '@/core/ajax'
|
||||
import { formData, matchUrlPattern } from '@/core/utils'
|
||||
import { matchUrlPattern } from '@/core/utils'
|
||||
import { allQualities } from '@/components/video/video-quality'
|
||||
import { bangumiUrls } from '@/core/utils/urls'
|
||||
import { compareQuality } from '../error'
|
||||
@ -24,7 +24,7 @@ const parseInfoFromJson = (data: any, extensions: string[]) => {
|
||||
length: it.length,
|
||||
size: it.size,
|
||||
url: it.url,
|
||||
backupUrls: it.backup_url,
|
||||
allUrls: [it.url, ...(it.backup_url ?? [])],
|
||||
extension: getExtension(index),
|
||||
} as DownloadVideoFragment),
|
||||
)
|
||||
@ -45,11 +45,13 @@ const downloadFlv = async (input: DownloadVideoInputItem) => {
|
||||
const params = {
|
||||
avid: aid,
|
||||
cid,
|
||||
qn: quality?.value ?? '',
|
||||
qn: quality?.value?.toString() ?? '',
|
||||
otype: 'json',
|
||||
}
|
||||
const isBanugmi = bangumiUrls.some(url => matchUrlPattern(url))
|
||||
const api = isBanugmi ? bangumiApi(formData(params)) : videoApi(formData(params))
|
||||
const api = isBanugmi
|
||||
? bangumiApi(new URLSearchParams(params).toString())
|
||||
: videoApi(new URLSearchParams(params).toString())
|
||||
const data = await bilibiliApi(getJsonWithCredentials(api), '获取视频链接失败')
|
||||
const info = new DownloadVideoInfo({
|
||||
input,
|
||||
|
||||
@ -35,8 +35,8 @@ export interface DownloadVideoFragment {
|
||||
length: number
|
||||
size: number
|
||||
url: string
|
||||
allUrls: string[]
|
||||
extension: string
|
||||
backupUrls?: string[]
|
||||
}
|
||||
/** 调用 API 后得到的视频详细信息, 包括下载链接, 清晰度, 分段等 */
|
||||
export class DownloadVideoInfo {
|
||||
|
||||
49
registry/lib/components/video/download/url-type.ts
Normal file
49
registry/lib/components/video/download/url-type.ts
Normal file
@ -0,0 +1,49 @@
|
||||
export enum DownloadVideoUrlType {
|
||||
Mirror = 'mirror',
|
||||
UPOS = 'upos',
|
||||
BCache = 'bcache',
|
||||
MCDN = 'mcdn',
|
||||
Other = 'other',
|
||||
}
|
||||
|
||||
export const MirrorCdnHostPattern =
|
||||
// spell-checker: disable-next-line
|
||||
/^upos-(sz|hz|bstar)-mirror([0-9,a-z]+)\.(bilivideo\.com|akamaized\.net)$/
|
||||
|
||||
/** @see https://github.com/the1812/Bilibili-Evolved/issues/3234#issuecomment-1504764774 */
|
||||
export const parseVideoUrlType = (url: string): DownloadVideoUrlType => {
|
||||
try {
|
||||
const { hostname, searchParams } = new URL(url)
|
||||
if (MirrorCdnHostPattern.test(hostname)) {
|
||||
return DownloadVideoUrlType.Mirror
|
||||
}
|
||||
const os = searchParams.get('os')?.toLowerCase()
|
||||
if (os === 'upos') {
|
||||
return DownloadVideoUrlType.UPOS
|
||||
}
|
||||
if (os === 'bcache') {
|
||||
return DownloadVideoUrlType.BCache
|
||||
}
|
||||
if (os === 'mcdn') {
|
||||
return DownloadVideoUrlType.MCDN
|
||||
}
|
||||
return DownloadVideoUrlType.Other
|
||||
} catch {
|
||||
return DownloadVideoUrlType.Other
|
||||
}
|
||||
}
|
||||
|
||||
export const sortVideoUrlByType = (urls: string[]) => {
|
||||
const sortKeys = {
|
||||
[DownloadVideoUrlType.Mirror]: 0,
|
||||
[DownloadVideoUrlType.UPOS]: 1,
|
||||
[DownloadVideoUrlType.BCache]: 2,
|
||||
[DownloadVideoUrlType.MCDN]: 3,
|
||||
[DownloadVideoUrlType.Other]: 4,
|
||||
}
|
||||
return [...urls].sort((a, b) => {
|
||||
const typeA = parseVideoUrlType(a)
|
||||
const typeB = parseVideoUrlType(b)
|
||||
return sortKeys[typeA] - sortKeys[typeB]
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user