Merge branch 'preview-fixes'

This commit is contained in:
the1812 2025-04-09 08:59:50 +08:00
commit 95fd680f29
32 changed files with 489 additions and 110 deletions

View File

@ -51,6 +51,7 @@
"Fullscreen",
"githubusercontent",
"Greasemonkey",
"guochuang",
"haruna",
"hdslb",
"HEVC",

View File

@ -1,6 +1,60 @@
<!-- spell-checker: disable -->
# 更新日志
## v2.10.1-preview
`2025-04-09`
包含 [v2.10.1](https://github.com/the1812/Bilibili-Evolved/releases/tag/v2.10.1) 的所有更新内容.
- 新增组件 `一键点亮直播间粉丝勋章`. (PR #5171 by [undefined](https://github.com/magicFeirl))
> 在直播间页面的功能面板添加一键点亮粉丝勋章功能,仅适用于有粉丝勋章且正在直播的直播间。原理:发送一个 300 次点赞的请求点亮粉丝勋章。
- 新增组件 `下载表情`. (PR #5176 by [Pencil](https://github.com/pencilqaq))
> 支持下载 up 主专属表情包,处于任意直播间页面时,下载按钮会在 `功能` 面板显示,以压缩包形式保存。
- 新增组件 `视频链接增强`. (PR #5197 by [Alan Ye](https://github.com/at-wr))
> 这个组件会将视频简介中的普通网址转换为可点击的链接,并将<del>被 Bilibili 抛弃</del>已失效的 `acg.tv` 跳转链接修复为 `nicovideo.jp` 链接。
>
> 例如: `https://acg.tv/sm37507315``https://www.nicovideo.jp/watch/sm37507315`
>
> <i>本组件不会保证目标链接的安全性,因此在点击前请自行验证其是否可信</i>
- `自定义顶栏` 新增 `使用主题色 Logo` 选项, 关闭时可以使用黑白纯色的 Logo. (#4996)
- `自定义顶栏` 更新 `游戏中心`, `漫画`, `赛事` 的弹窗, 废弃 `直播` 的弹窗. (#5055)
## v2.10.1
`2025-04-09`
<details>
<summary>正式版用户将获得 v2.9.6-preview ~ v2.10.0-preview 的所有改动, 点击展开查看</summary>
- 新增插件 `自定义顶栏 - 版权内容`. (#5045)
> 为自定义顶栏扩充版权内容相关的快速入口, 包括国创 / 电影 / 电视剧 /综艺 / 纪录片
- 视频截图在还有截图未处理 (保存 / 丢弃) 时, 离开页面增加二次确认. (#5066)
- `插件 - 下载视频 - WASM 混流输出` 在超过 4GB 时支持抛出错误提示. (PR #5102 by [WakelessSloth56](https://github.com/WakelessSloth56))
- `自定义顶栏` 的透明填充在同时开启了全局固定时, 支持下拉后过渡到不透明状态. (#4996)
- `自定义顶栏` 的弹窗增加了 0.2s 的触发延迟, 防止鼠标快速经过时误触. (#4949)
- 废弃 `双击全屏` 组件, 本体也不再会屏蔽双击全屏. (#5095)
</details>
✨新增
- 新增组件 `禁用双击全屏`. (#5095)
> ⚠ 请注意 `双击全屏` 组件已废弃, 本体也不再会屏蔽双击全屏, 更新后可以直接卸载 `双击全屏` 组件
- `快捷键扩展` 的使用说明增加关于屏蔽 b 站自带快捷键的指南. (#5168)
- `动态过滤器` 增加 `课程`, `直播`, `合集` 类型. (#5135)
> 这些类型的支持是根据 b 站代码里的枚举加的, 我的动态里没有这些类型无法测试, 如果还有相关问题请继续在 #5135 中反馈
🐛修复
- 修复 `快捷键扩展` 在聚焦元素相关的动作中出现失效的情况. (#5150, #5153, #5174)
- 修复 `启用快速收藏` 在存在和默认收藏夹同名的收藏夹时, 收藏到了错误的收藏夹. (#5151)
- 修复 `图片批量导出` 在 Firefox 中导出动态图片时出现重复图片. (#5166)
- 修复 `夜间模式` 导致登录时二维码扫码困难. (#5163, PR #5173 by [Pencil](https://github.com/pencilqaq))
- 修复 `删除广告` 在搜索页出现的空位. (#5032)
## v2.10.0-preview
`2025-02-25`

View File

@ -32,7 +32,7 @@
**注意事项**
- **⚠ 使用正式版 (GitHub 源) 和预览版须翻墙.**
- 默认不预装任何组件, 需要在按[设置](#设置)中的说明添加感兴趣的功能; 默认会屏蔽双击全屏, 如有需要可以安装 `双击全屏` 组件来恢复.
- 默认不预装任何组件, 需要在按[设置](#设置)中的说明添加感兴趣的功能.
- 这个是一个综合性的脚本, 如果只是想下载视频建议去 [GreasyFork](https://greasyfork.org/zh-CN/scripts/by-site/bilibili.com) 找个更专业的.
- 对性能有较大影响, 详见[配置要求](#配置).
- 默认不对未登录的状态做适配.

View File

@ -475,7 +475,7 @@
"type": "component",
"name": "doubleClickControl",
"displayName": "启用双击控制",
"description": "将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停. 请注意不能和 `双击全屏` 一同使用.",
"description": "将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停.\r\n",
"fullRelativePath": "../../registry/dist/components/touch/double-click-control.js",
"fullAbsolutePath": "registry/dist/components/touch/double-click-control.js"
},
@ -815,6 +815,14 @@
"fullRelativePath": "../../registry/dist/components/video/player/default-mode.js",
"fullAbsolutePath": "registry/dist/components/video/player/default-mode.js"
},
{
"type": "component",
"name": "disableDoubleClickFullscreen",
"displayName": "禁用双击全屏",
"description": "禁用播放器的双击全屏功能.\r\n",
"fullRelativePath": "../../registry/dist/components/video/player/disable-double-click-fullscreen.js",
"fullAbsolutePath": "registry/dist/components/video/player/disable-double-click-fullscreen.js"
},
{
"type": "component",
"name": "disableScrollVolume",
@ -823,14 +831,6 @@
"fullRelativePath": "../../registry/dist/components/video/player/disable-scroll-volume.js",
"fullAbsolutePath": "registry/dist/components/video/player/disable-scroll-volume.js"
},
{
"type": "component",
"name": "doubleClickFullscreen",
"displayName": "双击全屏",
"description": "为视频播放器启用双击全屏功能, 请注意不能和 `启用双击控制` 一同使用.",
"fullRelativePath": "../../registry/dist/components/video/player/double-click-fullscreen.js",
"fullAbsolutePath": "registry/dist/components/video/player/double-click-fullscreen.js"
},
{
"type": "component",
"name": "extendVideoSpeed",
@ -1056,6 +1056,14 @@
"fullRelativePath": "../../registry/dist/plugins/style/custom-navbar-dark-mode.js",
"fullAbsolutePath": "registry/dist/plugins/style/custom-navbar-dark-mode.js"
},
{
"type": "plugin",
"name": "customNavbar.items.pgc",
"displayName": "自定义顶栏 - 版权内容",
"description": "为自定义顶栏扩充版权内容相关的快速入口, 包括国创 / 电影 / 电视剧 /综艺 / 纪录片",
"fullRelativePath": "../../registry/dist/plugins/style/custom-navbar-pgc.js",
"fullAbsolutePath": "registry/dist/plugins/style/custom-navbar-pgc.js"
},
{
"type": "plugin",
"name": "keymap.actions.darkMode",

View File

@ -624,7 +624,7 @@ by [@RieN7](https://github.com/rien7),[@ZiuChen](https://github.com/ZiuChen),[@L
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/touch/double-click-control.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/touch/double-click-control.js)
将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停. 请注意不能和 `双击全屏` 一同使用.
将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停.
### [迷你播放器触摸拖动](../../registry/dist/components/touch/mini-player.js)
`touchMiniPlayer`
@ -1124,6 +1124,15 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56),[@LainIO24](https://gi
控制是否使用默认播放器模式, 可以为`常规`, `宽屏`, `网页全屏`或`全屏`. 注意: 不能和其他影响定位的功能一同使用, 例如播放器定位. (相关讨论: [#483](https://github.com/the1812/Bilibili-Evolved/issues/483))
### [禁用双击全屏](../../registry/dist/components/video/player/disable-double-click-fullscreen.js)
`disableDoubleClickFullscreen`
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/video/player/disable-double-click-fullscreen.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/video/player/disable-double-click-fullscreen.js)
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/video/player/disable-double-click-fullscreen.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/video/player/disable-double-click-fullscreen.js)
禁用播放器的双击全屏功能.
### [禁止滚轮调音量](../../registry/dist/components/video/player/disable-scroll-volume.js)
`disableScrollVolume`
@ -1133,15 +1142,6 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56),[@LainIO24](https://gi
在网页全屏 / 全屏模式下, 禁止鼠标滚轮控制播放器的音量. 请注意不能和 `反转滚轮调音量` 一同使用.
### [双击全屏](../../registry/dist/components/video/player/double-click-fullscreen.js)
`doubleClickFullscreen`
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/video/player/double-click-fullscreen.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/video/player/double-click-fullscreen.js)
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/video/player/double-click-fullscreen.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/video/player/double-click-fullscreen.js)
为视频播放器启用双击全屏功能, 请注意不能和 `启用双击控制` 一同使用.
### [扩展倍速](../../registry/dist/components/video/player/extend-speed.js)
`extendVideoSpeed`
@ -1472,6 +1472,15 @@ by FoundTheWOUT
为自定义顶栏添加一个夜间模式开关, 方便快速切换夜间模式.
### [自定义顶栏 - 版权内容](../../registry/dist/plugins/style/custom-navbar-pgc.js)
`customNavbar.items.pgc`
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/plugins/style/custom-navbar-pgc.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/plugins/style/custom-navbar-pgc.js)
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/plugins/style/custom-navbar-pgc.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/plugins/style/custom-navbar-pgc.js)
为自定义顶栏扩充版权内容相关的快速入口, 包括国创 / 电影 / 电视剧 /综艺 / 纪录片
### [快捷键扩展 - 夜间模式](../../registry/dist/plugins/utils/keymap-dark-mode.js)
`keymap.actions.darkMode`

View File

@ -146,7 +146,7 @@ export default Vue.extend({
}
this.allTypes = Object.entries(feedsCardTypes)
.concat(Object.entries(specialTypes))
.filter(([, type]) => type.id <= 2048)
.filter(([, type]) => type.id <= 2048 && type.id > 0)
.map(([name, type]) => [name, lodash.clone(type)])
cardsManager = await forEachFeedsCard({
added: card => {

View File

@ -74,6 +74,7 @@ export default Vue.extend({
<style lang="scss">
@import 'common';
@import './scroll-animation';
.van-message-box {
z-index: 10002 !important;
@ -121,7 +122,6 @@ body.fixed-navbar {
.custom-navbar *,
.custom-navbar {
transition: all 0.2s ease-out;
-webkit-tap-highlight-color: transparent;
outline: none !important;
}

View File

@ -170,12 +170,28 @@ export default Vue.extend({
<style lang="scss">
@import 'common';
@keyframes navbar-popup-in {
to {
pointer-events: initial;
}
}
// @keyframes navbar-popup-out {
// from {
// pointer-events: none;
// }
// to {
// pointer-events: none;
// opacity: 0;
// }
// }
.custom-navbar-item {
color: inherit;
position: relative;
height: 100%;
display: flex;
align-items: center;
transition: 0.2s background-color ease-out;
.active-bar {
position: absolute;
@ -241,7 +257,7 @@ export default Vue.extend({
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.05);
border: 1px solid #8882;
border-radius: 8px;
transition: all 0.2s ease-out 0.2s;
transition: opacity 0.2s ease-out 0.2s;
position: absolute;
top: 100%;
left: 50%;
@ -270,14 +286,17 @@ export default Vue.extend({
background-color: transparent !important;
box-shadow: none;
}
> * {
@include default-transition();
}
}
&:not(.disabled) .popup-container {
position: absolute;
top: calc(100% - 8px);
left: 50%;
transition: all 0.2s ease-out 0.2s;
pointer-events: none;
transition: all 0.2s ease-out 0.2s;
}
&:not(.disabled):hover,
@ -285,8 +304,7 @@ export default Vue.extend({
.popup-container {
top: 100%;
> .popup {
// transform: translateY(0) translateX(-50%);
pointer-events: initial;
animation: navbar-popup-in 0.2s ease-out 0.2s both;
opacity: 1;
}
}

View File

@ -0,0 +1,183 @@
@property --navbar-foreground {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
@property --navbar-background {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
@property --foreground-color-d {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
@property --theme-color {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
@property --theme-color-60 {
syntax: '<color>';
inherits: true;
initial-value: transparent;
}
@keyframes transparent-to-light {
to {
--navbar-foreground: #555;
--navbar-background: white;
}
}
@keyframes transparent-to-light-blur {
to {
--navbar-foreground: #555;
--navbar-background: #fffc;
backdrop-filter: blur(24px);
}
}
@keyframes transparent-to-dark {
to {
--navbar-foreground: #eee;
--navbar-background: #222;
}
}
@keyframes transparent-to-dark-blur {
to {
--navbar-foreground: #eee;
--navbar-background: #2228;
backdrop-filter: blur(24px);
}
}
@keyframes transparent-to-theme {
to {
--navbar-foreground: var(--foreground-color-d);
--navbar-background: var(--theme-color);
}
}
@keyframes transparent-to-theme-blur {
to {
--navbar-foreground: var(--foreground-color-d);
--navbar-background: var(--theme-color-60);
backdrop-filter: blur(24px);
}
}
@keyframes transparent-to-light-shadow {
to {
--navbar-foreground: #555;
--navbar-background: white;
box-shadow: #0002 0 1px 10px 1px;
}
}
@keyframes transparent-to-light-blur-shadow {
to {
--navbar-foreground: #555;
--navbar-background: #fffc;
backdrop-filter: blur(24px);
box-shadow: #0002 0 1px 10px 1px;
}
}
@keyframes transparent-to-dark-shadow {
to {
--navbar-foreground: #eee;
--navbar-background: #222;
box-shadow: #0004 0px 2px 10px 1px;
}
}
@keyframes transparent-to-dark-blur-shadow {
to {
--navbar-foreground: #eee;
--navbar-background: #2228;
backdrop-filter: blur(24px);
box-shadow: #0004 0px 2px 10px 1px;
}
}
@keyframes transparent-to-theme-shadow {
to {
--navbar-foreground: var(--foreground-color-d);
--navbar-background: var(--theme-color);
box-shadow: #0002 0 1px 10px 1px;
}
}
@keyframes transparent-to-theme-blur-shadow {
to {
--navbar-foreground: var(--foreground-color-d);
--navbar-background: var(--theme-color-60);
backdrop-filter: blur(24px);
box-shadow: #0002 0 1px 10px 1px;
}
}
@keyframes transparent-mask {
to {
opacity: 0;
}
}
@keyframes launch-bar-colors {
to {
background-color: #8883;
}
}
@keyframes launch-bar-colors-fill {
to {
background-color: #0002;
}
}
@supports (animation-timeline: scroll()) {
.custom-navbar.transparent {
--animation-range: clamp(116px, 7vw, 180px) clamp(155px, 9.375vw, 240px);
&::before {
animation: transparent-mask linear both;
animation-timeline: scroll();
animation-range: var(--animation-range);
}
body.fixed-navbar & {
animation: transparent-to-light linear both;
animation-timeline: scroll();
animation-range: var(--animation-range);
.launch-bar {
animation: launch-bar-colors linear both;
animation-timeline: scroll();
animation-range: var(--animation-range);
}
&.shadow {
animation-name: transparent-to-light-shadow;
}
&.blur:not(.fill) {
animation-name: transparent-to-light-blur;
&.shadow {
animation-name: transparent-to-light-blur-shadow;
}
}
&.fill {
animation-name: transparent-to-theme;
.launch-bar {
animation-name: launch-bar-colors-fill;
}
&.shadow {
animation-name: transparent-to-theme-shadow;
}
&.blur {
animation-name: transparent-to-theme-blur;
&.shadow {
animation-name: transparent-to-theme-blur-shadow;
}
}
}
}
body.fixed-navbar.dark &:not(.fill) {
animation-name: transparent-to-dark;
&.shadow {
animation-name: transparent-to-dark-shadow;
}
&.blur {
animation-name: transparent-to-dark-blur;
&.shadow {
animation-name: transparent-to-dark-blur-shadow;
}
}
}
}
}

View File

@ -102,6 +102,7 @@ export default Vue.extend({
cursor: pointer;
position: relative;
box-sizing: border-box;
transition: 0.2s background-color ease-out;
&.loading {
font-size: 10pt;
cursor: initial;

View File

@ -59,10 +59,11 @@ export default Vue.extend({
position: absolute;
width: 100%;
height: 100%;
transition: 0.2s all ease-out 0.2s;
opacity: 1;
z-index: 100;
.custom-navbar-item:hover & {
transform: scale(2) translateY(10px);
z-index: 100;
opacity: 1;
}
}
.user-face {

View File

@ -6,3 +6,28 @@
@include background-color();
}
}
.login-scan__qrcode {
@include background-color('f');
.qrcode__tip {
@include background-color('3');
img {
@include background-color();
}
span {
@include color('a');
}
}
}
.scan-tips-icon{
@include background-color('2');
}
.btn_other {
@include background-color('3');
@include border-color('4');
@include color('f');
}

View File

@ -1,5 +1,18 @@
.search-panel {
> .trending {
display: none !important;
}
&:not(:has(.history)) {
padding: 0 !important;
border: none !important;
}
}
#nav-searchform.is-focus {
/* 搜索框 focusing 且有关键字列表 or 历史记录列表时不显示低边框*/
&:not(:has(+ .search-panel > .suggestions), :has(+ .search-panel > .history)) {
border-radius: 8px !important;
}
}

View File

@ -1,5 +1,13 @@
import { defineComponentMetadata } from '@/components/define'
import { allMutations } from '@/core/observer'
import { select } from '@/core/spin-query'
const DEFAULT_PLACEHOLDER = '搜索'
const resetPlaceholder = (input: HTMLInputElement) => {
input.placeholder = DEFAULT_PLACEHOLDER
input.title = DEFAULT_PLACEHOLDER
}
export const component = defineComponentMetadata({
name: 'hideTrendingSearch',
@ -12,14 +20,24 @@ export const component = defineComponentMetadata({
},
],
entry: async () => {
const input: HTMLInputElement = await select('input.nav-search-input', {
queryInterval: 500,
})
if (input) {
resetPlaceholder(input)
return
}
// Fallback to observer
allMutations(records => {
records.forEach(record => {
if (
record.target instanceof HTMLInputElement &&
record.target.classList.contains('nav-search-input') &&
record.target.placeholder !== '搜索'
record.target.placeholder !== DEFAULT_PLACEHOLDER
) {
record.target.placeholder = '搜索'
resetPlaceholder(record.target)
}
})
})

View File

@ -0,0 +1 @@
将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停.

View File

@ -27,10 +27,6 @@ const entry = async () => {
export const component = defineComponentMetadata({
name: 'doubleClickControl',
displayName: '启用双击控制',
description: {
'zh-CN':
'将视频播放器的操作方式更改为: 单击显示 / 隐藏控制栏, 双击播放 / 暂停. 请注意不能和 `双击全屏` 一同使用.',
},
tags: [componentsTags.touch],
enabledByDefault: navigator.maxTouchPoints > 0,
urlInclude: playerUrls,

View File

@ -57,7 +57,8 @@ export const loadKeyBindings = lodash.once((bindings: KeyBinding[]) => {
return true
})()
if (
(binding.action.ignoreFocus !== false || binding.action.ignoreTyping !== false) &&
binding.action.ignoreFocus !== false &&
binding.action.ignoreTyping !== false &&
hasElementFocus
) {
return

View File

@ -9,6 +9,7 @@
- 如果希望映射到多种按键, 可以继续用空格隔开, 但注意组合键是固定的, 例如: 配置 `a b` 表示 `A``B` 键, 而配置 `shift a b` 就表示 `Shift + A` / `Shift + B`
- 组合键是精确匹配的, 按下 `Ctrl + Shift + A` 不能触发配置为 `shift a` 的快捷键
- 如果希望组合键可按可不按, 可以给它套上一个 `[]`, 例如 `[ctrl] shift a` 表示 `Ctrl + Shift +A` / `Shift + A`
- b 站自带有一些快捷键, 修改本组件提供的相同动作快捷键不会影响 b 站自带的快捷键, 如果希望屏蔽掉 b 站自带的快捷键, 可以安装 `快捷键扩展 - 无动作` 插件, 并将无动作绑定到希望屏蔽的快捷键上. 例如希望用 `C` 键投币且不希望自带的 `W` 键触发投币, 则应该在组件里配置投币为 `c`, 无动作为 `w`
## 预设
在第三列的表头中可以通过下拉框切换预设, 预设是一套固定的按键定义, 方便快速切换到用户更熟悉的按键风格中. 插件可以使用 `keymap.presets` 作为 key 添加新的预设.

View File

@ -37,7 +37,8 @@ body:not(.preserve-feed-goods) .dyn-goods,
.adcard,
.palette-button-adcard,
.palette-button-wrap .adcard-content,
.video-card-ad-small {
.video-card-ad-small,
.search-all-list .video-list > *:has(.bili-video-card__info--ad) {
display: none !important;
}
.recommend-list .rec-list > :not(.video-page-card) + .video-page-card {

View File

@ -0,0 +1 @@
禁用播放器的双击全屏功能.

View File

@ -0,0 +1,24 @@
import { defineComponentMetadata } from '@/components/define'
import { select } from '@/core/spin-query'
import { preventEvent } from '@/core/utils'
import { playerUrls } from '@/core/utils/urls'
let dispose: (() => void) | undefined
const entry = async () => {
const layer = (await select('.bpx-player-video-perch')) as HTMLElement
if (!layer) {
return
}
dispose = preventEvent(layer, 'dblclick')
}
export const component = defineComponentMetadata({
name: 'disableDoubleClickFullscreen',
displayName: '禁用双击全屏',
entry,
reload: entry,
unload: () => {
dispose?.()
},
tags: [componentsTags.video],
urlInclude: playerUrls,
})

View File

@ -1,50 +0,0 @@
import {
defineOptionsMetadata,
OptionsOfMetadata,
defineComponentMetadata,
} from '@/components/define'
import { ComponentEntry } from '@/components/types'
import { playerUrls } from '@/core/utils/urls'
const options = defineOptionsMetadata({
preventSingleClick: {
displayName: '双击时阻止单击事件',
defaultValue: true,
},
})
export type Options = OptionsOfMetadata<typeof options>
const entry: ComponentEntry<Options> = async ({ settings }) => {
const { videoChange } = await import('@/core/observer')
videoChange(async () => {
const { DoubleClickEvent } = await import('@/core/utils')
const { playerAgent } = await import('@/components/video/player-agent')
const playerArea = await playerAgent.query.danmakuTipLayer()
if (playerArea === null) {
console.error('playerArea not found')
return
}
const className = 'double-click-fullscreen'
if (!playerArea.classList.contains(className)) {
playerArea.classList.add(className)
const container = playerAgent.query.video.container.sync()
const doubleClick = new DoubleClickEvent(
() => playerAgent.fullscreen(),
settings.options.preventSingleClick,
)
doubleClick.singleClickHandler = () => playerAgent.togglePlay()
doubleClick.bind(container)
}
})
}
export const component = defineComponentMetadata({
name: 'doubleClickFullscreen',
displayName: '双击全屏',
description: '为视频播放器启用双击全屏功能, 请注意不能和 `启用双击控制` 一同使用.',
entry,
tags: [componentsTags.video],
options,
urlInclude: playerUrls,
})

View File

@ -8,10 +8,16 @@ import desc from './desc.md'
import ScreenshotContainer from './VideoScreenshotContainer.vue'
export const VideoScreenshotDisabledClass = 'video-screenshot-disable'
const entry = async () => {
let screenShotsList: Vue & {
screenshots: Screenshot[]
let screenShotsList: Vue & {
screenshots: Screenshot[]
}
const exitConfirmHandler = (e: BeforeUnloadEvent) => {
if (screenShotsList?.screenshots.length > 0) {
e.preventDefault()
}
}
const entry = async () => {
addControlBarButton({
name: 'takeScreenshot',
displayName: '截图',
@ -35,6 +41,7 @@ const entry = async () => {
}
},
})
window.addEventListener('beforeunload', exitConfirmHandler)
}
export const component = defineComponentMetadata({
name: 'videoScreenshot',
@ -45,8 +52,14 @@ export const component = defineComponentMetadata({
'zh-CN': desc,
},
urlInclude: playerUrls,
reload: () => document.body.classList.remove(VideoScreenshotDisabledClass),
unload: () => document.body.classList.add(VideoScreenshotDisabledClass),
reload: () => {
document.body.classList.remove(VideoScreenshotDisabledClass)
window.addEventListener('beforeunload', exitConfirmHandler)
},
unload: () => {
document.body.classList.add(VideoScreenshotDisabledClass)
window.removeEventListener('beforeunload', exitConfirmHandler)
},
plugin: {
displayName: '视频截图 - 快捷键支持',
setup: ({ addData }) => {

View File

@ -0,0 +1,46 @@
import { PluginMetadata } from '@/plugins/plugin'
import type { CustomNavbarItemInit } from '../../../components/style/custom-navbar/custom-navbar-item'
export const plugin: PluginMetadata = {
name: 'customNavbar.items.pgc',
displayName: '自定义顶栏 - 版权内容',
description: '为自定义顶栏扩充版权内容相关的快速入口, 包括国创 / 电影 / 电视剧 /综艺 / 纪录片',
async setup({ addData }) {
addData('customNavbar.items', (items: CustomNavbarItemInit[]) => {
const pgcItems = [
{
name: 'guochuang',
displayName: '国创',
href: 'https://www.bilibili.com/guochuang/',
},
{
name: 'movie',
displayName: '电影',
href: 'https://www.bilibili.com/movie/',
},
{
name: 'tv',
displayName: '电视剧',
href: 'https://www.bilibili.com/tv/',
},
{
name: 'variety',
displayName: '综艺',
href: 'https://www.bilibili.com/variety/',
},
{
name: 'documentary',
displayName: '纪录片',
href: 'https://www.bilibili.com/documentary/',
},
]
items.push(
...pgcItems.map(item => ({
...item,
content: item.displayName,
active: document.URL.startsWith(item.href),
})),
)
})
},
}

View File

@ -2,7 +2,7 @@ import { DownloadPackage, PackageEntry } from '@/core/download'
import { meta } from '@/core/meta'
import { getComponentSettings } from '@/core/settings'
import { Toast } from '@/core/toast'
import { formatPercent } from '@/core/utils/formatters'
import { formatFileSize, formatPercent } from '@/core/utils/formatters'
import { title as pluginTitle } from '.'
import type { Options } from '../../../../components/video/download'
import { DownloadVideoAction } from '../../../../components/video/download/types'
@ -139,6 +139,10 @@ export async function run(action: DownloadVideoAction, muxWithMetadata: boolean)
throw new Error('仅支持 DASH 格式视频和音频')
}
if (video.size + audio.size > 4294967295) {
throw new Error(`仅支持合并 4GB 内的音视频(${formatFileSize(video.size + audio.size)}`)
}
await single(
video.title,
video.url,

View File

@ -19,7 +19,7 @@ export const plugin: PluginMetadata = {
outputs.push({
name: 'wasm',
displayName: 'WASM',
description: `${desc}。运行过程中请勿关闭页面,初次使用或清除缓存后需要加载约 30 MB 的 WASM 文件。`,
description: `${desc}。运行过程中请勿关闭页面,初次使用或清除缓存后需要加载约 30 MB 的 WASM 文件。由于浏览器限制,仅支持合并 4GB 以内的音视频。`,
runAction: async (action, instance) => {
try {
await run(action, instance.muxWithMetadata)

View File

@ -1,5 +1,5 @@
{
"version": "2.10.0",
"version": "2.10.1",
"author": "Grant Howard, Coulomb-G",
"copyright": "[year], Grant Howard (https://github.com/the1812) & Coulomb-G (https://github.com/Coulomb-G)",
"license": "MIT",

View File

@ -15,6 +15,12 @@ const feedsCardTypeMap = {
DynamicTypeArticle: feedsCardTypes.column,
DynamicTypeMusic: feedsCardTypes.audio,
DynamicTypeLiveRcmd: feedsCardTypes.liveRecord,
DynamicTypeCourses: feedsCardTypes.courses,
DynamicTypeOpus: feedsCardTypes.textWithImages,
DynamicTypeLive: feedsCardTypes.live,
DynamicTypeMedialist: feedsCardTypes.mediaList,
DynamicTypeSubscription: feedsCardTypes.mediaList,
DynamicTypeUgcSeason: feedsCardTypes.ugcSeason,
}
const combineText = (...texts: string[]) =>
@ -23,7 +29,7 @@ const combineText = (...texts: string[]) =>
.join('\n')
.trim()
const getType = (rawType: string): FeedsCardType =>
feedsCardTypeMap[pascalCase(rawType)] ?? feedsCardTypeMap.DynamicTypeWord
feedsCardTypeMap[pascalCase(rawType)] ?? feedsCardTypes.unknown
const getText = (dynamicModule: any, cardType: FeedsCardType) => {
const isOpusModule = Object.hasOwn(dynamicModule, 'paragraphs')
if (isOpusModule) {

View File

@ -15,6 +15,10 @@ export interface RepostFeedsCardType extends FeedsCardType {
name: '转发'
}
export const feedsCardTypes = {
unknown: {
id: 0,
name: '未知',
},
repost: {
id: 1,
name: '转发',
@ -78,8 +82,20 @@ export const feedsCardTypes = {
id: 4300,
name: '收藏夹',
},
courses: {
id: 2044,
name: '课程',
},
live: {
id: 2045,
name: '直播',
},
ugcSeason: {
id: 2046,
name: '合集',
},
liveRecord: {
id: 2047, // FIXME: 暂时随便写个 id 了, 这个东西目前找不到 type
id: 2047,
name: '开播记录',
},
} satisfies Record<string, FeedsCardType>

View File

@ -73,7 +73,6 @@ export default Vue.extend({
--color: #eee;
}
.launch-bar-suggest-list {
transition: 0.2s ease-out;
top: calc(100% + 8px);
max-height: calc(80vh - 16px - #{$barHeight});
@include no-scrollbar();

View File

@ -333,7 +333,7 @@ export default Vue.extend({
width: 100%;
transform: translateX(-50%) translateY(-4px);
pointer-events: none;
// transition: 0.2s ease-out;
transition: 0.2s all ease-out;
border: 1px solid #8882;
white-space: nowrap;
border-radius: 8px;

View File

@ -1,6 +1,6 @@
import { attributes } from '@/core/observer'
import { select } from '@/core/spin-query'
import { playerReady, preventEvent } from '@/core/utils'
import { playerReady } from '@/core/utils'
import { createPlayerModeChangeEvent, PlayerMode } from './events'
/** 播放器模式标记同步 */
@ -36,19 +36,8 @@ const playerModePolyfill = async () => {
}
})
}
/** 3.x .
* , , 使.
*/
const doubleClickPolyfill = async () => {
const layer = (await select('.bpx-player-video-perch')) as HTMLElement
if (!layer) {
return
}
preventEvent(layer, 'dblclick')
}
/** 番剧 & 视频播放器 (BPX) 通用 polyfill */
export const bpxPlayerPolyfill = lodash.once(async () => {
playerModePolyfill()
doubleClickPolyfill()
})