Merge branch 'preview-fixes'

This commit is contained in:
the1812 2022-03-21 13:54:55 +08:00
commit 0c22c3a952
23 changed files with 279 additions and 207 deletions

View File

@ -7,6 +7,7 @@ export const history: CustomNavbarItemInit = {
content: '历史',
href,
touch: true,
active: document.URL.replace(/\?.*$/, '') === href,
loginRequired: true,

View File

@ -17,7 +17,7 @@ export default Vue.extend({
// transparent mode / no fill
.launch-bar {
--color: var(--custom-navbar-foreground);
background-color: #0002;
background-color: #000A;
opacity: 0.5;
transition: opacity 0.2s ease-out;
padding: 2px 6px;

View File

@ -1,4 +1,3 @@
import { attributes } from '@/core/observer'
import { addComponentListener, getComponentSettings } from '@/core/settings'
import { sq } from '@/core/spin-query'
import { mainSiteUrls, matchCurrentPage } from '@/core/utils/urls'
@ -13,23 +12,35 @@ export const checkTransparentFill = async (vm: {
return
}
sq(
() => dq('#banner_link,.international-header .bili-banner, .bili-header__banner'),
banner => (banner === null ? false : Boolean((banner as HTMLElement).style.backgroundImage)),
).then((banner: HTMLElement) => {
if (!banner) {
return
}
attributes(banner, () => {
addComponentListener('customNavbar.transparent', value => {
if (!getComponentSettings('hideBanner').enabled) {
vm.toggleStyle(value, 'transparent')
() => dqa('.animated-banner video, .banner-img img, #banner_link, .international-header .bili-banner, .bili-header__banner'),
banners => {
if (banners.length === 0) {
return false
}
const hasBannerImage = (banner: HTMLElement) => {
if (banner.style.backgroundImage) {
return true
}
}, true)
addComponentListener('hideBanner', value => {
if (getComponentSettings('customNavbar').options.transparent) {
vm.toggleStyle(!value, 'transparent')
if ((banner as HTMLVideoElement | HTMLImageElement).src) {
return true
}
})
return false
}
if (banners.some(hasBannerImage)) {
return true
}
return false
},
).then(() => {
addComponentListener('customNavbar.transparent', value => {
if (!getComponentSettings('hideBanner').enabled) {
vm.toggleStyle(value, 'transparent')
}
}, true)
addComponentListener('hideBanner', value => {
if (getComponentSettings('customNavbar').options.transparent) {
vm.toggleStyle(!value, 'transparent')
}
})
})
}

View File

@ -239,6 +239,7 @@
}
}
.s_tag .tag-area {
.tag,
.tag-item {
@include background-color("4");
@include border-color();

View File

@ -112,6 +112,12 @@
@include color("e");
}
}
&-title {
@include color("e");
&:hover {
@include theme-color();
}
}
}
}
&_second-line {

View File

@ -1032,6 +1032,10 @@ $media-types: (video, bangumi, cheese, live, manga, movie, ogv, article);
}
}
}
.border-bottom-line {
@include background-color("6");
}
}
.up-info {
.u-face .u-face__avatar {
@ -1054,3 +1058,11 @@ $media-types: (video, bangumi, cheese, live, manga, movie, ogv, article);
}
}
}
.user-card {
.info p {
@include color("e");
}
.gray-text {
@include color("a");
}
}

View File

@ -47,9 +47,23 @@ $prefix: "simplifyLiveroom-switch";
display: flex !important;
padding: 10px 24px 10px 12px !important;
height: auto !important;
.blive-avatar-icons,
.face-pendants {
display: none !important;
}
// .blive-avatar-icons {
// right: -2px !important;
// .blive-avatar-icon {
// width: 12px !important;
// height: 12px !important;
// border-width: 1px !important;
// }
// }
.blive-avatar-pendant {
width: 48px !important;
height: 48px !important;
}
.blive-avatar-face,
.blive-avatar,
.avatar {

View File

@ -112,7 +112,7 @@ export const builtInActions: Record<string, KeyBindingAction> = {
if (lodash.isNil(result)) {
return result
}
if (result) {
if (playerAgent.isMute()) {
showTip('已静音', 'mdi-volume-off')
} else {
showTip('已取消静音', 'mdi-volume-high')

View File

@ -20,6 +20,8 @@
<script lang="ts">
import { videoChange } from '@/core/observer'
import { select } from '@/core/spin-query'
import { matchUrlPattern } from '@/core/utils'
import { bangumiUrls } from '@/core/utils/urls'
import { VIcon } from '@/ui'
enum CopyIdType {
@ -30,6 +32,25 @@ const copyIds = [
CopyIdType.Aid,
CopyIdType.Bvid,
]
type LinkProvider = (context: { id: string, url: string, query: string }) => string
const linkProviders: LinkProvider[] = [
// , festival
({ id, query }) => {
if (copyIds.some(copyId => query.includes(`${copyId}=`))) {
return `https://www.bilibili.com/video/${id}`
}
return null
},
//
({ id }) => {
if (bangumiUrls.some(u => matchUrlPattern(u))) {
return `https://www.bilibili.com/video/${id}`
}
return null
},
//
({ id, url, query }) => url.replace(/\/[^\/]+$/, `/${id}`) + query,
]
export default Vue.extend({
components: { VIcon },
data() {
@ -51,23 +72,16 @@ export default Vue.extend({
})
},
methods: {
getParamCopyLink(data: CopyIdType) {
const query = window.location.search
if (copyIds.some(id => query.includes(`${id}=`))) {
return `https://www.bilibili.com/video/${this[data]}`
}
return null
},
getTailingCopyLink(data: CopyIdType) {
const query = window.location.search
const url = document.URL.replace(query, '')
return url.replace(/\/[^\/]+$/, `/${this[data]}`) + query
},
async copyLink(data: CopyIdType) {
if (this[`${data}Copied`]) {
return
}
const link = this.getParamCopyLink(data) ?? this.getTailingCopyLink(data)
const context = {
query: location.search,
url: location.origin + location.pathname,
id: this[data],
}
const link = linkProviders.map(p => p(context)).filter(it => it !== null)[0]
await navigator.clipboard.writeText(link)
this[`${data}Copied`] = true
setTimeout(() => (this[`${data}Copied`] = false), 1000)

View File

@ -14,105 +14,106 @@
<VIcon icon="mdi-close" :size="20" />
</VButton>
</div>
<div
v-if="selectedInput"
class="download-video-config-item"
>
<div class="download-video-config-title">
输入源:
</div>
<VDropdown
v-model="selectedInput"
:items="inputs"
/>
</div>
<div
v-if="inputs.length === 0"
class="download-video-config-item error"
>
没有匹配的输入源, 请确保安装了适合此页面的插件.
</div>
<component
:is="selectedInput.component"
v-if="selectedInput && selectedInput.component"
ref="inputOptions"
/>
<div
v-if="selectedApi"
class="download-video-config-item"
>
<div class="download-video-config-title">
格式:
</div>
<VDropdown
v-model="selectedApi"
:items="apis"
/>
</div>
<div
v-if="selectedApi && selectedApi.description"
class="download-video-config-description"
v-html="selectedApi.description"
>
</div>
<div
v-if="selectedQuality"
class="download-video-config-item"
>
<div class="download-video-config-title">
清晰度:
</div>
<VDropdown
v-model="selectedQuality"
:items="filteredQualities"
@change="saveSelectedQuality()"
/>
</div>
<template v-if="!testData.multiple && selectedQuality">
<div class="download-video-panel-content">
<div
v-if="testData.videoInfo"
class="download-video-config-description"
v-if="selectedInput"
class="download-video-config-item"
>
预计大小: {{ formatFileSize(testData.videoInfo.totalSize) }}
<div class="download-video-config-title">
输入源:
</div>
<VDropdown
v-model="selectedInput"
:items="inputs"
/>
</div>
<div
v-if="testData.videoInfo === null"
v-if="inputs.length === 0"
class="download-video-config-item error"
>
没有匹配的输入源, 请确保安装了适合此页面的插件.
</div>
<component
:is="selectedInput.component"
v-if="selectedInput && selectedInput.component"
ref="inputOptions"
/>
<div
v-if="selectedApi"
class="download-video-config-item"
>
<div class="download-video-config-title">
格式:
</div>
<VDropdown
v-model="selectedApi"
:items="apis"
/>
</div>
<div
v-if="selectedApi && selectedApi.description"
class="download-video-config-description"
v-html="selectedApi.description"
>
</div>
<div
v-if="selectedQuality"
class="download-video-config-item"
>
<div class="download-video-config-title">
清晰度:
</div>
<VDropdown
v-model="selectedQuality"
:items="filteredQualities"
@change="saveSelectedQuality()"
/>
</div>
<template v-if="!testData.multiple && selectedQuality">
<div
v-if="testData.videoInfo"
class="download-video-config-description"
>
预计大小: {{ formatFileSize(testData.videoInfo.totalSize) }}
</div>
<div
v-if="testData.videoInfo === null"
class="download-video-config-description"
>
正在计算大小
</div>
</template>
<component
:is="a.component"
v-for="a of assetsWithOptions"
:key="a.name"
ref="assetsOptions"
:name="a.name"
/>
<div
v-if="selectedOutput"
class="download-video-config-item"
>
<div class="download-video-config-title">
输出方式:
</div>
<VDropdown
v-model="selectedOutput"
:items="outputs"
/>
</div>
<div
v-if="selectedOutput && selectedOutput.description"
class="download-video-config-description"
>
正在计算大小
{{ selectedOutput.description }}
</div>
</template>
<component
:is="a.component"
v-for="a of assetsWithOptions"
:key="a.name"
ref="assetsOptions"
:name="a.name"
/>
<div
v-if="selectedOutput"
class="download-video-config-item"
>
<div class="download-video-config-title">
输出方式:
</div>
<VDropdown
v-model="selectedOutput"
:items="outputs"
<component
:is="selectedOutput.component"
v-if="selectedOutput && selectedOutput.component"
ref="outputOptions"
/>
</div>
<div
v-if="selectedOutput && selectedOutput.description"
class="download-video-config-description"
>
{{ selectedOutput.description }}
</div>
<component
:is="selectedOutput.component"
v-if="selectedOutput && selectedOutput.component"
ref="outputOptions"
/>
<div class="download-video-panel-footer">
<VButton
class="run-download"
@ -382,48 +383,31 @@ export default Vue.extend({
@import "common";
.download-video-panel {
@include no-scrollbar();
@include card();
font-size: 12px;
padding: 6px;
top: 100px;
left: 50%;
transform: translateX(-50%) scale(0.95);
transition: .2s ease-out;
z-index: 1000;
width: 320px;
max-height: calc(100vh - 200px);
height: calc(100vh - 200px);
display: flex;
flex-direction: column;
align-items: flex-start;
@include card();
&.open {
transform: translateX(-50%);
}
> * {
margin-top: 12px;
padding: 0 12px;
}
> :first-child {
margin-top: 0;
padding-top: 12px;
padding-bottom: 6px;
}
> :last-child {
margin-top: 6px;
padding-top: 6px;
padding-bottom: 12px;
}
.be-textbox,
.be-textarea {
flex-grow: 1;
}
&-header {
@include h-center();
align-self: stretch;
background-color: inherit;
position: sticky;
top: 0;
z-index: 1;
border-bottom: 1px solid #8882;
padding: 6px 0;
margin: 0 6px;
.title {
font-size: 16px;
@ -435,6 +419,16 @@ export default Vue.extend({
padding: 4px;
}
}
&-content {
@include no-scrollbar();
@include v-stretch();
flex: 1 0 0;
padding: 12px 6px;
align-items: flex-start;
> :not(:first-child) {
margin-top: 12px;
}
}
.download-video-config-item {
@include h-center();
.download-video-config-title {
@ -452,13 +446,11 @@ export default Vue.extend({
margin-top: 4px;
}
&-footer {
position: sticky;
bottom: 0;
z-index: 1;
background-color: inherit;
align-self: stretch;
justify-content: center;
@include h-center();
border-top: 1px solid #8882;
padding: 6px 0;
margin: 0 6px;
justify-content: center;
}
.run-download {
font-size: 13px;

View File

@ -181,10 +181,10 @@ const downloadDash = async (
videoCodec: codec,
})
const qualities = (data.accept_quality as number[])
.filter(qn => (
// 去掉当前编码不匹配的 quality
(video as any[]).some(d => d.id === qn && parseVideoCodec(d.codecid) === codec)
))
// .filter(qn => (
// // 去掉当前编码不匹配的 quality
// (video as any[]).some(d => d.id === qn && parseVideoCodec(d.codecid) === codec)
// ))
.map(qn => allQualities.find(q => q.value === qn))
.filter(q => q !== undefined)
const info = new DownloadVideoInfo({
@ -200,19 +200,19 @@ const downloadDash = async (
export const videoDashAvc: DownloadVideoApi = {
name: 'video.dash.avc',
displayName: 'dash (AVC/H.264)',
description: '音画分离的 mp4 格式, 编码为 H.264, 体积较大, 兼容性较好. 下载后可以合并为单个 mp4 文件.',
description: '音画分离的 mp4 格式, 编码为 H.264, 体积较大, 兼容性较好. 下载后可以合并为单个 mp4 文件. 如果视频源没有此编码, 则会自动选择其他同清晰度的编码格式.',
downloadVideoInfo: async input => downloadDash(input, { codec: DashCodec.Avc }),
}
export const videoDashHevc: DownloadVideoApi = {
name: 'video.dash.hevc',
displayName: 'dash (HEVC/H.265)',
description: '音画分离的 mp4 格式, 编码为 H.265, 体积中等, 兼容性较差. 下载后可以合并为单个 mp4 文件.',
description: '音画分离的 mp4 格式, 编码为 H.265, 体积中等, 兼容性较差. 下载后可以合并为单个 mp4 文件. 如果视频源没有此编码, 则会自动选择其他同清晰度的编码格式.',
downloadVideoInfo: async input => downloadDash(input, { codec: DashCodec.Hevc }),
}
export const videoDashAv1: DownloadVideoApi = {
name: 'video.dash.av1',
displayName: 'dash (AV1)',
description: '音画分离的 mp4 格式, 编码为 AV1, 体积较小, 兼容性中等. 下载后可以合并为单个 mp4 文件.',
description: '音画分离的 mp4 格式, 编码为 AV1, 体积较小, 兼容性中等. 下载后可以合并为单个 mp4 文件. 如果视频源没有此编码, 则会自动选择其他同清晰度的编码格式.',
downloadVideoInfo: async input => downloadDash(input, { codec: DashCodec.Av1 }),
}
export const videoAudioDash: DownloadVideoApi = {

View File

@ -39,6 +39,7 @@ export default Vue.extend({
</script>
<style lang="scss">
.single-video-info.download-video-config-section {
position: relative;
height: 125px;
display: flex;
align-items: center;

View File

@ -7,7 +7,7 @@ import { videoUrls } from '@/core/utils/urls'
export const component: ComponentMetadata = {
name: 'legacyAutoPlay',
displayName: '传统连播模式',
description: '模拟传统的多 P 连播策略: 仅连播视频的分 P 和番剧的多集, 最后 1P 放完禁止连播其他推荐视频.',
description: '模拟传统的多 P 连播策略: 仅连播视频的分 P, 最后 1P 放完禁止连播其他推荐视频.',
tags: [componentsTags.video],
urlInclude: videoUrls,
entry: async () => {
@ -19,10 +19,15 @@ export const component: ComponentMetadata = {
],
disable: ['.recommend-list .next-button'],
}
// 最后 1P 时不能开启连播
const disableConditions = [
// 最后 1P 时不能开启连播
// 传统分 P
() => Boolean(dq('.multi-page .list-box li.on:last-child')),
// TODO: 合计列表如何确定是最后 1P?
// 替代分 P 的合集
// & 可分子合集的分 P 合集, 布局长得比下面那个丑一点
() => Boolean(dq('.video-sections-item:last-child .video-episode-card:last-child .video-episode-card__info-playing')),
// 可分子合集的合集
() => Boolean(dq('.video-sections-item:last-child .video-episode-card:last-child .video-episode-card__info-title-playing')),
]
const isChecked = (container: HTMLElement) => Boolean(
container.querySelector('.switch-button.on') || container.matches(':checked'),

View File

@ -1,3 +1,4 @@
.bpx-player-video-wrap::after,
.bilibili-player-video::after {
position: absolute;
content: "";
@ -11,6 +12,11 @@
pointer-events: none;
z-index: 10;
}
.bpx-player-container.bpx-state-paused {
.bpx-player-video-wrap::after {
display: block;
}
}
.bilibili-player-area.video-control-show.video-state-pause {
.bilibili-player-video::after {
display: block;

View File

@ -1,6 +1,5 @@
import { ComponentMetadata } from '@/components/types'
import { videoChange } from '@/core/observer'
import { select } from '@/core/spin-query'
import { videoChange, VideoChangeCallback } from '@/core/observer'
import { createHook, isBwpVideo } from '@/core/utils'
import { playerUrls } from '@/core/utils/urls'
@ -12,8 +11,7 @@ const entry = async () => {
removeCover()
return true
})
const showCover = async () => {
const aid = await select(() => unsafeWindow.aid)
const showCover: VideoChangeCallback = async ({ aid }) => {
if (!aid) {
console.warn('[播放前显示封面] 未找到av号')
return
@ -25,9 +23,6 @@ const entry = async () => {
const { VideoInfo } = await import('@/components/video/video-info')
const info = new VideoInfo(aid)
await info.fetchInfo()
// if (!(dq('video') as HTMLVideoElement).paused) {
// return
// }
document.body.style.setProperty('--cover-url', `url('${info.coverUrl}')`)
}
videoChange(showCover)

View File

@ -1,3 +1,4 @@
import { cdnRoots } from '@/core/cdn-types'
import { ComponentMetadata, componentsTags } from '../types'
export const component: ComponentMetadata = {
@ -24,15 +25,17 @@ export const component: ComponentMetadata = {
const { monkey } = await import('@/core/ajax')
const { meta } = await import('@/core/meta')
const { Toast } = await import('@/core/toast')
const { getGeneralSettings } = await import('@/core/settings')
const now = Number(new Date())
const duration = now - options.lastUpdateCheck
if (duration < options.minimumDuration) { // 未到间隔期
return
}
const updateUrl = GM_info.scriptUpdateURL
if (!updateUrl) { // 本地调试版没有 updateUrl
// 本地调试版不检查
if (!GM_info.scriptUpdateURL) {
return
}
const updateUrl = `${cdnRoots[getGeneralSettings().cdnRoot](meta.compilationInfo.branch)}dist/${meta.originalFilename}`
const scriptText: string = await monkey({ url: updateUrl, responseType: 'text' })
options.lastUpdateCheck = Number(new Date())
const versionMatch = scriptText.match(/^\/\/ @version\s*([\d\.]+)$/m)

View File

@ -39,11 +39,6 @@ export const component: ComponentMetadata = {
displayName: '主题颜色',
color: true,
},
// accentColor: {
// defaultValue: '#D55480',
// displayName: '辅助颜色',
// color: true,
// },
scriptLoadingMode: {
defaultValue: LoadingMode.Delay,
displayName: '功能加载模式',

View File

@ -1,4 +1,4 @@
import { VueConstructor } from 'vue'
import { Component, VueConstructor } from 'vue'
export type Executable<ReturnType = void> = () => ReturnType | Promise<ReturnType>
export type ExecutableWithParameter<Parameters extends any[] = never[], ReturnType = void> = (
@ -7,7 +7,11 @@ export type ExecutableWithParameter<Parameters extends any[] = never[], ReturnTy
export type TestPattern = (string | RegExp)[]
export type ArrayContent<T> = T extends Array<infer R> ? R : T
export type VueModule = VueConstructor | { default: VueConstructor }
export type VueModule =
Component
| { default: Component }
| VueConstructor
| { default: VueConstructor }
export type I18nDescription = string | { 'zh-CN': string; [key: string]: string }
export type WithName = {
name: string

View File

@ -24,7 +24,7 @@ export const meta = {
if (branch === branches.stable) {
return 'bilibili-evolved.user.js'
}
return `bilibili-evolved.${branch}.user.js`
return `bilibili-evolved.${branches.preview}.user.js`
},
/** 检查更新的链接 */
get updateURL(): string {

View File

@ -245,7 +245,7 @@ const selectCid = lodash.once(() => select(() => {
}))
let cidHooked = false
type VideoChangeCallback = (id: { aid: string; cid: string }) => void
export type VideoChangeCallback = (id: { aid: string; cid: string }) => void
/**
* , resolve
* @param callback

View File

@ -1,5 +1,5 @@
import Color from 'color'
import { settings, addComponentListener } from '../settings'
import { getGeneralSettings, addComponentListener } from '../settings'
import { makeImageFilter } from './image-filter'
import { TextColor } from '../text-color'
@ -16,7 +16,23 @@ export const initColors = () => {
}
`.trim()
}, 100)
addComponentListener('settingsPanel.themeColor', (value: string) => {
const handleTextColorChange = (value: TextColor) => {
let textColor: 'black' | 'white'
if (value === TextColor.Auto) {
textColor = Color(getGeneralSettings().themeColor).isLight() ? 'black' : 'white'
} else {
textColor = value === TextColor.Black ? 'black' : 'white'
}
set('--text-color', textColor)
// for v1.x
set('--foreground-color', textColor)
set('--foreground-color-d', Color(textColor, 'keyword').alpha(14 / 16).rgb().string())
set('--foreground-color-b', Color(textColor, 'keyword').alpha(12 / 16).rgb().string())
set('--brightness', `${textColor === 'black' ? '100' : '0'}%`)
set('--invert-filter', textColor === 'black' ? 'invert(0)' : 'invert(1)')
update()
}
const handleThemeColorChange = (value: string) => {
set('--theme-color', value)
for (let delta = 10; delta <= 90; delta += 10) {
const color = Color(value, 'hex')
@ -33,27 +49,10 @@ export const initColors = () => {
g: 160,
b: 213,
}, 'rgb'), Color(value, 'hex')))
handleTextColorChange(getGeneralSettings().textColor)
update()
}, true)
addComponentListener('settingsPanel.accentColor', (value: string) => {
set('--accent-color', value)
update()
}, true)
addComponentListener('settingsPanel.textColor', (value: TextColor) => {
let textColor: 'black' | 'white'
if (value === TextColor.Auto) {
textColor = Color(settings.themeColor).isLight() ? 'black' : 'white'
} else {
textColor = value === TextColor.Black ? 'black' : 'white'
}
set('--text-color', textColor)
// for v1.x
set('--foreground-color', textColor)
set('--foreground-color-d', Color(textColor, 'keyword').alpha(14 / 16).rgb().string())
set('--foreground-color-b', Color(textColor, 'keyword').alpha(12 / 16).rgb().string())
set('--brightness', `${textColor === 'black' ? '100' : '0'}%`)
set('--invert-filter', textColor === 'black' ? 'invert(0)' : 'invert(1)')
update()
}, true)
}
addComponentListener('settingsPanel.themeColor', handleThemeColorChange, true)
addComponentListener('settingsPanel.textColor', handleTextColorChange, true)
return colorStyle
}

View File

@ -73,7 +73,7 @@ export const isBwpVideo = async () => {
return false
}
// eslint-disable-next-line no-underscore-dangle
return unsafeWindow.__ENABLE_WASM_PLAYER__ as boolean || Boolean(dq('bwp-video'))
return unsafeWindow.__ENABLE_WASM_PLAYER__ as boolean || Boolean(dq('#bilibili-player bwp-video'))
}
/**
*
@ -98,10 +98,22 @@ export const matchUrlPattern = (pattern: string | RegExp) => (
* @param module Vue组件模块对象
* @param target ,
*/
export const mountVueComponent = <T>(module: VueModule, target?: Element | string) => {
const instance = new Vue('default' in module ? module.default : module)
// const instance = new Vue({ render: h => h('default' in module ? module.default : module) })
return instance.$mount(target) as Vue & T
export const mountVueComponent = <T>(
module: VueModule,
target?: Element | string,
): Vue & T => {
const obj = 'default' in module ? module.default : module
const getInstance = (o: any) => {
if (o instanceof Function) {
// eslint-disable-next-line new-cap
return new o()
}
if (o.functional) {
return new (Vue.extend(o))()
}
return new Vue(o)
}
return getInstance(obj).$mount(target) as Vue & T
}
/** 是否处于其他网站的内嵌播放器中 */
export const isEmbeddedPlayer = () => window.location.host === 'player.bilibili.com' || document.URL.startsWith('https://www.bilibili.com/html/player.html')
@ -472,6 +484,6 @@ export const disableWindowScroll = async (action?: () => unknown | Promise<unkno
*/
export const getNumberValidator = (clampLower = -Infinity, clampUpper = Infinity) => (
(value: number, oldValue: number) => (
lodash.isNumber(value) ? lodash.clamp(value, clampLower, clampUpper) : oldValue
lodash.isNumber(Number(value)) ? lodash.clamp(value, clampLower, clampUpper) : oldValue
)
)

View File

@ -12,6 +12,7 @@ export const formatTitle = (
const now = new Date()
const data: StringMap = {
title: document.title
.replace(/-[^-]+-[^-]+在线观看-bilibili-哔哩哔哩$/, '')
.replace(/([^]+?)_.+?_bilibili_哔哩哔哩$/, '')
.replace(/_哔哩哔哩_bilibili$/, '')
.replace(/ - 哔哩哔哩$/, '')