From 91329e21a4a89f568fe7c5a15b2f4a56a9357ec2 Mon Sep 17 00:00:00 2001 From: the1812 Date: Tue, 10 Sep 2024 22:48:31 +0800 Subject: [PATCH 01/49] Fix medal style (#4895) --- .../components/live/chat-panel-fit/chat-panel-fit.scss | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/registry/lib/components/live/chat-panel-fit/chat-panel-fit.scss b/registry/lib/components/live/chat-panel-fit/chat-panel-fit.scss index f3d150d8d5..b07258ce78 100644 --- a/registry/lib/components/live/chat-panel-fit/chat-panel-fit.scss +++ b/registry/lib/components/live/chat-panel-fit/chat-panel-fit.scss @@ -21,9 +21,13 @@ } } } - .chat-input-ctnr-new .medal-section { - min-width: 0 !important; - max-width: 0 !important; + .chat-input-ctnr-new { + .medal-section { + display: none !important; + } + .chat-input-new:not(:has(textarea.focus)) { + padding-left: 12px !important; + } } } } From d6f4d244d932ec84871a5f93af5f6fe16d2c58f2 Mon Sep 17 00:00:00 2001 From: the1812 Date: Tue, 10 Sep 2024 23:03:40 +0800 Subject: [PATCH 02/49] Fix fold comment not work for CommentAreaV2 (fix #4905) --- .../lib/components/feeds/fold-comments/fold-comment.scss | 3 +++ registry/lib/components/feeds/fold-comments/index.ts | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/registry/lib/components/feeds/fold-comments/fold-comment.scss b/registry/lib/components/feeds/fold-comments/fold-comment.scss index 66f4b9e91d..441e18c0fa 100644 --- a/registry/lib/components/feeds/fold-comments/fold-comment.scss +++ b/registry/lib/components/feeds/fold-comments/fold-comment.scss @@ -28,10 +28,13 @@ } } .bili-comment-container { + display: flex !important; + flex-direction: column !important; &.bili-dyn-comment .reply-list { padding-bottom: 8px !important; } .fold-comment { + order: 1; font-size: 13px; } .at-panel, diff --git a/registry/lib/components/feeds/fold-comments/index.ts b/registry/lib/components/feeds/fold-comments/index.ts index 4ddce9bc21..724a776745 100644 --- a/registry/lib/components/feeds/fold-comments/index.ts +++ b/registry/lib/components/feeds/fold-comments/index.ts @@ -32,10 +32,7 @@ const entry = async () => { } if (feedsCardsManager.managerType === 'v2') { const getExistingComment = () => dq(card, commentSelector) as HTMLElement - const isCommentAreaReady = () => { - const existingComment = getExistingComment() - return existingComment !== null && dq(existingComment, 'bili-comments') - } + const isCommentAreaReady = () => getExistingComment() !== null const handler = () => { const button = dq(card, '.bili-dyn-action.comment') as HTMLElement button?.click() From 14066617fca36e291a8e7edb54ae8e2d623d65d9 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sat, 14 Sep 2024 22:44:55 +0800 Subject: [PATCH 03/49] Improve UI and code styles --- .../video/download/DownloadVideo.vue | 3 ++ .../video/download/aria2-output/RpcConfig.vue | 25 +++++++++--- .../video/download/aria2-output/aria2-rpc.ts | 38 ++++++++++--------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/registry/lib/components/video/download/DownloadVideo.vue b/registry/lib/components/video/download/DownloadVideo.vue index 6a1a637cd8..24b73efb0e 100644 --- a/registry/lib/components/video/download/DownloadVideo.vue +++ b/registry/lib/components/video/download/DownloadVideo.vue @@ -49,6 +49,9 @@
使用备用下载地址:
+
+ 若默认下载地址速度缓慢, 可以尝试更换备用下载地址. +
-
-
aria2下载附属资源(若支持):
- +
+
+
使用 aria2 下载附属资源:
+ +
+
+ 存在于服务器的附属资源 (例如封面) 可以一并发送到 aria2 下载. +
重命名 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) From 26a219b45f00f64d588b8bc74bf4e599f1e1b3c5 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 15 Sep 2024 09:51:43 +0800 Subject: [PATCH 04/49] Update changelog --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6bf39b5c6..eda46a55ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,45 @@ # 更新日志 +## v2.9.3-preview +主要是各种修复, 见 [v2.9.3](https://github.com/the1812/Bilibili-Evolved/releases/tag/v2.9.3). + +## v2.9.3 + +
+正式版用户将获得 v2.9.1-preview ~ v2.9.2-preview 的所有改动, 点击展开查看 + +✨新增 +- `查看封面` 可以为 aria2 输出提供直接的封面下载. (PR #4798 by [Oxygenくん](https://github.com/oxygenkun)) +- 新增组件 `保存视频元数据`. (PR #4840 by [WakelessSloth56](https://github.com/WakelessSloth56)) +> - 保存视频元数据为 [FFMETADATA](https://ffmpeg.org/ffmpeg-formats.html#Metadata-2) 格式 +> - 使用组件 `下载视频` 时指定 `WASM` 输出方式(插件 `下载视频 - WASM 混流输出`)可选择是否直接混流入输出文件。 +> - 保存视频章节为 OGM 格式 (https://github.com/the1812/Bilibili-Evolved/discussions/2069#discussioncomment-10110916) + +- `简化首页` 支持隐藏轮播图. (PR #4852 by [Lime](https://github.com/Liumingxun)) +- 新增组件 `添加直播间用户超链接`. (PR #4856 by [Light_Quanta](https://github.com/LightQuanta)) +> 网页版直播间右上角的房间观众和大航海界面的用户列表只可查看用户名,不可进行点击。该组件为用户头像和用户名称处添加点击效果,允许通过点击直接查看用户空间。 + +- 插件 `下载视频 - WASM 混流输出` 支持并行下载库和音视频流. (PR #4864 by [WakelessSloth56](https://github.com/WakelessSloth56)) +- `弹幕转义` 支持对正斜杠的换行 (`/n`) 进行转义. (#4865) +- `自定义顶栏` 支持直接在功能中打开布局设置. (#2666) +- `高分辨率图片` 支持处理没有指定高度的图片, 支持在专栏页面中请求原图. (#2868) +- `直播间网页全屏自适应` 样式适配较低的宽度值. (#4895) + +☕开发者相关 +- 外部资源接入 Subresource Integrity. (#4896) + +
+ +🐛修复 +- 修复 `快速收起评论` 对旧版评论区的兼容性. (#4905) +- 修复 `快捷键扩展` 的发送评论在新版评论区失效. (#4843) +- 修复 `禁用评论区搜索词` 偶现样式失效. (#4843) + +☕开发者相关 +- `ShadowDomObserver` 在使用前无需再调用 `observe()`. +- 增加工具方法 `getActiveElement` 检测当前页面的聚焦元素. + ## v2.9.2-preview `2024-09-08` From 36eb12574a966742847860e79bf6811d73800a1e Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 15 Sep 2024 09:51:54 +0800 Subject: [PATCH 05/49] Update version number --- src/client/common.meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/common.meta.json b/src/client/common.meta.json index 16baa7869c..f5a6c2d70f 100644 --- a/src/client/common.meta.json +++ b/src/client/common.meta.json @@ -1,5 +1,5 @@ { - "version": "2.9.2", + "version": "2.9.3", "author": "Grant Howard, Coulomb-G", "copyright": "[year], Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)", "license": "MIT", From 00723a9845e59ba58d75daff6c334c373718c6ba Mon Sep 17 00:00:00 2001 From: gouzi <530971494@qq.com> Date: Tue, 17 Sep 2024 10:07:50 +0800 Subject: [PATCH 06/49] feat: add del self feeds --- .../lib/components/feeds/del-feeds/Widget.vue | 42 ++++++ .../feeds/del-feeds/check-in-item.ts | 134 ++++++++++++++++++ .../lib/components/feeds/del-feeds/index.md | 1 + .../lib/components/feeds/del-feeds/index.ts | 16 +++ src/components/feeds/api/manager/v2.ts | 1 + src/components/feeds/api/types.ts | 2 + 6 files changed, 196 insertions(+) create mode 100644 registry/lib/components/feeds/del-feeds/Widget.vue create mode 100644 registry/lib/components/feeds/del-feeds/check-in-item.ts create mode 100644 registry/lib/components/feeds/del-feeds/index.md create mode 100644 registry/lib/components/feeds/del-feeds/index.ts diff --git a/registry/lib/components/feeds/del-feeds/Widget.vue b/registry/lib/components/feeds/del-feeds/Widget.vue new file mode 100644 index 0000000000..12321d57c8 --- /dev/null +++ b/registry/lib/components/feeds/del-feeds/Widget.vue @@ -0,0 +1,42 @@ + + + diff --git a/registry/lib/components/feeds/del-feeds/check-in-item.ts b/registry/lib/components/feeds/del-feeds/check-in-item.ts new file mode 100644 index 0000000000..aa06777df0 --- /dev/null +++ b/registry/lib/components/feeds/del-feeds/check-in-item.ts @@ -0,0 +1,134 @@ +import { getJsonWithCredentials, postJsonWithCredentials } from '@/core/ajax' +import { Toast } from '@/core/toast' +import { getUID, getCsrf } from '@/core/utils' +import { registerAndGetData } from '@/plugins/data' + +export interface CheckInItem { + name: string + displayName: string + icon: string + action: (button: HTMLDivElement, event: MouseEvent) => Promise + disabled?: boolean +} +const builtInItems: CheckInItem[] = [ + { + name: 'del-all-feeds', + displayName: '删除所有动态', + icon: 'mdi-delete', + action: async () => { + const { forEachFeedsCard } = await import('@/components/feeds/api') + Toast.info('如果想删除所有动态, 可以一直下拉页面到没有动态为止', '删除动态', 3000) + + // 只能删除本页 + await forEachFeedsCard({ + added: async card => { + // 检查是否是转发动态 + const post_del_dynamic_data = { + dyn_id_str: card.id, + dyn_type: card.type.id, + rid_str: card.id, + } + // 删除动态 + const del_dynamic_resp = JSON.parse( + await postJsonWithCredentials( + `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${getCsrf()}`, + post_del_dynamic_data, + ), + ) as { + code: number + message: string + ttl: number + data: null + } + + if (del_dynamic_resp.code === 0) { + console.info('删除动态成功: ', card.id) + } else { + console.info('删除动态失败: ', card.id, del_dynamic_resp) + } + }, + }) + Toast.success(`打开控制台查看删除动态结果`, '删除动态', 3000) + }, + }, + { + name: 'del-feeds', + displayName: '删除转发抽奖动态', + icon: 'mdi-delete', + action: async () => { + const { forEachFeedsCard } = await import('@/components/feeds/api') + Toast.info('如果想删除所有动态, 可以一直下拉页面到没有动态为止', '删除动态', 3000) + await forEachFeedsCard({ + added: async card => { + const { isRepostType } = await import('@/components/feeds/api') + + // 判断是否是转发动态 + if (isRepostType(card)) { + const uid = getUID() + + const dynamic_info = (await getJsonWithCredentials( + `https://api.vc.bilibili.com/lottery_svr/v2/lottery_svr/lottery_notice?dynamic_id=${card.repostId}`, + )) as { + code: number + data: { + lottery_time: number + lottery_result: { + first_prize_result: { + uid: number + }[] + } + } + } + + // 检查抽奖时间 + if ( + dynamic_info.data.lottery_time && + dynamic_info.data.lottery_time > Date.now() / 1000 + ) { + // 抽奖未开始, 不能删除 + return + } + + if (dynamic_info.code === 0) { + // 检查是否中奖 + for (let i = 0; i < dynamic_info.data.lottery_result.first_prize_result.length; i++) { + if (dynamic_info.data.lottery_result.first_prize_result[i].uid === Number(uid)) { + Toast.info(`居然中奖了, 不能接受😭😭😭, 动态ID: ${card.id}`, '删除动态', 10000) + return + } + } + + const post_del_dynamic_data = { + dyn_id_str: card.id, + dyn_type: card.type.id, + rid_str: card.id, + } + // 删除动态 + const del_dynamic_resp = JSON.parse( + await postJsonWithCredentials( + `https://api.bilibili.com/x/dynamic/feed/operate/remove?platform=web&csrf=${getCsrf()}`, + post_del_dynamic_data, + ), + ) as { + code: number + message: string + ttl: number + data: null + } + + if (del_dynamic_resp.code === 0) { + console.info('删除动态成功: ', card.id) + } else { + console.info('删除动态失败: ', card.id, del_dynamic_resp) + } + } + } + }, + }) + + Toast.success(`打开控制台查看删除动态结果`, '删除动态', 3000) + }, + }, +] + +export const [checkInItems] = registerAndGetData('checkInCenter.items', builtInItems) diff --git a/registry/lib/components/feeds/del-feeds/index.md b/registry/lib/components/feeds/del-feeds/index.md new file mode 100644 index 0000000000..59fb2a8534 --- /dev/null +++ b/registry/lib/components/feeds/del-feeds/index.md @@ -0,0 +1 @@ +删除动态, 可选转发抽奖, 和全部删除. diff --git a/registry/lib/components/feeds/del-feeds/index.ts b/registry/lib/components/feeds/del-feeds/index.ts new file mode 100644 index 0000000000..c271ccc814 --- /dev/null +++ b/registry/lib/components/feeds/del-feeds/index.ts @@ -0,0 +1,16 @@ +import { defineComponentMetadata } from '@/components/define' +import { getUID } from '@/core/utils' + +export const component = defineComponentMetadata({ + name: 'delFeeds', + displayName: '删除动态', + tags: [componentsTags.feeds], + description: { + 'zh-CN': `删除动态, 可选转发抽奖(不会删除自己中奖的动态), 和全部删除.`, + }, + entry: none, + urlInclude: [`https://space.bilibili.com/${getUID()}/dynamic`], + widget: { + component: () => import('./Widget.vue').then(m => m.default), + }, +}) 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..e7f0d5cb3b 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 + /** 被转发动态的卡片 */ + repostId: string type: RepostFeedsCardType } /** From 3519f5b4620fd7330b041bfa735a94df65f0ecb0 Mon Sep 17 00:00:00 2001 From: gouzi <530971494@qq.com> Date: Thu, 19 Sep 2024 22:43:44 +0800 Subject: [PATCH 07/49] rename --- registry/lib/components/feeds/del-feeds/index.ts | 2 +- src/components/feeds/api/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/lib/components/feeds/del-feeds/index.ts b/registry/lib/components/feeds/del-feeds/index.ts index c271ccc814..d9d9e9eb90 100644 --- a/registry/lib/components/feeds/del-feeds/index.ts +++ b/registry/lib/components/feeds/del-feeds/index.ts @@ -2,7 +2,7 @@ import { defineComponentMetadata } from '@/components/define' import { getUID } from '@/core/utils' export const component = defineComponentMetadata({ - name: 'delFeeds', + name: 'deleteFeeds', displayName: '删除动态', tags: [componentsTags.feeds], description: { diff --git a/src/components/feeds/api/types.ts b/src/components/feeds/api/types.ts index e7f0d5cb3b..0baa82e923 100644 --- a/src/components/feeds/api/types.ts +++ b/src/components/feeds/api/types.ts @@ -121,7 +121,7 @@ export interface RepostFeedsCard extends FeedsCard { repostUsername: string /** 被转发动态的内容 */ repostText: string - /** 被转发动态的卡片 */ + /** 被转发动态的 ID */ repostId: string type: RepostFeedsCardType } From 5626e3343dc94b9af3bfdf9e1852d10c67b954f8 Mon Sep 17 00:00:00 2001 From: WakelessSloth56 Date: Mon, 30 Sep 2024 11:43:51 +0800 Subject: [PATCH 08/49] feat: saveVideoMetadata - ffmetadata fields mode option --- .../lib/components/video/metadata/index.ts | 2 + .../lib/components/video/metadata/metadata.ts | 46 +++++++++++-------- .../lib/components/video/metadata/options.ts | 16 +++++++ 3 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 registry/lib/components/video/metadata/options.ts diff --git a/registry/lib/components/video/metadata/index.ts b/registry/lib/components/video/metadata/index.ts index bcdbdecea1..29aae4590e 100644 --- a/registry/lib/components/video/metadata/index.ts +++ b/registry/lib/components/video/metadata/index.ts @@ -5,6 +5,7 @@ import { Toast } from '@/core/toast' import { videoUrls } from '@/core/utils/urls' import { DownloadVideoAssets } from '../download/types' import { generateByType, MetadataType } from './metadata' +import { options } from './options' export const title = '保存视频元数据' export const name = 'saveVideoMetadata' @@ -28,6 +29,7 @@ export const component = defineComponentMetadata({ tags: [componentsTags.video], entry: none, urlInclude: videoUrls, + options, widget: { condition: hasVideo, component: () => import('./SaveMetadata.vue').then(m => m.default), diff --git a/registry/lib/components/video/metadata/metadata.ts b/registry/lib/components/video/metadata/metadata.ts index 6f30f52cab..a3c5ceca50 100644 --- a/registry/lib/components/video/metadata/metadata.ts +++ b/registry/lib/components/video/metadata/metadata.ts @@ -2,8 +2,10 @@ import { VideoInfo, VideoPageInfo } from '@/components/video/video-info' import { VideoQuality } from '@/components/video/video-quality' import { bilibiliApi, getJsonWithCredentials } from '@/core/ajax' import { meta } from '@/core/meta' +import { getComponentSettings } from '@/core/settings' import { Toast } from '@/core/toast' -import { title as pluginTitle } from '.' +import { name as componentName, title as pluginTitle } from '.' +import { FieldsMode, Options } from './options' export type MetadataType = 'ffmetadata' | 'ogm' @@ -62,6 +64,10 @@ async function generateFFMetadata(aid: string = unsafeWindow.aid, cid: string = const data = await fetchMetadata(aid, cid) const info = data.basic + const { + options: { fieldsMode }, + } = getComponentSettings(componentName) + const lines = [ ';FFMETADATA1', `;generated by Bilibili-Evolved v${meta.compilationInfo.version}`, @@ -70,25 +76,29 @@ async function generateFFMetadata(aid: string = unsafeWindow.aid, cid: string = ff('title', `${info.title} - ${data.page.title}`, false), ff('description', info.description, false), ff('artist', info.up.name, false), - // Custom fields - ff('title', info.title), - ff('description', info.description), - ff('publish_date', new Date(info.pubdate * 1000).toLocaleString()), - ff('aid', info.aid), - ff('bvid', info.bvid), - ff('cid', data.page.cid), - ff('category_id', info.tagId), - ff('category_name', info.tagName), - ff('page_title', data.page.title), - ff('page', data.page.pageNumber), - ff('pages', info.pages.length), - ff('up_name', info.up.name), - ff('up_uid', info.up.uid), ] - if (data.quality) { - lines.push(ff('quality', data.quality.value)) - lines.push(ff('quality_label', data.quality.name)) + if (fieldsMode === FieldsMode.ALL) { + // Custom fields + lines.push( + ff('title', info.title), + ff('description', info.description), + ff('publish_date', new Date(info.pubdate * 1000).toLocaleString()), + ff('aid', info.aid), + ff('bvid', info.bvid), + ff('cid', data.page.cid), + ff('category_id', info.tagId), + ff('category_name', info.tagName), + ff('page_title', data.page.title), + ff('page', data.page.pageNumber), + ff('pages', info.pages.length), + ff('up_name', info.up.name), + ff('up_uid', info.up.uid), + ) + if (data.quality) { + lines.push(ff('quality', data.quality.value)) + lines.push(ff('quality_label', data.quality.name)) + } } if (data.viewPoints.length > 0) { diff --git a/registry/lib/components/video/metadata/options.ts b/registry/lib/components/video/metadata/options.ts new file mode 100644 index 0000000000..f1e0971d40 --- /dev/null +++ b/registry/lib/components/video/metadata/options.ts @@ -0,0 +1,16 @@ +import { defineOptionsMetadata, OptionsOfMetadata } from '@/components/define' + +export enum FieldsMode { + ALL = '全部', + Standard = '仅标准字段', +} + +export const options = defineOptionsMetadata({ + fieldsMode: { + defaultValue: FieldsMode.ALL, + displayName: 'FFMETADATA 字段', + dropdownEnum: FieldsMode, + }, +}) + +export type Options = OptionsOfMetadata From b6457feabdd976b75a6ff7645ca624cedc7e96c3 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 13 Oct 2024 19:28:38 +0800 Subject: [PATCH 09/49] Add hideUserPendent --- .../style/hide/user-pendent/index.md | 1 + .../style/hide/user-pendent/index.ts | 19 ++++++++++++++ .../user-pendent/user-pendent-shadow.scss | 13 ++++++++++ .../style/hide/user-pendent/user-pendent.scss | 26 +++++++++++++++++++ .../style/simplify/comments/comments-v2.scss | 6 ----- .../style/simplify/comments/index.ts | 4 --- 6 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 registry/lib/components/style/hide/user-pendent/index.md create mode 100644 registry/lib/components/style/hide/user-pendent/index.ts create mode 100644 registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss create mode 100644 registry/lib/components/style/hide/user-pendent/user-pendent.scss diff --git a/registry/lib/components/style/hide/user-pendent/index.md b/registry/lib/components/style/hide/user-pendent/index.md new file mode 100644 index 0000000000..6d6069bab6 --- /dev/null +++ b/registry/lib/components/style/hide/user-pendent/index.md @@ -0,0 +1 @@ +隐藏页面中用户的头像框 (包括角标), 目前支持动态和视频页面. diff --git a/registry/lib/components/style/hide/user-pendent/index.ts b/registry/lib/components/style/hide/user-pendent/index.ts new file mode 100644 index 0000000000..5d2b950136 --- /dev/null +++ b/registry/lib/components/style/hide/user-pendent/index.ts @@ -0,0 +1,19 @@ +import { defineComponentMetadata } from '@/components/define' + +export const component = defineComponentMetadata({ + name: 'hideUserPendent', + displayName: '隐藏头像框', + entry: none, + tags: [componentsTags.style], + instantStyles: [ + { + name: 'hide-user-pendent', + style: () => import('./user-pendent.scss'), + }, + { + name: 'hide-user-pendent', + style: () => import('./user-pendent-shadow.scss'), + shadowDom: true, + }, + ], +}) diff --git a/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss b/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss new file mode 100644 index 0000000000..88e0cef20c --- /dev/null +++ b/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss @@ -0,0 +1,13 @@ +:host(bili-avatar) { + container-type: size; + container-name: avatar; + // .layers:first-child, + .layer:not(:first-child), + .layer:not(:has(picture)) { + display: none; + } + .layer:has(picture) { + min-width: 100cqw; + min-height: 100cqh; + } +} diff --git a/registry/lib/components/style/hide/user-pendent/user-pendent.scss b/registry/lib/components/style/hide/user-pendent/user-pendent.scss new file mode 100644 index 0000000000..8c4ac5f199 --- /dev/null +++ b/registry/lib/components/style/hide/user-pendent/user-pendent.scss @@ -0,0 +1,26 @@ + +.b-avatar { + container-type: size; + container-name: avatar; + &__layers:not(:first-child), + &__layer:not(:first-child) { + display: none !important; + } + &__layer:first-child { + min-width: 100cqw; + min-height: 100cqh; + } +} +.up-avatar { + container-type: size; + container-name: avatar; +} +.bili-avatar { + min-width: 100cqw; + min-height: 100cqh; + transform: none !important; + &-icon, + &-pendent-dom { + display: none !important; + } +} diff --git a/registry/lib/components/style/simplify/comments/comments-v2.scss b/registry/lib/components/style/simplify/comments/comments-v2.scss index b141557995..bc52ce7ee3 100644 --- a/registry/lib/components/style/simplify/comments/comments-v2.scss +++ b/registry/lib/components/style/simplify/comments/comments-v2.scss @@ -37,12 +37,6 @@ $prefix: 'simplifyComments-switch'; } } - body.#{$prefix}-userPendent & { - .bili-avatar-pendent-dom { - display: none !important; - } - } - body.#{$prefix}-decorateAndTime & { .reply-decorate { display: none !important; diff --git a/registry/lib/components/style/simplify/comments/index.ts b/registry/lib/components/style/simplify/comments/index.ts index 4a395d89e8..69800976da 100644 --- a/registry/lib/components/style/simplify/comments/index.ts +++ b/registry/lib/components/style/simplify/comments/index.ts @@ -17,10 +17,6 @@ export const component = wrapSwitchOptions({ defaultValue: true, displayName: '装扮 & 时间', }, - userPendent: { - defaultValue: false, - displayName: '头像框', - }, subReplyNewLine: { defaultValue: true, displayName: '回复换行', From c6fe03cb714e9c86819d8aca5c6e260b24bc9874 Mon Sep 17 00:00:00 2001 From: the1812 Date: Mon, 14 Oct 2024 22:33:16 +0800 Subject: [PATCH 10/49] Add hideLivePlayerBlur (#4634) --- .../live/hide-player-blur/hide-player-blur.scss | 3 +++ .../lib/components/live/hide-player-blur/index.md | 1 + .../lib/components/live/hide-player-blur/index.ts | 11 +++++++++++ 3 files changed, 15 insertions(+) create mode 100644 registry/lib/components/live/hide-player-blur/hide-player-blur.scss create mode 100644 registry/lib/components/live/hide-player-blur/index.md create mode 100644 registry/lib/components/live/hide-player-blur/index.ts diff --git a/registry/lib/components/live/hide-player-blur/hide-player-blur.scss b/registry/lib/components/live/hide-player-blur/hide-player-blur.scss new file mode 100644 index 0000000000..86abd60eb4 --- /dev/null +++ b/registry/lib/components/live/hide-player-blur/hide-player-blur.scss @@ -0,0 +1,3 @@ +#web-player-module-area-mask-panel { + z-index: -100 !important; +} diff --git a/registry/lib/components/live/hide-player-blur/index.md b/registry/lib/components/live/hide-player-blur/index.md new file mode 100644 index 0000000000..78df557fc3 --- /dev/null +++ b/registry/lib/components/live/hide-player-blur/index.md @@ -0,0 +1 @@ +移除直播画面中的马赛克区域. diff --git a/registry/lib/components/live/hide-player-blur/index.ts b/registry/lib/components/live/hide-player-blur/index.ts new file mode 100644 index 0000000000..b5cfe94b1d --- /dev/null +++ b/registry/lib/components/live/hide-player-blur/index.ts @@ -0,0 +1,11 @@ +import { defineComponentMetadata } from '@/components/define' +import { liveUrls } from '@/core/utils/urls' + +export const component = defineComponentMetadata({ + name: 'hideLivePlayerBlur', + displayName: '隐藏直播马赛克', + entry: none, + tags: [componentsTags.live, componentsTags.style], + urlInclude: [...liveUrls], + instantStyles: [{ name: 'hideLivePlayerBlur', style: () => import('./hide-player-blur.scss') }], +}) From f3f61287d8108f5a180f7b6b4fdbe911b3bfe7eb Mon Sep 17 00:00:00 2001 From: the1812 Date: Mon, 14 Oct 2024 23:04:02 +0800 Subject: [PATCH 11/49] Improve search accuracy (fix #4833) --- src/components/launch-bar/LaunchBar.vue | 4 +++- src/components/launch-bar/search-provider.ts | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/launch-bar/LaunchBar.vue b/src/components/launch-bar/LaunchBar.vue index b1a9050df7..765576c34f 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() { 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({ From 866ef53da008098dd713b465fa54d2096f030150 Mon Sep 17 00:00:00 2001 From: the1812 Date: Mon, 14 Oct 2024 23:10:47 +0800 Subject: [PATCH 12/49] Fix danmaku settings not read (#4824) --- .../components/video/danmaku/download/utils.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/registry/lib/components/video/danmaku/download/utils.ts b/registry/lib/components/video/danmaku/download/utils.ts index b480fc5650..18d2fd1ffe 100644 --- a/registry/lib/components/video/danmaku/download/utils.ts +++ b/registry/lib/components/video/danmaku/download/utils.ts @@ -117,17 +117,17 @@ export const getUserDanmakuConfig = async () => { })() // 加粗 - config.bold = playerAgent.getPlayerConfig('bold', false) + config.bold = playerAgent.getPlayerConfig('dmSetting.bold', false) // 透明度 config.alpha = lodash.clamp( - 1 - parseFloat(playerAgent.getPlayerConfig('opacity', '0.4')), + 1 - parseFloat(playerAgent.getPlayerConfig('dmSetting.opacity', '0.4')), 0, 1, ) // 分辨率 - const resolutionFactor = 1.4 - 0.4 * playerAgent.getPlayerConfig('fontsize', 1) + const resolutionFactor = 1.4 - 0.4 * playerAgent.getPlayerConfig('dmSetting.fontsize', 1) config.resolution = { x: Math.round(1920 * resolutionFactor), y: Math.round(1080 * resolutionFactor), @@ -135,7 +135,7 @@ export const getUserDanmakuConfig = async () => { // 弹幕持续时长 config.duration = (() => { - const scrollDuration = 18 - 3 * playerAgent.getPlayerConfig('speedplus', 0) + const scrollDuration = 18 - 3 * playerAgent.getPlayerConfig('dmSetting.speedplus', 0) return (danmaku: { type: number }) => { switch (danmaku.type) { case 4: @@ -148,10 +148,13 @@ export const getUserDanmakuConfig = async () => { })() // 底部间距 - const bottomMargin = playerAgent.getPlayerConfig('danmakuArea', 0) + const bottomMargin = playerAgent.getPlayerConfig('dmSetting.danmakuArea', 0) config.bottomMarginPercent = bottomMargin >= 100 ? 0 : bottomMargin / 100 // 无显示区域限制时要检查是否开启防挡字幕 - if (config.bottomMarginPercent === 0 && playerAgent.getPlayerConfig('preventshade', false)) { + if ( + config.bottomMarginPercent === 0 && + playerAgent.getPlayerConfig('dmSetting.preventshade', false) + ) { config.bottomMarginPercent = 0.15 } From f0d5f17aca88eabf9b8bdd33f07617cadeef473d Mon Sep 17 00:00:00 2001 From: the1812 Date: Mon, 14 Oct 2024 23:14:22 +0800 Subject: [PATCH 13/49] Add downloadDanmaku speed option (fix #4824) --- .../lib/components/video/danmaku/download/index.ts | 2 ++ .../lib/components/video/danmaku/download/options.ts | 10 ++++++++++ .../lib/components/video/danmaku/download/utils.ts | 10 +++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 registry/lib/components/video/danmaku/download/options.ts diff --git a/registry/lib/components/video/danmaku/download/index.ts b/registry/lib/components/video/danmaku/download/index.ts index a7c622f60d..380525bd80 100644 --- a/registry/lib/components/video/danmaku/download/index.ts +++ b/registry/lib/components/video/danmaku/download/index.ts @@ -5,6 +5,7 @@ import { Toast } from '@/core/toast' import { videoAndBangumiUrls } from '@/core/utils/urls' import { DownloadVideoAssets } from '../../download/types' import { DanmakuDownloadType } from './utils' +import { downloadDanmakuOptions } from './options' export const component = defineComponentMetadata({ name: 'downloadDanmaku', @@ -17,6 +18,7 @@ export const component = defineComponentMetadata({ entry: none, reload: none, unload: none, + options: downloadDanmakuOptions, plugin: { displayName: '下载视频 - 下载弹幕支持', setup: ({ addData }) => { diff --git a/registry/lib/components/video/danmaku/download/options.ts b/registry/lib/components/video/danmaku/download/options.ts new file mode 100644 index 0000000000..81f21a4350 --- /dev/null +++ b/registry/lib/components/video/danmaku/download/options.ts @@ -0,0 +1,10 @@ +import { defineOptionsMetadata, OptionsOfMetadata } from '@/components/define' + +export const downloadDanmakuOptions = defineOptionsMetadata({ + speed: { + defaultValue: 'auto' as 'auto' | number, + hidden: true, + }, +}) + +export type DownloadDanmakuOptions = OptionsOfMetadata diff --git a/registry/lib/components/video/danmaku/download/utils.ts b/registry/lib/components/video/danmaku/download/utils.ts index 18d2fd1ffe..e82b452069 100644 --- a/registry/lib/components/video/danmaku/download/utils.ts +++ b/registry/lib/components/video/danmaku/download/utils.ts @@ -6,6 +6,8 @@ import { DanmakuConverterConfig, DanmakuConverter } from '../converter/danmaku-c import { DanmakuType } from '../converter/danmaku-type' import { XmlDanmaku } from '../converter/xml-danmaku' import { playerAgent } from '@/components/video/player-agent' +import { getComponentSettings } from '@/core/settings' +import { DownloadDanmakuOptions } from './options' export class JsonDanmaku { // static SegmentSize = 6 * 60 @@ -71,6 +73,8 @@ export class JsonDanmaku { } export type DanmakuDownloadType = 'json' | 'xml' | 'ass' export const getUserDanmakuConfig = async () => { + const downloadDanmakuOptions = + getComponentSettings('downloadDanmaku').options const title = getFriendlyTitle() const defaultConfig: Omit = { font: '微软雅黑', @@ -135,7 +139,11 @@ export const getUserDanmakuConfig = async () => { // 弹幕持续时长 config.duration = (() => { - const scrollDuration = 18 - 3 * playerAgent.getPlayerConfig('dmSetting.speedplus', 0) + const speed = + downloadDanmakuOptions.speed === 'auto' + ? playerAgent.getPlayerConfig('dmSetting.speedplus', 0) + : downloadDanmakuOptions.speed + const scrollDuration = 18 - 3 * speed return (danmaku: { type: number }) => { switch (danmaku.type) { case 4: From 4f7b760c1ddd273202dc3b598bc9255290d9c0f3 Mon Sep 17 00:00:00 2001 From: the1812 Date: Tue, 15 Oct 2024 22:35:33 +0800 Subject: [PATCH 14/49] Fix avatar size --- .../components/style/hide/user-pendent/user-pendent.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/registry/lib/components/style/hide/user-pendent/user-pendent.scss b/registry/lib/components/style/hide/user-pendent/user-pendent.scss index 8c4ac5f199..925acfad62 100644 --- a/registry/lib/components/style/hide/user-pendent/user-pendent.scss +++ b/registry/lib/components/style/hide/user-pendent/user-pendent.scss @@ -1,4 +1,3 @@ - .b-avatar { container-type: size; container-name: avatar; @@ -14,11 +13,13 @@ .up-avatar { container-type: size; container-name: avatar; + .bili-avatar { + min-width: 100cqw; + min-height: 100cqh; + transform: none !important; + } } .bili-avatar { - min-width: 100cqw; - min-height: 100cqh; - transform: none !important; &-icon, &-pendent-dom { display: none !important; From a858410a153d14b85ada3946c909e980da5732fd Mon Sep 17 00:00:00 2001 From: the1812 Date: Sat, 19 Oct 2024 20:10:43 +0800 Subject: [PATCH 15/49] Add commentContentReplace (#4072) --- .../content-replace/handlers/index.ts | 5 + .../content-replace/handlers/text-replace.ts | 122 ++++++++++++++++++ .../content-replace/handlers/types.ts | 1 + .../utils/comments/content-replace/index.md | 3 + .../utils/comments/content-replace/index.ts | 36 ++++++ .../utils/comments/content-replace/options.ts | 10 ++ .../settings/ContentReplaceRow.vue | 60 +++++++++ .../content-replace/settings/ExtraOptions.vue | 90 +++++++++++++ .../comments/content-replace/settings/row.ts | 6 + .../utils/comments/content-replace/utils.ts | 9 ++ 10 files changed, 342 insertions(+) create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/index.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/types.ts create mode 100644 registry/lib/components/utils/comments/content-replace/index.md create mode 100644 registry/lib/components/utils/comments/content-replace/index.ts create mode 100644 registry/lib/components/utils/comments/content-replace/options.ts create mode 100644 registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue create mode 100644 registry/lib/components/utils/comments/content-replace/settings/ExtraOptions.vue create mode 100644 registry/lib/components/utils/comments/content-replace/settings/row.ts create mode 100644 registry/lib/components/utils/comments/content-replace/utils.ts diff --git a/registry/lib/components/utils/comments/content-replace/handlers/index.ts b/registry/lib/components/utils/comments/content-replace/handlers/index.ts new file mode 100644 index 0000000000..6c39a8e19f --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/index.ts @@ -0,0 +1,5 @@ +import { registerData } from '@/plugins/data' +import { textReplace } from './text-replace' + +export const CommentContentReplaceHandlers = 'commentContentReplace.handlers' +export const handlers = registerData(CommentContentReplaceHandlers, [textReplace]) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts b/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts new file mode 100644 index 0000000000..bd0246275b --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts @@ -0,0 +1,122 @@ +import { getComponentSettings } from '@/core/settings' +import { createEmotionImage } from '../utils' +import { CommentContentReplaceHandler } from './types' +import { CommentContentReplaceOptions } from '../options' + +const { options } = getComponentSettings('commentContentReplace') + +const isUrl = (text: string) => { + try { + const url = new URL(text) + return Boolean(url) + } catch (error) { + return false + } +} + +abstract class NodeContentReplacer { + abstract isKeywordMatch(node: Node, keyword: string, target: string): boolean + abstract replaceContent(node: Node, keyword: string, target: string): Node[] +} + +class TextToTextReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string) { + if (node instanceof Text) { + return node.textContent.includes(keyword) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + const index = node.textContent.indexOf(keyword) + if (index === -1) { + return [] + } + const leftPart = node.textContent.substring(0, index) + const rightPart = node.textContent.substring(index + keyword.length) + node.textContent = `${leftPart}${target}${rightPart}` + return [] + } +} + +class TextToEmotionReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string, target: string) { + if (node instanceof Text) { + return node.textContent.includes(keyword) && isUrl(target) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + const index = node.textContent.indexOf(keyword) + if (index === -1 || !(node instanceof Text)) { + return [] + } + const leftPart = new Text(node.textContent.substring(0, index)) + const imageElement = createEmotionImage(target, keyword) + const rightPart = new Text(node.textContent.substring(index + keyword.length)) + node.replaceWith(leftPart, imageElement, rightPart) + return [rightPart] + } +} + +class EmotionToTextReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string) { + if (node instanceof HTMLImageElement) { + return node.alt === keyword + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + if (!(node instanceof HTMLImageElement)) { + return [] + } + node.replaceWith(new Text(target)) + return [] + } +} + +class EmotionToEmotionReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string, target: string) { + if (node instanceof HTMLImageElement) { + return node.alt === keyword && isUrl(target) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + if (!(node instanceof HTMLImageElement)) { + return [] + } + node.src = target + return [] + } +} + +class RecursiveReplacer extends NodeContentReplacer { + isKeywordMatch() { + return true + } + replaceContent(node: Node): Node[] { + return Array.from(node.childNodes) + } +} + +const contentReplacers: NodeContentReplacer[] = [ + new TextToEmotionReplacer(), + new EmotionToEmotionReplacer(), + new TextToTextReplacer(), + new EmotionToTextReplacer(), + new RecursiveReplacer(), +] + +export const textReplace: CommentContentReplaceHandler = content => { + const { replaceMap } = options + content.forEach(node => { + Object.entries(replaceMap).forEach(([from, to]) => { + const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) + if (!replacer) { + return + } + const restParts = replacer.replaceContent(node, from, to) + textReplace(restParts) + }) + }) +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/types.ts b/registry/lib/components/utils/comments/content-replace/handlers/types.ts new file mode 100644 index 0000000000..a62cdbd24f --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/types.ts @@ -0,0 +1 @@ +export type CommentContentReplaceHandler = (content: Node[]) => void | Promise diff --git a/registry/lib/components/utils/comments/content-replace/index.md b/registry/lib/components/utils/comments/content-replace/index.md new file mode 100644 index 0000000000..8162bc810c --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/index.md @@ -0,0 +1,3 @@ +替换评论中的内容. + +可以添加多个替换配置, 每项配置可将一个关键词替换为其他文本. 若替换的目标是一个链接, 则视作替换为表情. diff --git a/registry/lib/components/utils/comments/content-replace/index.ts b/registry/lib/components/utils/comments/content-replace/index.ts new file mode 100644 index 0000000000..b1a7cc798d --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/index.ts @@ -0,0 +1,36 @@ +import { defineComponentMetadata } from '@/components/define' +import { forEachCommentItem } from '@/components/utils/comment-apis' +import { select } from '@/core/spin-query' +import { getData } from '@/plugins/data' +import type { CommentContentReplaceHandler } from './handlers/types' +import { commentContentReplaceOptions } from './options' + +export const component = defineComponentMetadata({ + name: 'commentContentReplace', + displayName: '评论内容替换', + tags: [componentsTags.utils], + options: commentContentReplaceOptions, + entry: async () => { + forEachCommentItem({ + added: async commentItem => { + const { CommentContentReplaceHandlers } = await import('./handlers') + if (commentItem.shadowDomEntry === undefined) { + return + } + const content = await select( + () => commentItem.shadowDomEntry.querySelector(':host(bili-rich-text) #contents'), + { queryInterval: 200 }, + ) + if (content === null) { + return + } + content.childNodes + const [handlers] = getData(CommentContentReplaceHandlers) as [ + CommentContentReplaceHandler[], + ] + handlers.forEach(h => h(Array.from(content.childNodes))) + }, + }) + }, + extraOptions: () => import('./settings/ExtraOptions.vue'), +}) diff --git a/registry/lib/components/utils/comments/content-replace/options.ts b/registry/lib/components/utils/comments/content-replace/options.ts new file mode 100644 index 0000000000..dbc206fddc --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/options.ts @@ -0,0 +1,10 @@ +import { defineOptionsMetadata, OptionsOfMetadata } from '@/components/define' + +export const commentContentReplaceOptions = defineOptionsMetadata({ + replaceMap: { + defaultValue: {} as Record, + hidden: true, + }, +}) + +export type CommentContentReplaceOptions = OptionsOfMetadata diff --git a/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue b/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue new file mode 100644 index 0000000000..f0f69fb2b1 --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue @@ -0,0 +1,60 @@ + + + diff --git a/registry/lib/components/utils/comments/content-replace/settings/ExtraOptions.vue b/registry/lib/components/utils/comments/content-replace/settings/ExtraOptions.vue new file mode 100644 index 0000000000..ca4f483d7e --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/settings/ExtraOptions.vue @@ -0,0 +1,90 @@ + + + diff --git a/registry/lib/components/utils/comments/content-replace/settings/row.ts b/registry/lib/components/utils/comments/content-replace/settings/row.ts new file mode 100644 index 0000000000..40dd2057b5 --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/settings/row.ts @@ -0,0 +1,6 @@ +import { getRandomId } from '@/core/utils' + +export class CommentContentReplaceRow { + key = getRandomId() + constructor(public from = '', public to = '') {} +} diff --git a/registry/lib/components/utils/comments/content-replace/utils.ts b/registry/lib/components/utils/comments/content-replace/utils.ts new file mode 100644 index 0000000000..b914c4250a --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/utils.ts @@ -0,0 +1,9 @@ +export const createEmotionImage = (src: string, alt: string) => { + const element = document.createElement('img') + element.src = src + element.alt = alt + element.loading = 'lazy' + element.style.width = '50px' + element.style.height = '50px' + return element +} From ecb93e149e81fe195523265a0193d4912c81d10a Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 10:45:21 +0800 Subject: [PATCH 16/49] Refactor files --- .../handlers/emoticon-to-emoticon.ts | 18 +++ .../handlers/emoticon-to-text.ts | 17 +++ .../content-replace/handlers/index.ts | 36 +++++- .../handlers/node-content-replacer.ts | 4 + .../content-replace/handlers/recursive.ts | 10 ++ .../content-replace/handlers/text-replace.ts | 122 ------------------ .../handlers/text-to-emoticon.ts | 22 ++++ .../content-replace/handlers/text-to-text.ts | 20 +++ .../utils/comments/content-replace/utils.ts | 9 ++ 9 files changed, 134 insertions(+), 124 deletions(-) create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-emoticon.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/node-content-replacer.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/recursive.ts delete mode 100644 registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts create mode 100644 registry/lib/components/utils/comments/content-replace/handlers/text-to-text.ts diff --git a/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-emoticon.ts b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-emoticon.ts new file mode 100644 index 0000000000..bc2586407b --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-emoticon.ts @@ -0,0 +1,18 @@ +import { isUrl } from '../utils' +import { NodeContentReplacer } from './node-content-replacer' + +export class EmoticonToEmoticonReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string, target: string) { + if (node instanceof HTMLImageElement) { + return node.alt === keyword && isUrl(target) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + if (!(node instanceof HTMLImageElement)) { + return [] + } + node.src = target + return [] + } +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts new file mode 100644 index 0000000000..4c6b1a4dc6 --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts @@ -0,0 +1,17 @@ +import { NodeContentReplacer } from './node-content-replacer' + +export class EmoticonToTextReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string) { + if (node instanceof HTMLImageElement) { + return node.alt === keyword + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + if (!(node instanceof HTMLImageElement)) { + return [] + } + node.replaceWith(new Text(target)) + return [] + } +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/index.ts b/registry/lib/components/utils/comments/content-replace/handlers/index.ts index 6c39a8e19f..685b47214d 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/index.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/index.ts @@ -1,5 +1,37 @@ import { registerData } from '@/plugins/data' -import { textReplace } from './text-replace' +import { getComponentSettings } from '@/core/settings' +import { CommentContentReplaceHandler } from './types' +import { CommentContentReplaceOptions } from '../options' +import { NodeContentReplacer } from './node-content-replacer' +import { EmoticonToEmoticonReplacer } from './emoticon-to-emoticon' +import { EmoticonToTextReplacer } from './emoticon-to-text' +import { RecursiveReplacer } from './recursive' +import { TextToEmoticonReplacer } from './text-to-emoticon' +import { TextToTextReplacer } from './text-to-text' + +const { options } = getComponentSettings('commentContentReplace') + +const contentReplacers: NodeContentReplacer[] = [ + new TextToEmoticonReplacer(), + new EmoticonToEmoticonReplacer(), + new TextToTextReplacer(), + new EmoticonToTextReplacer(), + new RecursiveReplacer(), +] + +export const defaultHandler: CommentContentReplaceHandler = content => { + const { replaceMap } = options + content.forEach(node => { + Object.entries(replaceMap).forEach(([from, to]) => { + const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) + if (!replacer) { + return + } + const restParts = replacer.replaceContent(node, from, to) + defaultHandler(restParts) + }) + }) +} export const CommentContentReplaceHandlers = 'commentContentReplace.handlers' -export const handlers = registerData(CommentContentReplaceHandlers, [textReplace]) +export const handlers = registerData(CommentContentReplaceHandlers, [defaultHandler]) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/node-content-replacer.ts b/registry/lib/components/utils/comments/content-replace/handlers/node-content-replacer.ts new file mode 100644 index 0000000000..819f1ced7c --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/node-content-replacer.ts @@ -0,0 +1,4 @@ +export abstract class NodeContentReplacer { + abstract isKeywordMatch(node: Node, keyword: string, target: string): boolean + abstract replaceContent(node: Node, keyword: string, target: string): Node[] +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/recursive.ts b/registry/lib/components/utils/comments/content-replace/handlers/recursive.ts new file mode 100644 index 0000000000..fa0a17e0e2 --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/recursive.ts @@ -0,0 +1,10 @@ +import { NodeContentReplacer } from './node-content-replacer' + +export class RecursiveReplacer extends NodeContentReplacer { + isKeywordMatch() { + return true + } + replaceContent(node: Node): Node[] { + return Array.from(node.childNodes) + } +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts b/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts deleted file mode 100644 index bd0246275b..0000000000 --- a/registry/lib/components/utils/comments/content-replace/handlers/text-replace.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { getComponentSettings } from '@/core/settings' -import { createEmotionImage } from '../utils' -import { CommentContentReplaceHandler } from './types' -import { CommentContentReplaceOptions } from '../options' - -const { options } = getComponentSettings('commentContentReplace') - -const isUrl = (text: string) => { - try { - const url = new URL(text) - return Boolean(url) - } catch (error) { - return false - } -} - -abstract class NodeContentReplacer { - abstract isKeywordMatch(node: Node, keyword: string, target: string): boolean - abstract replaceContent(node: Node, keyword: string, target: string): Node[] -} - -class TextToTextReplacer extends NodeContentReplacer { - isKeywordMatch(node: Node, keyword: string) { - if (node instanceof Text) { - return node.textContent.includes(keyword) - } - return false - } - replaceContent(node: Node, keyword: string, target: string): Node[] { - const index = node.textContent.indexOf(keyword) - if (index === -1) { - return [] - } - const leftPart = node.textContent.substring(0, index) - const rightPart = node.textContent.substring(index + keyword.length) - node.textContent = `${leftPart}${target}${rightPart}` - return [] - } -} - -class TextToEmotionReplacer extends NodeContentReplacer { - isKeywordMatch(node: Node, keyword: string, target: string) { - if (node instanceof Text) { - return node.textContent.includes(keyword) && isUrl(target) - } - return false - } - replaceContent(node: Node, keyword: string, target: string): Node[] { - const index = node.textContent.indexOf(keyword) - if (index === -1 || !(node instanceof Text)) { - return [] - } - const leftPart = new Text(node.textContent.substring(0, index)) - const imageElement = createEmotionImage(target, keyword) - const rightPart = new Text(node.textContent.substring(index + keyword.length)) - node.replaceWith(leftPart, imageElement, rightPart) - return [rightPart] - } -} - -class EmotionToTextReplacer extends NodeContentReplacer { - isKeywordMatch(node: Node, keyword: string) { - if (node instanceof HTMLImageElement) { - return node.alt === keyword - } - return false - } - replaceContent(node: Node, keyword: string, target: string): Node[] { - if (!(node instanceof HTMLImageElement)) { - return [] - } - node.replaceWith(new Text(target)) - return [] - } -} - -class EmotionToEmotionReplacer extends NodeContentReplacer { - isKeywordMatch(node: Node, keyword: string, target: string) { - if (node instanceof HTMLImageElement) { - return node.alt === keyword && isUrl(target) - } - return false - } - replaceContent(node: Node, keyword: string, target: string): Node[] { - if (!(node instanceof HTMLImageElement)) { - return [] - } - node.src = target - return [] - } -} - -class RecursiveReplacer extends NodeContentReplacer { - isKeywordMatch() { - return true - } - replaceContent(node: Node): Node[] { - return Array.from(node.childNodes) - } -} - -const contentReplacers: NodeContentReplacer[] = [ - new TextToEmotionReplacer(), - new EmotionToEmotionReplacer(), - new TextToTextReplacer(), - new EmotionToTextReplacer(), - new RecursiveReplacer(), -] - -export const textReplace: CommentContentReplaceHandler = content => { - const { replaceMap } = options - content.forEach(node => { - Object.entries(replaceMap).forEach(([from, to]) => { - const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) - if (!replacer) { - return - } - const restParts = replacer.replaceContent(node, from, to) - textReplace(restParts) - }) - }) -} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts b/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts new file mode 100644 index 0000000000..2d06f9ffba --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts @@ -0,0 +1,22 @@ +import { createEmotionImage, isUrl } from '../utils' +import { NodeContentReplacer } from './node-content-replacer' + +export class TextToEmoticonReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string, target: string) { + if (node instanceof Text) { + return node.textContent.includes(keyword) && isUrl(target) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + const index = node.textContent.indexOf(keyword) + if (index === -1 || !(node instanceof Text)) { + return [] + } + const leftPart = new Text(node.textContent.substring(0, index)) + const imageElement = createEmotionImage(target, keyword) + const rightPart = new Text(node.textContent.substring(index + keyword.length)) + node.replaceWith(leftPart, imageElement, rightPart) + return [rightPart] + } +} diff --git a/registry/lib/components/utils/comments/content-replace/handlers/text-to-text.ts b/registry/lib/components/utils/comments/content-replace/handlers/text-to-text.ts new file mode 100644 index 0000000000..0f8a634ee1 --- /dev/null +++ b/registry/lib/components/utils/comments/content-replace/handlers/text-to-text.ts @@ -0,0 +1,20 @@ +import { NodeContentReplacer } from './node-content-replacer' + +export class TextToTextReplacer extends NodeContentReplacer { + isKeywordMatch(node: Node, keyword: string) { + if (node instanceof Text) { + return node.textContent.includes(keyword) + } + return false + } + replaceContent(node: Node, keyword: string, target: string): Node[] { + const index = node.textContent.indexOf(keyword) + if (index === -1) { + return [] + } + const leftPart = node.textContent.substring(0, index) + const rightPart = node.textContent.substring(index + keyword.length) + node.textContent = `${leftPart}${target}${rightPart}` + return [] + } +} diff --git a/registry/lib/components/utils/comments/content-replace/utils.ts b/registry/lib/components/utils/comments/content-replace/utils.ts index b914c4250a..8c096334eb 100644 --- a/registry/lib/components/utils/comments/content-replace/utils.ts +++ b/registry/lib/components/utils/comments/content-replace/utils.ts @@ -7,3 +7,12 @@ export const createEmotionImage = (src: string, alt: string) => { element.style.height = '50px' return element } + +export const isUrl = (text: string) => { + try { + const url = new URL(text) + return Boolean(url) + } catch (error) { + return false + } +} From 710e6bec56cf21e4f0bfc6298f9412bb60c98b56 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 10:45:29 +0800 Subject: [PATCH 17/49] Fix Vue typing --- src/global.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/global.d.ts b/src/global.d.ts index fc8a7e90c6..4c903adc40 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -8,6 +8,7 @@ declare global { const lodash: LoDashStatic const Vue: typeof import('vue/types/umd') + type Vue = import('vue/types/umd') type EnumEventTarget = EventTarget & { addEventListener( From 83f70e949b808691f297f9ef4d1c9bd74314cadd Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 11:15:14 +0800 Subject: [PATCH 18/49] Allow remove emoticon --- .../comments/content-replace/handlers/emoticon-to-text.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts index 4c6b1a4dc6..7c70bf8748 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/emoticon-to-text.ts @@ -11,7 +11,11 @@ export class EmoticonToTextReplacer extends NodeContentReplacer { if (!(node instanceof HTMLImageElement)) { return [] } - node.replaceWith(new Text(target)) + if (target === '') { + node.remove() + } else { + node.replaceWith(new Text(target)) + } return [] } } From 437b7ad2a28ef12be562b23d9e3bdbf8028b77d0 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 11:15:31 +0800 Subject: [PATCH 19/49] Add CommentContentReplaceMap plugin data --- .../utils/comments/content-replace/handlers/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/index.ts b/registry/lib/components/utils/comments/content-replace/handlers/index.ts index 685b47214d..cd67b0a0e3 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/index.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/index.ts @@ -1,4 +1,4 @@ -import { registerData } from '@/plugins/data' +import { registerAndGetData } from '@/plugins/data' import { getComponentSettings } from '@/core/settings' import { CommentContentReplaceHandler } from './types' import { CommentContentReplaceOptions } from '../options' @@ -19,10 +19,12 @@ const contentReplacers: NodeContentReplacer[] = [ new RecursiveReplacer(), ] -export const defaultHandler: CommentContentReplaceHandler = content => { +export const CommentContentReplaceMap = 'commentContentReplace.map' +const defaultHandler: CommentContentReplaceHandler = content => { const { replaceMap } = options + const [finalReplaceMap] = registerAndGetData(CommentContentReplaceMap, replaceMap) content.forEach(node => { - Object.entries(replaceMap).forEach(([from, to]) => { + Object.entries(finalReplaceMap).forEach(([from, to]) => { const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) if (!replacer) { return @@ -34,4 +36,4 @@ export const defaultHandler: CommentContentReplaceHandler = content => { } export const CommentContentReplaceHandlers = 'commentContentReplace.handlers' -export const handlers = registerData(CommentContentReplaceHandlers, [defaultHandler]) +export const handlers = registerAndGetData(CommentContentReplaceHandlers, [defaultHandler]) From bb4914d1d1326402e88a8f2621c3f30daf7d1ffa Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 11:17:45 +0800 Subject: [PATCH 20/49] Update descriptions --- .../utils/comments/content-replace/index.md | 2 +- .../content-replace/settings/ContentReplaceRow.vue | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/registry/lib/components/utils/comments/content-replace/index.md b/registry/lib/components/utils/comments/content-replace/index.md index 8162bc810c..6d980e9c7e 100644 --- a/registry/lib/components/utils/comments/content-replace/index.md +++ b/registry/lib/components/utils/comments/content-replace/index.md @@ -1,3 +1,3 @@ 替换评论中的内容. -可以添加多个替换配置, 每项配置可将一个关键词替换为其他文本. 若替换的目标是一个链接, 则视作替换为表情. +可以添加多个替换配置, 每项配置可将一个关键词替换为其他文本. 若替换的目标是一个链接, 则视作替换为表情, 留空则会移除命中的关键词. diff --git a/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue b/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue index f0f69fb2b1..6b89b14465 100644 --- a/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue +++ b/registry/lib/components/utils/comments/content-replace/settings/ContentReplaceRow.vue @@ -1,7 +1,12 @@ From 13d8e870855932dad26ee2369ba51c2658d7fbd9 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 11:20:57 +0800 Subject: [PATCH 21/49] Ignore invalid keyword --- .../utils/comments/content-replace/handlers/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/index.ts b/registry/lib/components/utils/comments/content-replace/handlers/index.ts index cd67b0a0e3..3062a90aa9 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/index.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/index.ts @@ -25,6 +25,9 @@ const defaultHandler: CommentContentReplaceHandler = content => { const [finalReplaceMap] = registerAndGetData(CommentContentReplaceMap, replaceMap) content.forEach(node => { Object.entries(finalReplaceMap).forEach(([from, to]) => { + if (from === to || from === '') { + return + } const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) if (!replacer) { return From 315826f038e12c3945a0c93facec058ad9714eef Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 20 Oct 2024 11:40:58 +0800 Subject: [PATCH 22/49] Add CommentContentReplaceContext, improve namings --- .../content-replace/handlers/index.ts | 37 +++++++++++-------- .../handlers/text-to-emoticon.ts | 4 +- .../content-replace/handlers/types.ts | 10 ++++- .../utils/comments/content-replace/index.ts | 2 +- .../utils/comments/content-replace/utils.ts | 2 +- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/registry/lib/components/utils/comments/content-replace/handlers/index.ts b/registry/lib/components/utils/comments/content-replace/handlers/index.ts index 3062a90aa9..514fdc828a 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/index.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/index.ts @@ -19,23 +19,30 @@ const contentReplacers: NodeContentReplacer[] = [ new RecursiveReplacer(), ] -export const CommentContentReplaceMap = 'commentContentReplace.map' -const defaultHandler: CommentContentReplaceHandler = content => { +export const CommentContentReplaceDefaultHandlerReplaceMap = + 'commentContentReplace.defaultHandler.replaceMap' +const defaultHandler: CommentContentReplaceHandler = ({ content }) => { const { replaceMap } = options - const [finalReplaceMap] = registerAndGetData(CommentContentReplaceMap, replaceMap) - content.forEach(node => { - Object.entries(finalReplaceMap).forEach(([from, to]) => { - if (from === to || from === '') { - return - } - const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) - if (!replacer) { - return - } - const restParts = replacer.replaceContent(node, from, to) - defaultHandler(restParts) + const [finalReplaceMap] = registerAndGetData( + CommentContentReplaceDefaultHandlerReplaceMap, + replaceMap, + ) + const replaceNodes = (nodes: Node[]) => { + nodes.forEach(node => { + Object.entries(finalReplaceMap).forEach(([from, to]) => { + if (from === to || from === '') { + return + } + const replacer = contentReplacers.find(r => r.isKeywordMatch(node, from, to)) + if (!replacer) { + return + } + const restParts = replacer.replaceContent(node, from, to) + replaceNodes(restParts) + }) }) - }) + } + replaceNodes(content) } export const CommentContentReplaceHandlers = 'commentContentReplace.handlers' diff --git a/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts b/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts index 2d06f9ffba..dd71991bd2 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/text-to-emoticon.ts @@ -1,4 +1,4 @@ -import { createEmotionImage, isUrl } from '../utils' +import { createEmoticonImage, isUrl } from '../utils' import { NodeContentReplacer } from './node-content-replacer' export class TextToEmoticonReplacer extends NodeContentReplacer { @@ -14,7 +14,7 @@ export class TextToEmoticonReplacer extends NodeContentReplacer { return [] } const leftPart = new Text(node.textContent.substring(0, index)) - const imageElement = createEmotionImage(target, keyword) + const imageElement = createEmoticonImage(target, keyword) const rightPart = new Text(node.textContent.substring(index + keyword.length)) node.replaceWith(leftPart, imageElement, rightPart) return [rightPart] diff --git a/registry/lib/components/utils/comments/content-replace/handlers/types.ts b/registry/lib/components/utils/comments/content-replace/handlers/types.ts index a62cdbd24f..493f99df7a 100644 --- a/registry/lib/components/utils/comments/content-replace/handlers/types.ts +++ b/registry/lib/components/utils/comments/content-replace/handlers/types.ts @@ -1 +1,9 @@ -export type CommentContentReplaceHandler = (content: Node[]) => void | Promise +import type { CommentItem } from '@/components/utils/comment-apis' + +export interface CommentContentReplaceContext { + commentItem: CommentItem + content: Node[] +} +export type CommentContentReplaceHandler = ( + context: CommentContentReplaceContext, +) => void | Promise diff --git a/registry/lib/components/utils/comments/content-replace/index.ts b/registry/lib/components/utils/comments/content-replace/index.ts index b1a7cc798d..a383c06be9 100644 --- a/registry/lib/components/utils/comments/content-replace/index.ts +++ b/registry/lib/components/utils/comments/content-replace/index.ts @@ -28,7 +28,7 @@ export const component = defineComponentMetadata({ const [handlers] = getData(CommentContentReplaceHandlers) as [ CommentContentReplaceHandler[], ] - handlers.forEach(h => h(Array.from(content.childNodes))) + handlers.forEach(h => h({ commentItem, content: Array.from(content.childNodes) })) }, }) }, diff --git a/registry/lib/components/utils/comments/content-replace/utils.ts b/registry/lib/components/utils/comments/content-replace/utils.ts index 8c096334eb..8df970e63a 100644 --- a/registry/lib/components/utils/comments/content-replace/utils.ts +++ b/registry/lib/components/utils/comments/content-replace/utils.ts @@ -1,4 +1,4 @@ -export const createEmotionImage = (src: string, alt: string) => { +export const createEmoticonImage = (src: string, alt: string) => { const element = document.createElement('img') element.src = src element.alt = alt From c856bf9cce2eb541aac078a1230a9c2f09779a20 Mon Sep 17 00:00:00 2001 From: oxygenkun Date: Sun, 20 Oct 2024 14:46:27 +0800 Subject: [PATCH 23/49] =?UTF-8?q?[feat]=20=E7=9B=B4=E6=92=AD=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=94=A8=E6=96=B0=E7=9A=84url=EF=BC=8C=E7=AE=80?= =?UTF-8?q?=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/feeds/extend-live/LiveList.vue | 71 +++++-------------- 1 file changed, 16 insertions(+), 55 deletions(-) diff --git a/registry/lib/components/feeds/extend-live/LiveList.vue b/registry/lib/components/feeds/extend-live/LiveList.vue index f73827ca2d..3a4412ecce 100644 --- a/registry/lib/components/feeds/extend-live/LiveList.vue +++ b/registry/lib/components/feeds/extend-live/LiveList.vue @@ -50,7 +50,7 @@ From 7668bd0f05f4a084d634932d674f2604ecf670a0 Mon Sep 17 00:00:00 2001 From: the1812 Date: Mon, 21 Oct 2024 21:53:40 +0800 Subject: [PATCH 24/49] Fix pendent styles --- .../style/hide/user-pendent/user-pendent-shadow.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss b/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss index 88e0cef20c..ffba4a2fb0 100644 --- a/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss +++ b/registry/lib/components/style/hide/user-pendent/user-pendent-shadow.scss @@ -3,11 +3,13 @@ container-name: avatar; // .layers:first-child, .layer:not(:first-child), - .layer:not(:has(picture)) { + .layer:not(:has(picture), :has([style*="/face/"])) { display: none; } - .layer:has(picture) { + .layer:is(:has(picture), :has([style*="/face/"])) { min-width: 100cqw; min-height: 100cqh; + max-width: 100cqw; + max-height: 100cqh; } } From 32b64401eab57c82c79c9628cff2bc40e8b68204 Mon Sep 17 00:00:00 2001 From: the1812 Date: Tue, 22 Oct 2024 08:21:17 +0800 Subject: [PATCH 25/49] Update docs --- doc/features/features.json | 32 ++++++++++++++++++++++++++++++++ doc/features/features.md | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/doc/features/features.json b/doc/features/features.json index 176fc61fe9..f5dfb806e1 100644 --- a/doc/features/features.json +++ b/doc/features/features.json @@ -7,6 +7,14 @@ "fullRelativePath": "../../registry/dist/components/feeds/copy-link.js", "fullAbsolutePath": "registry/dist/components/feeds/copy-link.js" }, + { + "type": "component", + "name": "deleteFeeds", + "displayName": "删除动态", + "description": "删除动态, 可选转发抽奖, 和全部删除.\r\n", + "fullRelativePath": "../../registry/dist/components/feeds/del-feeds.js", + "fullAbsolutePath": "registry/dist/components/feeds/del-feeds.js" + }, { "type": "component", "name": "disableFeedsDetails", @@ -135,6 +143,14 @@ "fullRelativePath": "../../registry/dist/components/live/hide-gift-fullscreen.js", "fullAbsolutePath": "registry/dist/components/live/hide-gift-fullscreen.js" }, + { + "type": "component", + "name": "hideLivePlayerBlur", + "displayName": "隐藏直播马赛克", + "description": "移除直播画面中的马赛克区域.\r\n", + "fullRelativePath": "../../registry/dist/components/live/hide-player-blur.js", + "fullAbsolutePath": "registry/dist/components/live/hide-player-blur.js" + }, { "type": "component", "name": "liveHomeMute", @@ -295,6 +311,14 @@ "fullRelativePath": "../../registry/dist/components/style/hide/user-card.js", "fullAbsolutePath": "registry/dist/components/style/hide/user-card.js" }, + { + "type": "component", + "name": "hideUserPendent", + "displayName": "隐藏头像框", + "description": "隐藏页面中用户的头像框 (包括角标), 目前支持动态和视频页面.\r\n", + "fullRelativePath": "../../registry/dist/components/style/hide/user-pendent.js", + "fullAbsolutePath": "registry/dist/components/style/hide/user-pendent.js" + }, { "type": "component", "name": "hideVideoNotes", @@ -527,6 +551,14 @@ "fullRelativePath": "../../registry/dist/components/utils/column-unlock.js", "fullAbsolutePath": "registry/dist/components/utils/column-unlock.js" }, + { + "type": "component", + "name": "commentContentReplace", + "displayName": "评论内容替换", + "description": "替换评论中的内容.\r\n\r\n可以添加多个替换配置, 每项配置可将一个关键词替换为其他文本. 若替换的目标是一个链接, 则视作替换为表情, 留空则会移除命中的关键词.\r\n", + "fullRelativePath": "../../registry/dist/components/utils/comments/content-replace.js", + "fullAbsolutePath": "registry/dist/components/utils/comments/content-replace.js" + }, { "type": "component", "name": "copyCommentsLink", diff --git a/doc/features/features.md b/doc/features/features.md index 570495dddf..7a2126e1e3 100644 --- a/doc/features/features.md +++ b/doc/features/features.md @@ -11,6 +11,15 @@ 开启后, 可在每条动态的菜单中选择复制链接. +### [删除动态](../../registry/dist/components/feeds/del-feeds.js) +`deleteFeeds` + +**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/feeds/del-feeds.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/feeds/del-feeds.js) + +**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/feeds/del-feeds.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/feeds/del-feeds.js) + +删除动态, 可选转发抽奖, 和全部删除. + ### [禁止跳转动态详情](../../registry/dist/components/feeds/disable-details.js) `disableFeedsDetails` @@ -170,6 +179,15 @@ by [@TimmyOVO](https://github.com/TimmyOVO) 移除全屏观看直播时的底部礼物栏 +### [隐藏直播马赛克](../../registry/dist/components/live/hide-player-blur.js) +`hideLivePlayerBlur` + +**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/live/hide-player-blur.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/live/hide-player-blur.js) + +**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/live/hide-player-blur.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/live/hide-player-blur.js) + +移除直播画面中的马赛克区域. + ### [直播首页静音](../../registry/dist/components/live/home-mute.js) `liveHomeMute` @@ -389,6 +407,15 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56) 隐藏鼠标指向用户名或用户头像时弹出的浮动用户信息卡片 +### [隐藏头像框](../../registry/dist/components/style/hide/user-pendent.js) +`hideUserPendent` + +**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/style/hide/user-pendent.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/style/hide/user-pendent.js) + +**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/hide/user-pendent.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/hide/user-pendent.js) + +隐藏页面中用户的头像框 (包括角标), 目前支持动态和视频页面. + ### [隐藏记笔记](../../registry/dist/components/style/hide/video/notes.js) `hideVideoNotes` @@ -695,6 +722,17 @@ by [@snowraincloud](https://github.com/snowraincloud) 避免专栏的文字复制后在最后带上出处信息, 更贴近原生的复制行为. +### [评论内容替换](../../registry/dist/components/utils/comments/content-replace.js) +`commentContentReplace` + +**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/utils/comments/content-replace.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/utils/comments/content-replace.js) + +**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/comments/content-replace.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/comments/content-replace.js) + +替换评论中的内容. + +可以添加多个替换配置, 每项配置可将一个关键词替换为其他文本. 若替换的目标是一个链接, 则视作替换为表情, 留空则会移除命中的关键词. + ### [复制评论链接](../../registry/dist/components/utils/comments/copy-link.js) `copyCommentsLink` From f26495c63bb5015d668b54f5cda4ba66133dcde0 Mon Sep 17 00:00:00 2001 From: the1812 Date: Sun, 10 Nov 2024 14:03:04 +0800 Subject: [PATCH 26/49] Improve search and empty state (#4975, #4973) --- registry/lib/docs/index.ts | 1 + .../online-registry/OnlineRegistry.vue | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/registry/lib/docs/index.ts b/registry/lib/docs/index.ts index 37b60ce513..9b7d711730 100644 --- a/registry/lib/docs/index.ts +++ b/registry/lib/docs/index.ts @@ -19,6 +19,7 @@ export interface DocSourceItem { name: string displayName: string description?: string + descriptionText?: string fullAbsolutePath: string fullRelativePath: string owner?: string 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 @@
- +