mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge branch 'preview-fixes' into preview-features
This commit is contained in:
commit
00d8e8a0a2
@ -82,7 +82,7 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"caniuse-lite": "1.0.30001547",
|
||||
"caniuse-lite": "1.0.30001609",
|
||||
"consolidate": "1.0.1"
|
||||
}
|
||||
},
|
||||
|
||||
6135
pnpm-lock.yaml
generated
6135
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,7 @@
|
||||
}
|
||||
.at-panel,
|
||||
.emoji-panel,
|
||||
.reply-box .box-expand,
|
||||
.reply-operation .operation-list {
|
||||
z-index: 111 !important;
|
||||
}
|
||||
|
||||
@ -286,9 +286,9 @@ const decode = lodash.curry(async (type: string, blob: Blob) => {
|
||||
export const decodeDanmakuSegment = decode('DmSegMobileReply')
|
||||
export const decodeDanmakuView = decode('DmWebViewReply')
|
||||
|
||||
// 这里为了兼容 pakku, 只能用 fetch https://github.com/xmcp/pakku.js/issues/153
|
||||
// 同时安装了 pakku 时,unsafeWindow.fetch 将调用 hook 过的 fetch 来获取修改后的弹幕
|
||||
const fetchBlob = async (url: string) => {
|
||||
const response = await fetch(url, { mode: 'cors', credentials: 'include' })
|
||||
const response = await unsafeWindow.fetch(url, { mode: 'cors', credentials: 'include' })
|
||||
return response.blob()
|
||||
}
|
||||
export const getDanmakuView = async (aid: string | number, cid: string | number) => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { getVueData } from '@/components/feeds/api'
|
||||
import { playerAgent } from '@/components/video/player-agent'
|
||||
import { childListSubtree, videoChange } from '@/core/observer'
|
||||
import { select } from '@/core/spin-query'
|
||||
@ -44,9 +45,50 @@ export const component = defineComponentMetadata({
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
await playerReady()
|
||||
|
||||
// 初始化状态
|
||||
const rightPanel = await Promise.any([
|
||||
select('.right-container-inner'),
|
||||
select('.playlist-container--right'),
|
||||
])
|
||||
// 如果未找到元素,提前退出不执行后续动作
|
||||
if (!rightPanel) {
|
||||
console.warn('未找到 rightPanelContainer 或 playListContainer')
|
||||
return
|
||||
}
|
||||
|
||||
const checkPlayListPlayMode = async () => {
|
||||
// 检查是不是播放列表中的最后一个视频或者最后一个分 P 视频的最后一个视频
|
||||
const videoSequentialNumber = dq('.list-count')
|
||||
const sequentialNumbers = videoSequentialNumber.innerHTML.split('/')
|
||||
// 检查最后一个元素是单个视频还是多 P 视频
|
||||
const lastVedioElement = dq(
|
||||
'.action-list .action-list-inner .action-list-item-wrap:last-child .action-list-item .actionlist-item-inner',
|
||||
)
|
||||
|
||||
let isLastVideo = false
|
||||
if (lastVedioElement.classList.contains('singlep-list-item-inner')) {
|
||||
isLastVideo = lastVedioElement.classList.contains('siglep-active')
|
||||
} else {
|
||||
isLastVideo =
|
||||
lastVedioElement.children[1].lastElementChild.classList.contains(
|
||||
'multip-list-item-active',
|
||||
)
|
||||
}
|
||||
|
||||
const shouldContinue = !(sequentialNumbers[0] >= sequentialNumbers[1] && isLastVideo)
|
||||
|
||||
const app = document.getElementById('app')
|
||||
const vueInstance = getVueData(app)
|
||||
// 不用判断当前状态是什么,直接将将是否需要继续播放赋值 vue 实例中的 continuousPlay
|
||||
vueInstance.setContinuousPlay(shouldContinue)
|
||||
}
|
||||
|
||||
const isChecked = (container: HTMLElement) =>
|
||||
Boolean(container.querySelector('.switch-button.on') || container.matches(':checked'))
|
||||
await playerReady()
|
||||
|
||||
const checkPlayMode = async () => {
|
||||
const element = (await select(
|
||||
[...autoPlayControls.disable, ...autoPlayControls.enable].join(','),
|
||||
@ -62,16 +104,19 @@ export const component = defineComponentMetadata({
|
||||
element.click()
|
||||
}
|
||||
}
|
||||
videoChange(async () => {
|
||||
checkPlayMode()
|
||||
const video = (await playerAgent.query.video.element()) as HTMLVideoElement
|
||||
video?.addEventListener('play', checkPlayMode, { once: true })
|
||||
})
|
||||
const rightPanelContainer = await select('.right-container-inner')
|
||||
if (!rightPanelContainer) {
|
||||
console.warn('未找到 rightPanelContainer')
|
||||
return
|
||||
|
||||
const checkRightPanelPlayMode = async () => {
|
||||
rightPanel.classList.contains('right-container-inner')
|
||||
? checkPlayMode()
|
||||
: checkPlayListPlayMode()
|
||||
}
|
||||
childListSubtree(rightPanelContainer, () => checkPlayMode())
|
||||
|
||||
videoChange(async () => {
|
||||
checkRightPanelPlayMode()
|
||||
const video = (await playerAgent.query.video.element()) as HTMLVideoElement
|
||||
video?.addEventListener('play', checkRightPanelPlayMode, { once: true })
|
||||
})
|
||||
|
||||
childListSubtree(rightPanel, () => checkRightPanelPlayMode())
|
||||
},
|
||||
})
|
||||
|
||||
@ -1 +1 @@
|
||||
删除视频播放器中出现的各种弹窗, 类别可在选项中分别选择.
|
||||
删除视频播放器中出现的各种弹窗, 类别可在选项中分别选择. 如果之前点了收起弹窗, 则收起后的小弹窗则会直接删除. (不受类别选择影响)
|
||||
|
||||
@ -8,6 +8,13 @@ export const component = defineComponentMetadata({
|
||||
entry: ({ settings, metadata }) => {
|
||||
const { options } = settings
|
||||
const { kebabCase } = lodash
|
||||
addComponentListener(
|
||||
metadata.name,
|
||||
(enabled: boolean) => {
|
||||
document.body.classList.toggle(kebabCase(metadata.name), enabled)
|
||||
},
|
||||
true,
|
||||
)
|
||||
Object.keys(options).forEach(optionName => {
|
||||
addComponentListener(
|
||||
`${metadata.name}.${optionName}`,
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
body {
|
||||
&.remove-player-popup {
|
||||
// https://www.bilibili.com/video/BV1WA4m1L7xa/
|
||||
.bili-cmd-shrink {
|
||||
display: none !important;
|
||||
}
|
||||
// https://www.bilibili.com/video/BV1V54y1b7yW/
|
||||
&-combo-likes {
|
||||
.bili-guide,
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { childList, urlChange } from '@/core/observer'
|
||||
import { playerReady } from '@/core/utils'
|
||||
import { playerReady, getVue2Data } from '@/core/utils'
|
||||
import { videoUrls } from '@/core/utils/urls'
|
||||
import { getVueData } from '@/components/feeds/api'
|
||||
import { VideoInfo } from '@/components/video/video-info'
|
||||
import { useScopedConsole } from '@/core/utils/log'
|
||||
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
||||
@ -147,7 +146,7 @@ export const component = defineComponentMetadata({
|
||||
|
||||
const getRecoList = () => {
|
||||
const reco_list = dq('#reco_list')
|
||||
let recoList: RecommendList = getVueData(reco_list)
|
||||
let recoList: RecommendList = getVue2Data(reco_list)
|
||||
if (recoList.isOpen === undefined) {
|
||||
recoList = recoList.$children[0]
|
||||
if (recoList.isOpen === undefined) {
|
||||
|
||||
@ -4,10 +4,11 @@ export const plugin: PluginMetadata = {
|
||||
name: 'feedsFilter.pluginBlocks.chargeFeeds',
|
||||
displayName: '动态过滤器 - 移除充电专属动态',
|
||||
async setup() {
|
||||
const { forEachFeedsCard, getVueData } = await import('@/components/feeds/api')
|
||||
const { getVue2Data } = await import('@/core/utils')
|
||||
const { forEachFeedsCard } = await import('@/components/feeds/api')
|
||||
forEachFeedsCard({
|
||||
added: card => {
|
||||
const vueData = getVueData(card.element)
|
||||
const vueData = getVue2Data(card.element)
|
||||
const blockedType: number | null = lodash.get(
|
||||
vueData,
|
||||
'data.modules.module_dynamic.major.blocked.blocked_type',
|
||||
|
||||
@ -34,21 +34,40 @@ async function load(toast: Toast) {
|
||||
),
|
||||
})
|
||||
}
|
||||
export async function run(name: string, videoUrl: string, audioUrl: string, toast: Toast) {
|
||||
export async function run(
|
||||
name: string,
|
||||
toast: Toast,
|
||||
videoUrl: string,
|
||||
audioUrl: string,
|
||||
isFlac: boolean,
|
||||
) {
|
||||
if (!ffmpeg.loaded) {
|
||||
await load(toast)
|
||||
}
|
||||
|
||||
ffmpeg.writeFile('video', await httpGet(videoUrl, toastProgress(toast, '正在下载视频流')))
|
||||
ffmpeg.writeFile('audio', await httpGet(audioUrl, toastProgress(toast, '正在下载音频流')))
|
||||
|
||||
toast.message = '混流中……'
|
||||
|
||||
await ffmpeg.exec(['-i', 'video', '-i', 'audio', '-c:v', 'copy', '-c:a', 'copy', 'output.mp4'])
|
||||
const outputExt = isFlac ? 'mkv' : 'mp4'
|
||||
name = isFlac ? name.replace(/.[^/.]+$/, `.${outputExt}`) : name
|
||||
await ffmpeg.exec([
|
||||
'-i',
|
||||
'video',
|
||||
'-i',
|
||||
'audio',
|
||||
'-c:v',
|
||||
'copy',
|
||||
'-c:a',
|
||||
isFlac ? 'flac' : 'copy',
|
||||
'-f',
|
||||
isFlac ? 'matroska' : 'mp4',
|
||||
`output.${outputExt}`,
|
||||
])
|
||||
|
||||
const output = await ffmpeg.readFile('output.mp4')
|
||||
const output = await ffmpeg.readFile(`output.${outputExt}`)
|
||||
const outputBlob = new Blob([output], {
|
||||
type: 'video/mp4',
|
||||
type: isFlac ? 'video/x-matroska' : 'video/mp4',
|
||||
})
|
||||
|
||||
toast.message = '完成!'
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { Toast } from '@/core/toast'
|
||||
import { Toast, ToastType } from '@/core/toast'
|
||||
import { PluginMetadata } from '@/plugins/plugin'
|
||||
import { DownloadVideoOutput } from '../../../../components/video/download/types'
|
||||
import { run } from './handler'
|
||||
import { getComponentSettings } from '@/core/settings'
|
||||
import type { Options } from '../../../../components/video/download'
|
||||
|
||||
const title = 'WASM 混流输出'
|
||||
const desc = '使用 WASM 在浏览器中下载并合并音视频'
|
||||
@ -22,12 +24,35 @@ export const plugin: PluginMetadata = {
|
||||
description: `${desc},运行过程中请勿关闭页面,初次使用或清除缓存后需要加载约 30 MB 的 WASM 文件`,
|
||||
runAction: async action => {
|
||||
const fragments = action.infos.flatMap(it => it.titledFragments)
|
||||
if (fragments.length !== 2) {
|
||||
Toast.error('仅支持 DASH 格式', title)
|
||||
const { dashAudioExtension, dashFlacAudioExtension, dashVideoExtension } =
|
||||
getComponentSettings<Options>('downloadVideo').options
|
||||
|
||||
if (
|
||||
fragments.length > 2 ||
|
||||
(fragments.length === 2 &&
|
||||
!(
|
||||
fragments[0].extension === dashVideoExtension &&
|
||||
(fragments[1].extension === dashAudioExtension ||
|
||||
fragments[1].extension === dashFlacAudioExtension)
|
||||
))
|
||||
) {
|
||||
Toast.error('仅支持单个视频下载', title).duration = 1500
|
||||
} else {
|
||||
const toast = Toast.info('正在加载', title)
|
||||
try {
|
||||
await run(fragments[0].title, fragments[0].url, fragments[1].url, toast)
|
||||
if (fragments.length === 2) {
|
||||
await run(
|
||||
fragments[0].title,
|
||||
toast,
|
||||
fragments[0].url,
|
||||
fragments[1].url,
|
||||
fragments[1].extension === dashFlacAudioExtension,
|
||||
) // [dash]
|
||||
} else {
|
||||
toast.type = ToastType.Error
|
||||
toast.message = '仅支持 dash 格式视频下载'
|
||||
toast.duration = 1500
|
||||
}
|
||||
} catch (error) {
|
||||
toast.close()
|
||||
Toast.error(String(error), title)
|
||||
|
||||
@ -5,9 +5,8 @@ import { ListAdaptorKey, FeedsCardsListAdaptor } from './adaptor'
|
||||
|
||||
export const feedsCardCallbacks: Required<FeedsCardCallback>[] = []
|
||||
|
||||
export const getVueData = (el: any) =>
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
el.__vue__ ?? el.parentElement.__vue__ ?? el.children[0].__vue__
|
||||
// TODO: 仅做兼容性导出, v2.8.11 之后可移除
|
||||
export { getVue2Data as getVueData } from '@/core/utils'
|
||||
|
||||
export const createNodeValidator =
|
||||
(className: string) =>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { childList } from '@/core/observer'
|
||||
import { getVue2Data } from '@/core/utils'
|
||||
import { sq } from '@/core/spin-query'
|
||||
import { FeedsCardsManager, FeedsCardsManagerEventType, getVueData } from './base'
|
||||
import { FeedsCardsManager, FeedsCardsManagerEventType } from './base'
|
||||
import { feedsCardTypes, isRepostType, FeedsCard, FeedsCardType } from '../types'
|
||||
|
||||
const getFeedsCardType = (element: HTMLElement) => {
|
||||
@ -85,7 +86,7 @@ const parseCard = async (element: HTMLElement): Promise<FeedsCard> => {
|
||||
}
|
||||
const el = await sq(
|
||||
() => element,
|
||||
(it: any) => Boolean(getVueData(it) || !element.parentNode),
|
||||
(it: any) => Boolean(getVue2Data(it) || !element.parentNode),
|
||||
{ queryInterval: 100 },
|
||||
)
|
||||
if (element.parentNode === null) {
|
||||
@ -93,10 +94,10 @@ const parseCard = async (element: HTMLElement): Promise<FeedsCard> => {
|
||||
return ''
|
||||
}
|
||||
if (el === null) {
|
||||
console.warn(el, element, getVueData(el), element.parentNode)
|
||||
console.warn(el, element, getVue2Data(el), element.parentNode)
|
||||
return ''
|
||||
}
|
||||
const vueData = getVueData(el)
|
||||
const vueData = getVue2Data(el)
|
||||
if (type === feedsCardTypes.repost) {
|
||||
const currentText = vueData.card.item.content
|
||||
const repostData = getRepostData(vueData)
|
||||
@ -135,7 +136,7 @@ const parseCard = async (element: HTMLElement): Promise<FeedsCard> => {
|
||||
element.setAttribute('data-type', card.type.id.toString())
|
||||
if (isRepostType(card)) {
|
||||
const currentUsername = card.username
|
||||
const vueData = getVueData(card.element)
|
||||
const vueData = getVue2Data(card.element)
|
||||
const repostUsername = lodash.get(vueData, 'card.origin_user.info.uname', '')
|
||||
if (currentUsername === repostUsername) {
|
||||
element.setAttribute('data-self-repost', 'true')
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import { childList } from '@/core/observer'
|
||||
import { descendingStringSort } from '@/core/utils/sort'
|
||||
import { pascalCase } from '@/core/utils'
|
||||
import {
|
||||
createNodeValidator,
|
||||
FeedsCardsManager,
|
||||
FeedsCardsManagerEventType,
|
||||
getVueData,
|
||||
} from './base'
|
||||
import { pascalCase, getVue2Data } from '@/core/utils'
|
||||
import { createNodeValidator, FeedsCardsManager, FeedsCardsManagerEventType } from './base'
|
||||
import { FeedsCard, FeedsCardType, feedsCardTypes, isRepostType } from '../types'
|
||||
import { selectAll } from '@/core/spin-query'
|
||||
|
||||
@ -78,7 +73,7 @@ const getText = (dynamicModule: any, cardType: FeedsCardType) => {
|
||||
return combineText(mainText, typeText)
|
||||
}
|
||||
const parseCard = async (element: HTMLElement): Promise<FeedsCard> => {
|
||||
const vueData = getVueData(element)
|
||||
const vueData = getVue2Data(element)
|
||||
const parseModules = (rawModules: any) => {
|
||||
if (Array.isArray(rawModules)) {
|
||||
return Object.fromEntries(
|
||||
@ -157,7 +152,7 @@ export class FeedsCardsManagerV2 extends FeedsCardsManager {
|
||||
if (!isNodeValid(node)) {
|
||||
return
|
||||
}
|
||||
const vueData = getVueData(node)
|
||||
const vueData = getVue2Data(node)
|
||||
if (!vueData) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { dqa } from '@/core/utils'
|
||||
import { dqa, getVue2Data } from '@/core/utils'
|
||||
import { allMutations, childList, childListSubtree } from '@/core/observer'
|
||||
import { contentLoaded } from '@/core/life-cycle'
|
||||
|
||||
@ -98,15 +98,14 @@ const itemRemovedCallbacks: CommentItemCallback[] = []
|
||||
const commentAreaCallbacks: CommentAreaCallback[] = []
|
||||
|
||||
/** (v2) 获取 Vue 数据 (评论 / 回复) */
|
||||
const getVueData = (element: HTMLElement) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const props = (element as any).__vueParentComponent?.props
|
||||
const getReplyFromVueData = (element: HTMLElement) => {
|
||||
const props = getVue2Data(element)?.props
|
||||
return props?.reply ?? props?.subReply
|
||||
}
|
||||
/** (v2) 获取回复对应的元素 */
|
||||
const getReplyItemElement = (parent: HTMLElement, replyId: string) => {
|
||||
const [replyElement] = dqa(parent, '.sub-reply-item').filter(
|
||||
(it: HTMLElement) => getVueData(it).rpid_str === replyId,
|
||||
(it: HTMLElement) => getReplyFromVueData(it).rpid_str === replyId,
|
||||
)
|
||||
return replyElement as HTMLElement
|
||||
}
|
||||
@ -116,13 +115,13 @@ const getCommentId = (element: HTMLElement) => {
|
||||
if (attributeId) {
|
||||
return attributeId
|
||||
}
|
||||
const vueData = getVueData(element)
|
||||
const vueData = getReplyFromVueData(element)
|
||||
return vueData?.rpid_str ?? ''
|
||||
}
|
||||
|
||||
/** (v2) 解析评论对象 */
|
||||
const parseCommentItemV2 = (element: HTMLElement) => {
|
||||
const vueData = getVueData(element)
|
||||
const vueData = getReplyFromVueData(element)
|
||||
if (!vueData) {
|
||||
throw new Error('Invalid comment item')
|
||||
}
|
||||
|
||||
@ -701,3 +701,8 @@ export const simulateClick = (target: EventTarget, eventParams?: PointerEventIni
|
||||
target.dispatchEvent(mouseUpEvent)
|
||||
target.dispatchEvent(clickEventEvent)
|
||||
}
|
||||
|
||||
/** 尝试获取元素对应的 Vue Data (仅适用于 Vue 2 组件) */
|
||||
export const getVue2Data = (el: any) =>
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
el.__vue__ ?? el.parentElement.__vue__ ?? el.children[0].__vue__ ?? el.__vueParentComponent
|
||||
|
||||
Loading…
Reference in New Issue
Block a user