Merge tag 'v2.8.9-preview' into preview-fixes

This commit is contained in:
the1812 2024-04-23 22:17:51 +08:00
commit 092cdbf866
15 changed files with 278 additions and 53 deletions

View File

@ -0,0 +1 @@
将动态中左右切换式的图片改回传统的平铺展示. (在动态详情中可能稍有延迟)

View File

@ -0,0 +1,45 @@
import { defineComponentMetadata } from '@/components/define'
import { forEachFeedsCard, getVueData } from '@/components/feeds/api'
import { feedsUrls } from '@/core/utils/urls'
export const component = defineComponentMetadata({
name: 'legacyFeedsImageViewer',
displayName: '动态图片平铺展示',
tags: [componentsTags.feeds],
urlInclude: feedsUrls,
entry: () => {
forEachFeedsCard({
added: card => {
const vueData = getVueData(card.element)
// 普通动态
const path = 'data.modules.module_dynamic.major.opus.style'
const imageViewerStyle: number | null = lodash.get(vueData, path, null)
if (imageViewerStyle === 1) {
lodash.set(vueData, path, undefined)
return
}
// 动态详情
const modules = vueData?.data?.modules
if (Array.isArray(modules)) {
const moduleTop = modules.find(m => m.module_type === 'MODULE_TYPE_TOP')
const moduleContent = modules.find(m => m.module_type === 'MODULE_TYPE_CONTENT')
const album = moduleTop?.module_top?.display?.album
const paragraphs: any[] = moduleContent?.module_content?.paragraphs
if (album && paragraphs) {
modules.splice(modules.indexOf(moduleTop), 1)
paragraphs.push({
align: 0,
para_type: 2,
pic: {
pics: album.pics,
style: 1,
},
})
}
}
},
})
},
})

View File

@ -11,7 +11,7 @@
<script lang="ts">
import { TabControl, VIcon } from '@/ui'
import { FeedsCardType, feedsCardTypes } from '@/components/feeds/api'
import { getNotifyCount } from '@/components/feeds/notify'
import { getNotifyCountByType } from '@/components/feeds/notify'
import { popperMixin } from '../mixins'
import { tabs } from './tabs/tabs'
@ -49,7 +49,7 @@ export default Vue.extend({
if (!feedsCardType.apiType) {
return
}
const count = await getNotifyCount(feedsCardType.apiType)
const count = await getNotifyCountByType(feedsCardType.apiType)
tab.count = count
console.log(tab)
})

View File

@ -2,7 +2,7 @@
<span
title="稍后再看"
class="watchlater be-outer-watchlater video-toolbar-left-item"
:class="{ on }"
:class="{ on, ...displayModeClass }"
@click="toggle()"
>
<VIcon class="icon" :size="28" icon="mdi-timetable"></VIcon>
@ -14,14 +14,18 @@
<script lang="ts">
import { VIcon } from '@/ui'
import { watchlaterList, toggleWatchlater } from '@/components/video/watchlater'
import { DisplayMode, Options } from './options'
import { addComponentListener, getComponentSettings } from '@/core/settings'
export default Vue.extend({
components: {
VIcon,
},
data() {
const { displayMode } = getComponentSettings<Options>('outerWatchlater').options
return {
watchlaterList,
displayMode,
aid: unsafeWindow.aid,
tipText: '',
tipShowing: false,
@ -33,6 +37,17 @@ export default Vue.extend({
console.log(this.watchlaterList, this.aid, this.watchlaterList.includes(parseInt(this.aid)))
return this.watchlaterList.includes(parseInt(this.aid))
},
displayModeClass() {
return {
'icon-only': this.displayMode === DisplayMode.Icon,
'icon-and-text': this.displayMode === DisplayMode.IconAndText,
}
},
},
created() {
addComponentListener('outerWatchlater.displayMode', (value: DisplayMode) => {
this.displayMode = value
})
},
methods: {
showTip(text: string) {
@ -62,12 +77,22 @@ export default Vue.extend({
margin-right: 28px !important;
position: relative;
width: auto !important;
@media screen and (max-width: 1340px), (max-height: 750px) {
@mixin icon-only {
margin-right: max(calc(min(11vw, 11vh) - 117.2px), 6px) !important;
.text {
display: none;
}
}
&.icon-only {
@include icon-only();
}
&:not(.icon-and-text) {
@media screen and (max-width: 1340px), (max-height: 750px) {
@include icon-only();
}
}
.tip {
position: absolute;
top: calc(100% + 8px);

View File

@ -1,22 +1,10 @@
import {
defineComponentMetadata,
defineOptionsMetadata,
OptionsOfMetadata,
} from '@/components/define'
import { defineComponentMetadata } from '@/components/define'
import { ComponentEntry } from '@/components/types'
import { getUID, matchUrlPattern, mountVueComponent } from '@/core/utils'
import { videoUrls, watchlaterUrls } from '@/core/utils/urls'
import { KeyBindingAction } from '../../utils/keymap/bindings'
import { addVideoActionButton } from '@/components/video/video-actions'
const options = defineOptionsMetadata({
showInWatchlaterPages: {
defaultValue: false,
displayName: '在稍后再看页面中仍然显示',
},
})
type Options = OptionsOfMetadata<typeof options>
import { Options, options } from './options'
const entry: ComponentEntry<Options> = async ({ settings }) => {
if (watchlaterUrls.some(matchUrlPattern) && !settings.options.showInWatchlaterPages) {

View File

@ -0,0 +1,20 @@
import { defineOptionsMetadata, OptionsOfMetadata } from '@/components/define'
export enum DisplayMode {
Auto = '自动',
Icon = '图标',
IconAndText = '图标 + 文字',
}
export const options = defineOptionsMetadata({
showInWatchlaterPages: {
defaultValue: false,
displayName: '在稍后再看页面中仍然显示',
},
displayMode: {
defaultValue: DisplayMode.Auto,
displayName: '显示方式',
dropdownEnum: DisplayMode,
},
})
export type Options = OptionsOfMetadata<typeof options>

View File

@ -2,7 +2,7 @@
<span
class="quick-favorite be-quick-favorite video-toolbar-left-item"
title="快速收藏"
:class="{ on: isFavorite }"
:class="{ on: isFavorite, ...displayModeClass }"
@click.left.self="toggle()"
@click.right.prevent.self="listShowing = !listShowing"
>
@ -33,12 +33,13 @@
</span>
</template>
<script lang="ts">
import { getComponentSettings } from '@/core/settings'
import { addComponentListener, getComponentSettings } from '@/core/settings'
import { getJsonWithCredentials, postTextWithCredentials } from '@/core/ajax'
import { getUID, getCsrf } from '@/core/utils'
import { logError } from '@/core/utils/log'
import { Toast } from '@/core/toast'
import { VDropdown } from '@/ui'
import { DisplayMode, Options } from './options'
const { options } = getComponentSettings('quickFavorite')
export default Vue.extend({
@ -46,6 +47,7 @@ export default Vue.extend({
VDropdown,
},
data() {
const { displayMode } = getComponentSettings<Options>('outerWatchlater').options
return {
aid: unsafeWindow.aid,
favoriteTitle: '',
@ -56,8 +58,17 @@ export default Vue.extend({
lists: [],
selectedFavorite: '<未选择>',
listShowing: false,
displayMode,
}
},
computed: {
displayModeClass() {
return {
'icon-only': this.displayMode === DisplayMode.Icon,
'icon-and-text': this.displayMode === DisplayMode.IconAndText,
}
},
},
watch: {
selectedFavorite(value: string) {
if (this.lists.length === 0) {
@ -101,6 +112,9 @@ export default Vue.extend({
},
created() {
this.syncFavoriteState()
addComponentListener('quickFavorite.displayMode', (value: DisplayMode) => {
this.displayMode = value
})
},
methods: {
async syncFavoriteState() {
@ -192,12 +206,22 @@ export default Vue.extend({
.text {
display: inline;
}
@media screen and (max-width: 1340px), (max-height: 750px) {
@mixin icon-only {
margin-right: max(calc(min(11vw, 11vh) - 117.2px), 6px) !important;
.text {
display: none;
}
}
&.icon-only {
@include icon-only();
}
&:not(.icon-and-text) {
@media screen and (max-width: 1340px), (max-height: 750px) {
@include icon-only();
}
}
&-icon {
font-family: 'quick-favorite' !important;
font-size: 28px;

View File

@ -1,28 +1,11 @@
import {
defineComponentMetadata,
defineOptionsMetadata,
OptionsOfMetadata,
} from '@/components/define'
import { defineComponentMetadata } from '@/components/define'
import { ComponentEntry } from '@/components/types'
import { getUID, matchUrlPattern, mountVueComponent } from '@/core/utils'
import { favoriteListUrls, videoUrls } from '@/core/utils/urls'
import { KeyBindingAction } from '../../utils/keymap/bindings'
import { addVideoActionButton } from '@/components/video/video-actions'
import { videoChange } from '@/core/observer'
const options = defineOptionsMetadata({
favoriteFolderID: {
defaultValue: 0,
displayName: '快速收藏夹ID',
hidden: true,
},
showInFavoritePages: {
defaultValue: false,
displayName: '在收藏夹播放页面仍然显示',
},
})
type Options = OptionsOfMetadata<typeof options>
import { options, Options } from './options'
const entry: ComponentEntry<Options> = async ({ settings }) => {
if (favoriteListUrls.some(matchUrlPattern) && !settings.options.showInFavoritePages) {

View File

@ -0,0 +1,25 @@
import { defineOptionsMetadata, OptionsOfMetadata } from '@/components/define'
export enum DisplayMode {
Auto = '自动',
Icon = '图标',
IconAndText = '图标 + 文字',
}
export const options = defineOptionsMetadata({
favoriteFolderID: {
defaultValue: 0,
displayName: '快速收藏夹ID',
hidden: true,
},
showInFavoritePages: {
defaultValue: false,
displayName: '在收藏夹播放页面仍然显示',
},
displayMode: {
defaultValue: DisplayMode.Auto,
displayName: '显示方式',
dropdownEnum: DisplayMode,
},
})
export type Options = OptionsOfMetadata<typeof options>

View File

@ -0,0 +1 @@
移除动态里的商品带货动态 (UP主的推荐 · 来自 XX), 装有 `动态过滤器` 时生效.

View File

@ -0,0 +1,24 @@
import type { PluginMetadata } from '@/plugins/plugin'
export const plugin: PluginMetadata = {
name: 'feedsFilter.pluginBlocks.goods',
displayName: '动态过滤器 - 移除商品带货动态',
async setup() {
const { forEachFeedsCard, getVueData } = await import('@/components/feeds/api')
forEachFeedsCard({
added: card => {
const vueData = getVueData(card.element)
const additionalType: string | null = lodash.get(
vueData,
'data.modules.module_dynamic.additional.type',
null,
)
const isGoods = additionalType === 'ADDITIONAL_TYPE_GOODS'
if (!isGoods) {
return
}
card.element.classList.add('plugin-block')
},
})
},
}

View File

@ -0,0 +1,64 @@
const DB_NAME = 'bilibili-evolved-wasm-output'
const DB_VERSION = 124
export const storeNames = {
cache: 'cache',
} as const
async function database() {
return new Promise((reslove: (db: IDBDatabase) => void, reject) => {
const req = unsafeWindow.indexedDB.open(DB_NAME, DB_VERSION)
req.onerror = reject
req.onupgradeneeded = () => {
const db = req.result
for (const name of db.objectStoreNames) {
db.deleteObjectStore(name)
}
Object.values(storeNames).forEach(v => {
db.createObjectStore(v)
})
}
req.onsuccess = () => reslove(req.result)
})
}
async function objectStore(name: string, mode?: IDBTransactionMode) {
return database().then(
db =>
new Promise((reslove: (db: IDBObjectStore) => void, reject) => {
const tr = db.transaction(name, mode)
reslove(tr.objectStore(name))
tr.onerror = reject
}),
)
}
async function get(store: IDBObjectStore, key: IDBValidKey | IDBKeyRange) {
return new Promise((reslove: (db: any) => void, reject) => {
const res = store.get(key)
res.onerror = reject
res.onsuccess = () => reslove(res.result)
})
}
async function put(store: IDBObjectStore, value: any, key?: IDBValidKey) {
return new Promise((reslove: (db: IDBValidKey) => void, reject) => {
const res = store.put(value, key)
res.onerror = reject
res.onsuccess = () => reslove(res.result)
})
}
export async function getOrLoad<K extends keyof typeof storeNames, V>(
storeName: K,
key: IDBValidKey,
loader: (key: IDBValidKey) => V,
) {
const value: V = await objectStore(storeName).then(store => get(store, key))
if (value) {
return value
}
const newValue = await loader(key)
await objectStore(storeName, 'readwrite').then(store => put(store, newValue, key))
return newValue
}

View File

@ -2,26 +2,35 @@ import { DownloadPackage } from '@/core/download'
import { meta } from '@/core/meta'
import { Toast } from '@/core/toast'
import { FFmpeg } from './ffmpeg'
import { httpGet, toBlobUrl, toastProgress } from './utils'
import { getCacheOrGet, httpGet, toastProgress, toBlobUrl } from './utils'
const ffmpeg = new FFmpeg()
async function load(toast: Toast) {
await ffmpeg.load({
workerLoadURL: await toBlobUrl(
meta.compilationInfo.altCdn.library.ffmpeg.worker,
workerLoadURL: toBlobUrl(
await getCacheOrGet(
'ffmpeg-worker',
meta.compilationInfo.altCdn.library.ffmpeg.worker,
toastProgress(toast, '正在加载 FFmpeg Worker'),
),
'text/javascript',
toastProgress(toast, '正在加载 FFmpeg Worker'),
),
coreURL: await toBlobUrl(
meta.compilationInfo.altCdn.library.ffmpeg.core,
coreURL: toBlobUrl(
await getCacheOrGet(
'ffmpeg-core',
meta.compilationInfo.altCdn.library.ffmpeg.core,
toastProgress(toast, '正在加载 FFmpeg Core'),
),
'text/javascript',
toastProgress(toast, '正在加载 FFmpeg Core'),
),
wasmURL: await toBlobUrl(
meta.compilationInfo.altCdn.library.ffmpeg.wasm,
wasmURL: toBlobUrl(
await getCacheOrGet(
'ffmpeg-wasm',
meta.compilationInfo.altCdn.library.ffmpeg.wasm,
toastProgress(toast, '正在加载 FFmpeg WASM'),
),
'application/wasm',
toastProgress(toast, '正在加载 FFmpeg WASM'),
),
})
}

View File

@ -1,5 +1,6 @@
import { Toast } from '@/core/toast'
import { formatFileSize, formatPercent } from '@/core/utils/formatters'
import { getOrLoad, storeNames } from './database'
type OnProgress = (received: number, length: number) => void
@ -47,8 +48,11 @@ export async function httpGet(url: string, onprogress: OnProgress) {
return chunksAll
}
export async function toBlobUrl(url: string, mimeType: string, onprogress: OnProgress) {
const buffer = await httpGet(url, onprogress)
export async function getCacheOrGet(key: string, url: string, loading: OnProgress) {
return getOrLoad(storeNames.cache, key, async () => httpGet(url, loading))
}
export function toBlobUrl(buffer: Uint8Array, mimeType: string) {
const blob = new Blob([buffer], { type: mimeType })
return URL.createObjectURL(blob)
}

View File

@ -32,7 +32,8 @@ export const updateLatestID = (cards: { id: string }[]) => {
const [id] = [...cards.map(c => c.id)].sort(compareID).reverse()
setLatestID(id)
}
export const getNotifyCount = async (type = 'all'): Promise<number> => {
/** 按类型获取动态提醒数 */
export const getNotifyCountByType = async (type: string): Promise<number> => {
const api = `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all/update?type=${type}&update_baseline=${getLatestID()}`
const json = await getJsonWithCredentials(api)
if (json.code !== 0) {
@ -40,3 +41,14 @@ export const getNotifyCount = async (type = 'all'): Promise<number> => {
}
return lodash.get(json, 'data.update_num', 0)
}
/** (, , ; )
* @see https://github.com/the1812/Bilibili-Evolved/issues/4427
*/
export const getNotifyCount = async (): Promise<number> => {
const api = 'https://api.bilibili.com/x/web-interface/dynamic/entrance'
const json = await getJsonWithCredentials(api)
if (json.code !== 0) {
return 0
}
return lodash.get(json, 'data.update_info.item.count', 0)
}