mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge branch 'preview-fixes'
This commit is contained in:
commit
0c22c3a952
@ -7,6 +7,7 @@ export const history: CustomNavbarItemInit = {
|
||||
content: '历史',
|
||||
|
||||
href,
|
||||
touch: true,
|
||||
active: document.URL.replace(/\?.*$/, '') === href,
|
||||
loginRequired: true,
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -239,6 +239,7 @@
|
||||
}
|
||||
}
|
||||
.s_tag .tag-area {
|
||||
.tag,
|
||||
.tag-item {
|
||||
@include background-color("4");
|
||||
@include border-color();
|
||||
|
||||
@ -112,6 +112,12 @@
|
||||
@include color("e");
|
||||
}
|
||||
}
|
||||
&-title {
|
||||
@include color("e");
|
||||
&:hover {
|
||||
@include theme-color();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&_second-line {
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -39,11 +39,6 @@ export const component: ComponentMetadata = {
|
||||
displayName: '主题颜色',
|
||||
color: true,
|
||||
},
|
||||
// accentColor: {
|
||||
// defaultValue: '#D55480',
|
||||
// displayName: '辅助颜色',
|
||||
// color: true,
|
||||
// },
|
||||
scriptLoadingMode: {
|
||||
defaultValue: LoadingMode.Delay,
|
||||
displayName: '功能加载模式',
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 回调函数
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@ -12,6 +12,7 @@ export const formatTitle = (
|
||||
const now = new Date()
|
||||
const data: StringMap = {
|
||||
title: document.title
|
||||
.replace(/-[^-]+-[^-]+在线观看-bilibili-哔哩哔哩$/, '')
|
||||
.replace(/:([^:]+?)_.+?_bilibili_哔哩哔哩$/, '')
|
||||
.replace(/_哔哩哔哩_bilibili$/, '')
|
||||
.replace(/ - 哔哩哔哩$/, '')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user