重命名 RPC 预设:
@@ -117,11 +122,15 @@ export default Vue.extend({
}
},
methods: {
- saveSettings() {
+ saveProfileSettings() {
options.selectedRpcProfileName = this.selectedRpcProfile.name
options.rpcProfiles = this.rpcProfiles
+ storedOptions.selectedRpcProfileName = options.selectedRpcProfileName
+ storedOptions.rpcProfiles = options.rpcProfiles
+ },
+ saveAssetsSettings() {
options.isPluginDownloadAssets = this.isPluginDownloadAssets
- Object.assign(storedOptions, options)
+ storedOptions.isPluginDownloadAssets = options.isPluginDownloadAssets
},
async startRename() {
this.profileRename = this.selectedRpcProfile.name
@@ -143,7 +152,7 @@ export default Vue.extend({
}
this.selectedRpcProfile.name = this.profileRename
this.isRenaming = false
- this.saveSettings()
+ this.saveProfileSettings()
},
newProfile() {
const newProfile: Aria2RpcProfile = { ...this.selectedRpcProfile }
@@ -206,5 +215,9 @@ export default Vue.extend({
.profile-method {
align-self: flex-start;
}
+ .online-assets-download {
+ flex-direction: column;
+ align-items: start;
+ }
}
diff --git a/registry/lib/plugins/video/download/aria2-output/aria2-rpc.ts b/registry/lib/plugins/video/download/aria2-output/aria2-rpc.ts
index 4a10d1e007..48f38b7750 100644
--- a/registry/lib/plugins/video/download/aria2-output/aria2-rpc.ts
+++ b/registry/lib/plugins/video/download/aria2-output/aria2-rpc.ts
@@ -2,7 +2,10 @@ import { getJson, monkey, postJson } from '@/core/ajax'
import { Toast } from '@/core/toast'
import { UserAgent } from '@/core/utils/constants'
import { logError } from '@/core/utils/log'
-import { DownloadVideoOutput } from '../../../../components/video/download/types'
+import {
+ DownloadVideoAssets,
+ DownloadVideoOutput,
+} from '../../../../components/video/download/types'
import { Aria2RpcProfile } from './rpc-profiles'
interface RpcParam {
@@ -138,7 +141,7 @@ export const aria2Rpc: DownloadVideoOutput = {
const { selectedRpcProfile, isPluginDownloadAssets } = instance
const { secretKey, dir, other } = selectedRpcProfile
const referer = document.URL.replace(window.location.search, '')
- const ariaParamsGenerator = (url: string, title: string) => {
+ const getAria2Params = (url: string, title: string) => {
const singleInfoParams = []
if (secretKey) {
singleInfoParams.push(`token:${secretKey}`)
@@ -158,30 +161,29 @@ export const aria2Rpc: DownloadVideoOutput = {
}
}
- // handle video params
const videoParams = infos
.map(info =>
info.titledFragments.map(fragment => {
const { url, title } = fragment
- return ariaParamsGenerator(url, title)
+ return getAria2Params(url, title)
}),
)
.flat()
- // handle assets
- const assetsAriaParams = []
- const extraAssetsForBrowerDownload = []
- for (const { asset, instance: assetInstance } of extraOnlineAssets) {
- if (isPluginDownloadAssets && 'getUrls' in asset) {
- // get asset from aria2
- const results = await asset.getUrls(infos, assetInstance)
- assetsAriaParams.push(...results.map(({ name, url }) => ariaParamsGenerator(url, name)))
- } else {
- // remain asset in `extraOnlineAssets`
- extraAssetsForBrowerDownload.push({ asset, instance: assetInstance })
- }
- }
- action.extraOnlineAssets = extraAssetsForBrowerDownload
+ const isAriaAsset = (asset: DownloadVideoAssets) =>
+ isPluginDownloadAssets && asset.getUrls !== undefined
+ const assetsAriaParams = (
+ await Promise.all(
+ extraOnlineAssets
+ .filter(it => isAriaAsset(it.asset))
+ .map(async it => {
+ const { asset, instance: assetInstance } = it
+ const results = await asset.getUrls(infos, assetInstance)
+ return results.map(({ name, url }) => getAria2Params(url, name))
+ }),
+ )
+ ).flat()
+ action.extraOnlineAssets = extraOnlineAssets.filter(it => !isAriaAsset(it.asset))
const totalParams = [...videoParams, ...assetsAriaParams]
const results = await sendRpc(selectedRpcProfile, totalParams)
diff --git a/registry/lib/plugins/video/download/wasm-output/Config.vue b/registry/lib/plugins/video/download/wasm-output/Config.vue
index 0934be6e4f..09862e113b 100644
--- a/registry/lib/plugins/video/download/wasm-output/Config.vue
+++ b/registry/lib/plugins/video/download/wasm-output/Config.vue
@@ -33,7 +33,7 @@ export default Vue.extend({
},
methods: {
saveOptions() {
- options.muxWithMetadata = this.muxExtraAssets
+ options.muxWithMetadata = this.muxWithMetadata
Object.assign(storedOptions, options)
},
},
diff --git a/registry/lib/plugins/video/download/wasm-output/ffmpeg.ts b/registry/lib/plugins/video/download/wasm-output/ffmpeg.ts
index 5f991c0ba1..f02e78d346 100644
--- a/registry/lib/plugins/video/download/wasm-output/ffmpeg.ts
+++ b/registry/lib/plugins/video/download/wasm-output/ffmpeg.ts
@@ -17,7 +17,9 @@ enum FFMessageType {
EXEC = 'EXEC',
WRITE_FILE = 'WRITE_FILE',
READ_FILE = 'READ_FILE',
+ DELETE_FILE = 'DELETE_FILE',
ERROR = 'ERROR',
+ PROGRESS = 'PROGRESS',
}
export class FFmpeg {
@@ -25,6 +27,8 @@ export class FFmpeg {
#resolves: Callbacks = {}
#rejects: Callbacks = {}
+ #progressEventCallback: (event: ProgressEvent) => void
+
public loaded = false
#registerHandlers = () => {
@@ -38,8 +42,14 @@ export class FFmpeg {
case FFMessageType.EXEC:
case FFMessageType.WRITE_FILE:
case FFMessageType.READ_FILE:
+ case FFMessageType.DELETE_FILE:
this.#resolves[id](data)
break
+ case FFMessageType.PROGRESS:
+ if (this.#progressEventCallback) {
+ this.#progressEventCallback(
data)
+ }
+ break
case FFMessageType.ERROR:
this.#rejects[id](data)
break
@@ -140,6 +150,20 @@ export class FFmpeg {
undefined,
signal,
) as Promise
+
+ public deleteFile = (path: string, signal?: AbortSignal) =>
+ this.#send(
+ {
+ type: FFMessageType.DELETE_FILE,
+ data: { path },
+ },
+ undefined,
+ signal,
+ ) as Promise
+
+ public onProgress(callback: (event: ProgressEvent) => void): void {
+ this.#progressEventCallback = callback
+ }
}
// ========================================================================== //
@@ -165,18 +189,28 @@ interface FFMessageReadFileData {
encoding: string
}
+interface FFMessageDeleteFileData {
+ path: string
+}
+
type FFMessageData =
| FFMessageLoadConfig
| FFMessageExecData
| FFMessageWriteFileData
| FFMessageReadFileData
+ | FFMessageDeleteFileData
interface Message {
type: string
data?: FFMessageData
}
-type CallbackData = Uint8Array | string | boolean | Error | undefined
+interface ProgressEvent {
+ progress: number
+ time: number
+}
+
+type CallbackData = Uint8Array | string | boolean | Error | ProgressEvent | undefined
interface Callbacks {
[id: number | string]: (data: CallbackData) => void
diff --git a/registry/lib/plugins/video/download/wasm-output/handler.ts b/registry/lib/plugins/video/download/wasm-output/handler.ts
index 7d6842e1cf..4be359a0dd 100644
--- a/registry/lib/plugins/video/download/wasm-output/handler.ts
+++ b/registry/lib/plugins/video/download/wasm-output/handler.ts
@@ -2,6 +2,7 @@ import { DownloadPackage, PackageEntry } from '@/core/download'
import { meta } from '@/core/meta'
import { getComponentSettings } from '@/core/settings'
import { Toast } from '@/core/toast'
+import { formatPercent } from '@/core/utils/formatters'
import { title as pluginTitle } from '.'
import type { Options } from '../../../../components/video/download'
import { DownloadVideoAction } from '../../../../components/video/download/types'
@@ -59,13 +60,13 @@ async function single(
httpGet(audioUrl, progress(1, '正在下载音频流')),
])
- ffmpeg.writeFile('video', video)
- ffmpeg.writeFile('audio', audio)
+ await ffmpeg.writeFile('video', video)
+ await ffmpeg.writeFile('audio', audio)
const args = ['-i', 'video', '-i', 'audio']
if (ffmetadata) {
- ffmpeg.writeFile('ffmetadata', new TextEncoder().encode(ffmetadata))
+ await ffmpeg.writeFile('ffmetadata', new TextEncoder().encode(ffmetadata))
args.push('-i', 'ffmetadata', '-map_metadata', '2')
if (!outputMkv) {
args.push('-movflags', '+use_metadata_tags')
@@ -76,7 +77,9 @@ async function single(
console.debug('FFmpeg commandline args:', args.join(' '))
- toast.message = '混流中……'
+ ffmpeg.onProgress(event => {
+ toast.message = `混流中: ${formatPercent(event.progress)}`
+ })
await ffmpeg.exec(args)
const output = await ffmpeg.readFile('output')
@@ -87,6 +90,13 @@ async function single(
toast.message = '完成!'
toast.duration = 1000
+ await Promise.all([
+ ffmpeg.deleteFile('video'),
+ ffmpeg.deleteFile('audio'),
+ ffmpeg.deleteFile('output'),
+ ffmetadata ? ffmpeg.deleteFile('ffmetadata') : Promise.resolve(),
+ ])
+
await DownloadPackage.single(
name.replace(/.[^/.]+$/, `.${outputMkv ? 'mkv' : 'mp4'}`),
outputBlob,
diff --git a/src/client/common.meta.json b/src/client/common.meta.json
index 45aff1fb4e..60a9e94376 100644
--- a/src/client/common.meta.json
+++ b/src/client/common.meta.json
@@ -1,5 +1,5 @@
{
- "version": "2.9.4",
+ "version": "2.9.5",
"author": "Grant Howard, Coulomb-G",
"copyright": "[year], Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)",
"license": "MIT",
diff --git a/src/components/feeds/api/index.ts b/src/components/feeds/api/index.ts
index 708104370e..1d8487e438 100644
--- a/src/components/feeds/api/index.ts
+++ b/src/components/feeds/api/index.ts
@@ -1,4 +1,4 @@
-import { getUID, pascalCase } from '@/core/utils'
+import { getUID, pascalCase, raiseEvent } from '@/core/utils'
import { getJsonWithCredentials } from '@/core/ajax'
import { formatCount, formatDuration, parseCount, parseDuration } from '@/core/utils/formatters'
import { watchlaterList } from '@/components/video/watchlater'
@@ -6,6 +6,8 @@ import { getData, registerData } from '@/plugins/data'
import { descendingStringSort } from '@/core/utils/sort'
import { VideoCard } from '../video-card'
import { FeedsCard, FeedsCardType, feedsCardTypes } from './types'
+import { childList } from '@/core/observer'
+import { select } from '@/core/spin-query'
export * from './types'
export * from './manager'
@@ -189,7 +191,7 @@ export const addMenuItem = (
) => {
const morePanel = dq(
card.element,
- '.more-panel, .bili-dyn-more__menu, .opus-more__menu',
+ '.more-panel, .bili-dyn-more__menu, .opus-more__menu, .bili-dyn-item__more, .opus-more',
) as HTMLElement
const { className, text, action } = config
if (!morePanel || dq(morePanel, `.${className}`)) {
@@ -198,43 +200,92 @@ export const addMenuItem = (
}
const isV2 = !morePanel.classList.contains('more-panel')
const isOpus = morePanel.classList.contains('opus-more__menu')
- const menuItem = document.createElement(isV2 ? 'div' : 'p')
- if (isOpus) {
- menuItem.classList.add('opus-more__menu__item', className)
- const styleReferenceElement = morePanel.children[0] as HTMLElement
- if (styleReferenceElement) {
- menuItem.setAttribute('style', styleReferenceElement.getAttribute('style'))
+ const isCascader =
+ morePanel.classList.contains('bili-dyn-item__more') || morePanel.classList.contains('opus-more')
+
+ const createMenuItem = (): HTMLElement => {
+ if (isCascader) {
+ const menuItem = document.createElement('div')
+ menuItem.innerHTML = /* html */ `
+
+ `
+ return menuItem
}
- menuItem.dataset.type = 'more'
- menuItem.dataset.stype = lodash.snakeCase(`ThreePoint${pascalCase(className)}`).toUpperCase()
- menuItem.dataset.params = '{}'
- } else if (isV2) {
- menuItem.classList.add('bili-dyn-more__menu__item', className)
- const styleReferenceElement = morePanel.children[0] as HTMLElement
- if (styleReferenceElement) {
- menuItem.setAttribute('style', styleReferenceElement.getAttribute('style'))
+
+ const menuItem = document.createElement(isV2 ? 'div' : 'p')
+ if (isOpus) {
+ menuItem.classList.add('opus-more__menu__item', className)
+ const styleReferenceElement = morePanel.children[0] as HTMLElement
+ if (styleReferenceElement) {
+ menuItem.setAttribute('style', styleReferenceElement.getAttribute('style'))
+ }
+ menuItem.dataset.type = 'more'
+ menuItem.dataset.stype = lodash.snakeCase(`ThreePoint${pascalCase(className)}`).toUpperCase()
+ menuItem.dataset.params = '{}'
+ } else if (isV2) {
+ menuItem.classList.add('bili-dyn-more__menu__item', className)
+ const styleReferenceElement = morePanel.children[0] as HTMLElement
+ if (styleReferenceElement) {
+ menuItem.setAttribute('style', styleReferenceElement.getAttribute('style'))
+ } else {
+ menuItem.style.height = '25px'
+ menuItem.style.padding = '2px 0'
+ menuItem.style.textAlign = 'center'
+ }
+ menuItem.dataset.module = 'more'
+ menuItem.dataset.type = lodash.snakeCase(`ThreePoint${pascalCase(className)}`).toUpperCase()
+ menuItem.dataset.params = '{}'
} else {
- menuItem.style.height = '25px'
- menuItem.style.padding = '2px 0'
- menuItem.style.textAlign = 'center'
+ menuItem.classList.add('child-button', 'c-pointer', className)
}
- menuItem.dataset.module = 'more'
- menuItem.dataset.type = lodash.snakeCase(`ThreePoint${pascalCase(className)}`).toUpperCase()
- menuItem.dataset.params = '{}'
- } else {
- menuItem.classList.add('child-button', 'c-pointer', className)
+ menuItem.textContent = text
+ const vueScopeAttributes = [
+ ...new Set(
+ [...morePanel.children]
+ .map((element: HTMLElement) =>
+ element.getAttributeNames().filter(it => it.startsWith('data-v-')),
+ )
+ .flat(),
+ ),
+ ]
+ vueScopeAttributes.forEach(attr => menuItem.setAttribute(attr, ''))
+ return menuItem
}
- menuItem.textContent = text
- const vueScopeAttributes = [
- ...new Set(
- [...morePanel.children]
- .map((element: HTMLElement) =>
- element.getAttributeNames().filter(it => it.startsWith('data-v-')),
- )
- .flat(),
- ),
- ]
- vueScopeAttributes.forEach(attr => menuItem.setAttribute(attr, ''))
+
+ if (isCascader) {
+ ;(async () => {
+ const cascader = await select(() => dq(morePanel, '.bili-cascader'))
+ const [observer] = childList(cascader, () => {
+ const cascaderOptions = dq(cascader, '.bili-cascader-options')
+ if (cascaderOptions === null) {
+ return
+ }
+ observer.disconnect()
+ const menuItem = createMenuItem()
+ menuItem.addEventListener('click', e => {
+ action(e)
+ const triggerButton = dq(morePanel, '.bili-dyn-more__btn') as HTMLElement | null
+ if (triggerButton !== null) {
+ raiseEvent(triggerButton, 'mouseleave')
+ } else {
+ raiseEvent(morePanel, 'mouseleave')
+ }
+ })
+ cascaderOptions.appendChild(menuItem)
+ })
+ })()
+ return
+ }
+
+ const menuItem = createMenuItem()
menuItem.addEventListener('click', e => {
action(e)
card.element.click()
diff --git a/src/components/feeds/api/manager/v2.ts b/src/components/feeds/api/manager/v2.ts
index 06c45e1307..1c523847d2 100644
--- a/src/components/feeds/api/manager/v2.ts
+++ b/src/components/feeds/api/manager/v2.ts
@@ -124,6 +124,7 @@ const parseCard = async (element: HTMLElement): Promise => {
}
card.getText = async () =>
combineText(getText(modules.module_dynamic, cardType), getText(repostDynamicModule, cardType))
+ card.repostId = vueData.data.orig.id_str
}
card.text = await card.getText()
card.element.setAttribute('data-did', card.id)
diff --git a/src/components/feeds/api/types.ts b/src/components/feeds/api/types.ts
index 5280bfdc40..0baa82e923 100644
--- a/src/components/feeds/api/types.ts
+++ b/src/components/feeds/api/types.ts
@@ -121,6 +121,8 @@ export interface RepostFeedsCard extends FeedsCard {
repostUsername: string
/** 被转发动态的内容 */
repostText: string
+ /** 被转发动态的 ID */
+ repostId: string
type: RepostFeedsCardType
}
/**
diff --git a/src/components/launch-bar/LaunchBar.vue b/src/components/launch-bar/LaunchBar.vue
index b1a9050df7..4a2b8bce75 100644
--- a/src/components/launch-bar/LaunchBar.vue
+++ b/src/components/launch-bar/LaunchBar.vue
@@ -115,10 +115,12 @@ async function getOnlineActions() {
}
const fuse = new Fuse(onlineActions, {
keys: ['indexer', 'displayName', 'name', 'description', 'key'],
+ includeScore: true,
+ threshold: 0.1,
})
const fuseResult = fuse.search(this.keyword)
console.log(fuseResult)
- this.actions = sortActions(fuseResult.map(it => it.item).slice(0, 12))
+ this.actions = sortActions(fuseResult.map(it => it.item).slice(0, 13))
this.noActions = this.actions.length === 0
}
async function getActions() {
@@ -186,10 +188,12 @@ export default Vue.extend({
if (!input) {
return
}
- this.keyword = input.value
urlChange(url => {
const params = new URLSearchParams(url)
- this.keyword = params.get('keyword')
+ const keywordFromParam = params.get('keyword')
+ if (keywordFromParam !== null) {
+ this.keyword = params.get('keyword')
+ }
})
await this.$nextTick()
},
diff --git a/src/components/launch-bar/search-provider.ts b/src/components/launch-bar/search-provider.ts
index 8b6cb81126..5e7284cfb8 100644
--- a/src/components/launch-bar/search-provider.ts
+++ b/src/components/launch-bar/search-provider.ts
@@ -1,4 +1,4 @@
-import { formData, getUID } from '@/core/utils'
+import { getUID } from '@/core/utils'
import { getJson } from '@/core/ajax'
import { LaunchBarAction, LaunchBarActionProvider } from './launch-bar-action'
import { addHistoryItem } from './history-provider'
@@ -12,7 +12,7 @@ export const search = (keyword: string) => {
keyword,
from_source: 'nav_suggest_new',
}
- window.open(`https://search.bilibili.com/all?${formData(params)}`, '_blank')
+ window.open(`https://search.bilibili.com/all?${new URLSearchParams(params)}`, '_blank')
}
export const searchProvider: LaunchBarActionProvider = {
name: 'search',
@@ -48,7 +48,7 @@ export const searchProvider: LaunchBarActionProvider = {
}
results.push(
...suggests.map(result => ({
- name: result.value,
+ name: `${input}.${result.value}`,
icon: 'search',
content: async () =>
Vue.extend({
diff --git a/src/components/settings-panel/sub-pages/online-registry/OnlineRegistry.vue b/src/components/settings-panel/sub-pages/online-registry/OnlineRegistry.vue
index f3251253d1..66a0b9606e 100644
--- a/src/components/settings-panel/sub-pages/online-registry/OnlineRegistry.vue
+++ b/src/components/settings-panel/sub-pages/online-registry/OnlineRegistry.vue
@@ -52,7 +52,7 @@
-
+