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: '历史', content: '历史',
href, href,
touch: true,
active: document.URL.replace(/\?.*$/, '') === href, active: document.URL.replace(/\?.*$/, '') === href,
loginRequired: true, loginRequired: true,

View File

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

View File

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

View File

@ -112,6 +112,12 @@
@include color("e"); @include color("e");
} }
} }
&-title {
@include color("e");
&:hover {
@include theme-color();
}
}
} }
} }
&_second-line { &_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 { .up-info {
.u-face .u-face__avatar { .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; display: flex !important;
padding: 10px 24px 10px 12px !important; padding: 10px 24px 10px 12px !important;
height: auto !important; height: auto !important;
.blive-avatar-icons,
.face-pendants { .face-pendants {
display: none !important; 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-face,
.blive-avatar, .blive-avatar,
.avatar { .avatar {

View File

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

View File

@ -20,6 +20,8 @@
<script lang="ts"> <script lang="ts">
import { videoChange } from '@/core/observer' import { videoChange } from '@/core/observer'
import { select } from '@/core/spin-query' import { select } from '@/core/spin-query'
import { matchUrlPattern } from '@/core/utils'
import { bangumiUrls } from '@/core/utils/urls'
import { VIcon } from '@/ui' import { VIcon } from '@/ui'
enum CopyIdType { enum CopyIdType {
@ -30,6 +32,25 @@ const copyIds = [
CopyIdType.Aid, CopyIdType.Aid,
CopyIdType.Bvid, 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({ export default Vue.extend({
components: { VIcon }, components: { VIcon },
data() { data() {
@ -51,23 +72,16 @@ export default Vue.extend({
}) })
}, },
methods: { 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) { async copyLink(data: CopyIdType) {
if (this[`${data}Copied`]) { if (this[`${data}Copied`]) {
return 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) await navigator.clipboard.writeText(link)
this[`${data}Copied`] = true this[`${data}Copied`] = true
setTimeout(() => (this[`${data}Copied`] = false), 1000) setTimeout(() => (this[`${data}Copied`] = false), 1000)

View File

@ -14,105 +14,106 @@
<VIcon icon="mdi-close" :size="20" /> <VIcon icon="mdi-close" :size="20" />
</VButton> </VButton>
</div> </div>
<div <div class="download-video-panel-content">
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 <div
v-if="testData.videoInfo" v-if="selectedInput"
class="download-video-config-description" class="download-video-config-item"
> >
预计大小: {{ formatFileSize(testData.videoInfo.totalSize) }} <div class="download-video-config-title">
输入源:
</div>
<VDropdown
v-model="selectedInput"
:items="inputs"
/>
</div> </div>
<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" class="download-video-config-description"
> >
正在计算大小 {{ selectedOutput.description }}
</div> </div>
</template> <component
<component :is="selectedOutput.component"
:is="a.component" v-if="selectedOutput && selectedOutput.component"
v-for="a of assetsWithOptions" ref="outputOptions"
: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>
<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"> <div class="download-video-panel-footer">
<VButton <VButton
class="run-download" class="run-download"
@ -382,48 +383,31 @@ export default Vue.extend({
@import "common"; @import "common";
.download-video-panel { .download-video-panel {
@include no-scrollbar(); @include card();
font-size: 12px; font-size: 12px;
padding: 6px;
top: 100px; top: 100px;
left: 50%; left: 50%;
transform: translateX(-50%) scale(0.95); transform: translateX(-50%) scale(0.95);
transition: .2s ease-out; transition: .2s ease-out;
z-index: 1000; z-index: 1000;
width: 320px; width: 320px;
max-height: calc(100vh - 200px); height: calc(100vh - 200px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start;
@include card();
&.open { &.open {
transform: translateX(-50%); 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-textbox,
.be-textarea { .be-textarea {
flex-grow: 1; flex-grow: 1;
} }
&-header { &-header {
@include h-center(); @include h-center();
align-self: stretch; border-bottom: 1px solid #8882;
background-color: inherit; padding: 6px 0;
position: sticky; margin: 0 6px;
top: 0;
z-index: 1;
.title { .title {
font-size: 16px; font-size: 16px;
@ -435,6 +419,16 @@ export default Vue.extend({
padding: 4px; 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 { .download-video-config-item {
@include h-center(); @include h-center();
.download-video-config-title { .download-video-config-title {
@ -452,13 +446,11 @@ export default Vue.extend({
margin-top: 4px; margin-top: 4px;
} }
&-footer { &-footer {
position: sticky;
bottom: 0;
z-index: 1;
background-color: inherit;
align-self: stretch;
justify-content: center;
@include h-center(); @include h-center();
border-top: 1px solid #8882;
padding: 6px 0;
margin: 0 6px;
justify-content: center;
} }
.run-download { .run-download {
font-size: 13px; font-size: 13px;

View File

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

View File

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

View File

@ -7,7 +7,7 @@ import { videoUrls } from '@/core/utils/urls'
export const component: ComponentMetadata = { export const component: ComponentMetadata = {
name: 'legacyAutoPlay', name: 'legacyAutoPlay',
displayName: '传统连播模式', displayName: '传统连播模式',
description: '模拟传统的多 P 连播策略: 仅连播视频的分 P 和番剧的多集, 最后 1P 放完禁止连播其他推荐视频.', description: '模拟传统的多 P 连播策略: 仅连播视频的分 P, 最后 1P 放完禁止连播其他推荐视频.',
tags: [componentsTags.video], tags: [componentsTags.video],
urlInclude: videoUrls, urlInclude: videoUrls,
entry: async () => { entry: async () => {
@ -19,10 +19,15 @@ export const component: ComponentMetadata = {
], ],
disable: ['.recommend-list .next-button'], disable: ['.recommend-list .next-button'],
} }
// 最后 1P 时不能开启连播
const disableConditions = [ const disableConditions = [
// 最后 1P 时不能开启连播 // 传统分 P
() => Boolean(dq('.multi-page .list-box li.on:last-child')), () => 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( const isChecked = (container: HTMLElement) => Boolean(
container.querySelector('.switch-button.on') || container.matches(':checked'), container.querySelector('.switch-button.on') || container.matches(':checked'),

View File

@ -1,3 +1,4 @@
.bpx-player-video-wrap::after,
.bilibili-player-video::after { .bilibili-player-video::after {
position: absolute; position: absolute;
content: ""; content: "";
@ -11,6 +12,11 @@
pointer-events: none; pointer-events: none;
z-index: 10; 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-area.video-control-show.video-state-pause {
.bilibili-player-video::after { .bilibili-player-video::after {
display: block; display: block;

View File

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

View File

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

View File

@ -39,11 +39,6 @@ export const component: ComponentMetadata = {
displayName: '主题颜色', displayName: '主题颜色',
color: true, color: true,
}, },
// accentColor: {
// defaultValue: '#D55480',
// displayName: '辅助颜色',
// color: true,
// },
scriptLoadingMode: { scriptLoadingMode: {
defaultValue: LoadingMode.Delay, defaultValue: LoadingMode.Delay,
displayName: '功能加载模式', 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 Executable<ReturnType = void> = () => ReturnType | Promise<ReturnType>
export type ExecutableWithParameter<Parameters extends any[] = never[], ReturnType = void> = ( 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 TestPattern = (string | RegExp)[]
export type ArrayContent<T> = T extends Array<infer R> ? R : T 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 I18nDescription = string | { 'zh-CN': string; [key: string]: string }
export type WithName = { export type WithName = {
name: string name: string

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import Color from 'color' import Color from 'color'
import { settings, addComponentListener } from '../settings' import { getGeneralSettings, addComponentListener } from '../settings'
import { makeImageFilter } from './image-filter' import { makeImageFilter } from './image-filter'
import { TextColor } from '../text-color' import { TextColor } from '../text-color'
@ -16,7 +16,23 @@ export const initColors = () => {
} }
`.trim() `.trim()
}, 100) }, 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) set('--theme-color', value)
for (let delta = 10; delta <= 90; delta += 10) { for (let delta = 10; delta <= 90; delta += 10) {
const color = Color(value, 'hex') const color = Color(value, 'hex')
@ -33,27 +49,10 @@ export const initColors = () => {
g: 160, g: 160,
b: 213, b: 213,
}, 'rgb'), Color(value, 'hex'))) }, 'rgb'), Color(value, 'hex')))
handleTextColorChange(getGeneralSettings().textColor)
update() update()
}, true) }
addComponentListener('settingsPanel.accentColor', (value: string) => { addComponentListener('settingsPanel.themeColor', handleThemeColorChange, true)
set('--accent-color', value) addComponentListener('settingsPanel.textColor', handleTextColorChange, true)
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)
return colorStyle return colorStyle
} }

View File

@ -73,7 +73,7 @@ export const isBwpVideo = async () => {
return false return false
} }
// eslint-disable-next-line no-underscore-dangle // 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 module Vue组件模块对象
* @param target , * @param target ,
*/ */
export const mountVueComponent = <T>(module: VueModule, target?: Element | string) => { export const mountVueComponent = <T>(
const instance = new Vue('default' in module ? module.default : module) module: VueModule,
// const instance = new Vue({ render: h => h('default' in module ? module.default : module) }) target?: Element | string,
return instance.$mount(target) as Vue & T ): 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') 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) => ( export const getNumberValidator = (clampLower = -Infinity, clampUpper = Infinity) => (
(value: number, oldValue: number) => ( (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 now = new Date()
const data: StringMap = { const data: StringMap = {
title: document.title title: document.title
.replace(/-[^-]+-[^-]+在线观看-bilibili-哔哩哔哩$/, '')
.replace(/([^]+?)_.+?_bilibili_哔哩哔哩$/, '') .replace(/([^]+?)_.+?_bilibili_哔哩哔哩$/, '')
.replace(/_哔哩哔哩_bilibili$/, '') .replace(/_哔哩哔哩_bilibili$/, '')
.replace(/ - 哔哩哔哩$/, '') .replace(/ - 哔哩哔哩$/, '')