Merge pull request #488 from hakadao/main

sync: main to bugfixes
This commit is contained in:
Hakadao
2024-04-08 01:12:48 +08:00
committed by GitHub
29 changed files with 668 additions and 149 deletions

View File

@@ -19,6 +19,6 @@ jobs:
token: ${{ secrets.RELEASE_TOKEN }}
release-type: node
package-name: release-please-action
release-as: 0.15.5
release-as: 0.16.0
signoff: 'github-actions <41898282+github-actions[bot]@users.noreply.github.com>'
changelog-types: '[{"type":"types","section":"Types","hidden":false},{"type":"revert","section":"Reverts","hidden":false},{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"improvement","section":"Feature Improvements","hidden":false},{"type":"docs","section":"Docs","hidden":false},{"type":"i18n","section":"I18n","hidden":true},{"type":"style","section":"Style Changes","hidden":false},{"type":"ci","section":"CI","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":true},{"type":"contributor","section":"New Contributors","hidden":false},{"type":"notice","section":"Notices","hidden":false}]'

View File

@@ -1,6 +1,8 @@
{
"cSpell.words": [
"bewly",
"bilibili",
"unocss",
"Vitesse",
"webext"
],

View File

@@ -1,5 +1,40 @@
# Changelog
## [0.16.0](https://github.com/hakadao/BewlyBewly/compare/v0.15.5...v0.16.0) (2024-04-07)
### Features
* add delay hover for topbar ([4a1f0a2](https://github.com/hakadao/BewlyBewly/commit/4a1f0a278ce413eeb54a7d50d49618afd7bc841e))
* add play all button to FavoritesPop & WatchLaterPop ([dd75b9d](https://github.com/hakadao/BewlyBewly/commit/dd75b9d038eae2468286df62fc56f2ea2f3e57f3))
* add some details for dock ([0cb2142](https://github.com/hakadao/BewlyBewly/commit/0cb21427c519bbe85552cba492c8c3c1ac4b8da7))
* adjust two bilibili evolved's buttons to match the BewlyBewly theme ([#423](https://github.com/hakadao/BewlyBewly/issues/423)) ([c55e86f](https://github.com/hakadao/BewlyBewly/commit/c55e86fdc32cd1520cae88f54d3ed3dfce2bc2ca))
* **Appearance:** add wallpaper change watcher ([ec7ffaa](https://github.com/hakadao/BewlyBewly/commit/ec7ffaa9c467e77e8c508bf0c79ea72d5878e359))
* **Home:** clicking the tab will back to top and refresh data on homepage ([#419](https://github.com/hakadao/BewlyBewly/issues/419)) ([aae391d](https://github.com/hakadao/BewlyBewly/commit/aae391d9f49eef3a8e3df98874207baedb0af0b8))
* **VideoCard:** improve not interested feature ([ba783ff](https://github.com/hakadao/BewlyBewly/commit/ba783ff0763ae758aa8d2ff7fc49873ca83a89bb))
### Bug Fixes
* attempt to resolve "EBUSY error from 'C:\DumpStack.log.tmp'" ([8009d8b](https://github.com/hakadao/BewlyBewly/commit/8009d8b93a7e62faefc85a55c3855876eed8ca6c))
* attempt to resolve "EBUSY error from 'C:\DumpStack.log.tmp'" ([60c2740](https://github.com/hakadao/BewlyBewly/commit/60c274076f49ee22006d29086ae9c12fa59cde83))
* cleanup fn ([#482](https://github.com/hakadao/BewlyBewly/issues/482)) ([b00d9b2](https://github.com/hakadao/BewlyBewly/commit/b00d9b271015a10798b5475ee9451234a8656f61))
* **Following:** resolve the "no more content" & "need to login first" occurring together ([69b6d86](https://github.com/hakadao/BewlyBewly/commit/69b6d865ad2548c82d8d7a233ce26ff9334d71cf))
* **Trending:** resolve the infinite data retrieval issus ([#445](https://github.com/hakadao/BewlyBewly/issues/445)) ([80b72c4](https://github.com/hakadao/BewlyBewly/commit/80b72c43b615d6b7a7f12246eb677fa3b7fa148b))
* **VideoCard:** author avatar display issue in horizontal mode ([efacf5b](https://github.com/hakadao/BewlyBewly/commit/efacf5bd281afbcf63c72e6652a6160128f64e81))
### Docs
* add introduction section to readme ([2ce9926](https://github.com/hakadao/BewlyBewly/commit/2ce9926cec0625783687aee9f7c2871abfa8057b))
### Style Changes
* adjust style of frosted glasses ([b6b7ed7](https://github.com/hakadao/BewlyBewly/commit/b6b7ed7c5abbc098f081b845982048ff5ba17ae9))
* **MomentsPop:** adjust styles ([#483](https://github.com/hakadao/BewlyBewly/issues/483)) ([f99086d](https://github.com/hakadao/BewlyBewly/commit/f99086dde9561f5f463672f74ccea9744f3bf4b3))
* **VideoCard:** adjust removed status style ([cfe6fd5](https://github.com/hakadao/BewlyBewly/commit/cfe6fd5601ab4ea33890bbbc28ae43a692184ff0))
## [0.15.5](https://github.com/hakadao/BewlyBewly/compare/v0.15.4...v0.15.5) (2024-03-30)

View File

@@ -1,7 +1,7 @@
{
"name": "bewly-bewly",
"displayName": "BewlyBewly",
"version": "0.15.5",
"version": "0.16.0",
"private": true,
"packageManager": "pnpm@8.15.3",
"description": "Just make a few small changes to your Bilibili homepage.",

View File

@@ -1,5 +1,6 @@
common:
view_all: 查看更多
play_all: 播放全部
loading: 加载中...
undo: 还原
view: '{count}次'
@@ -24,6 +25,8 @@ common:
refresh: 刷新
reset: 重置
no_more_content: 没有更多内容了
confirm: 确定
cancel: 取消
settings:
title: 设置
@@ -248,7 +251,8 @@ dock:
light_mode: 亮色模式
settings: 设置
home:
not_interested_in: 对哪方面不感兴趣
not_interested: 不感兴趣
tell_us_why: 为什么这么做...
video_removed: 视频已移除
for_you: 个性推荐
following: 正在关注
@@ -297,7 +301,6 @@ favorites:
unfavorite: 取消收藏
watch_later:
title: 稍后再看
play_all: 播放全部
clear_all: 清空稍后再看
clear_all_confirm: |-
要清空稍后再看吗?

View File

@@ -1,5 +1,6 @@
common:
view_all: 檢視所有
play_all: 全部播放
loading: 載入中...
undo: 復原
view: '{count}次'
@@ -24,6 +25,8 @@ common:
refresh: 重新整理
reset: 重置
no_more_content: 沒有更多內容了
confirm: 確定
cancel: 取消
settings:
title: 設定
@@ -251,7 +254,8 @@ dock:
light_mode: 淺色模式
settings: 設定
home:
not_interested_in: 對哪方面不感興趣
not_interested: 不感興趣
tell_us_why: 為什麼這樣做...
video_removed: 影片已移除
for_you: 爲你推薦
following: 正在跟隨
@@ -300,7 +304,6 @@ favorites:
unfavorite: 取消收藏
watch_later:
title: 稍後觀看
play_all: 全部播放
clear_all: 清空稍後觀看
clear_all_confirm: |-
要清空稍後觀看的影片嗎?

View File

@@ -1,5 +1,6 @@
common:
view_all: View ALL
play_all: Play ALL
loading: Loading...
undo: UNDO
view: no view | {count} view | {count} views
@@ -24,6 +25,8 @@ common:
refresh: Refresh
reset: Reset
no_more_content: No more content now, owari da
confirm: Confirm
cancel: Cancel
settings:
title: Settings
@@ -249,7 +252,8 @@ dock:
light_mode: Light Mode
settings: Settings
home:
not_interested_in: Not interested in...
not_interested: Not interested
tell_us_why: Choose a reason why
video_removed: Video removed
for_you: For you
following: Following
@@ -296,7 +300,6 @@ favorites:
unfavorite: Unfavorite
watch_later:
title: Watch Later
play_all: Play all
clear_all: Clear all watch later
clear_all_confirm: |-
Clear all watch later?

View File

@@ -1,7 +1,8 @@
common:
view_all: 睇晒佢哋
play_all: 播晒佢哋
loading: 撈緊...
undo: 整返
undo: 整返返去
view: '{count}次'
viewWithoutNum:
danmaku: '{count} 彈幕'
@@ -24,6 +25,8 @@ common:
refresh: 重新整理
reset: 重置
no_more_content: 唔使睇喇,冇嘢喇
confirm: 確定
cancel: 取消
settings:
title: 設定
@@ -251,8 +254,9 @@ dock:
light_mode: 淺色模式
settings: 設定
home:
not_interested_in: 對邊方面冇癮?
video_removed: 經已鏟咗條片
not_interested: 唔鍾意
tell_us_why: 點解噉做...
video_removed: 已經鏟咗條片
for_you: 估你心水
following: Follow 緊
trending: 時下至 Hit
@@ -300,7 +304,6 @@ favorites:
unfavorite: 唔再愛喇
watch_later:
title: 陣間至睇
play_all: 播晒佢哋
clear_all: 剷曬陣間至睇啲片
clear_all_confirm: |-
係咪要剷曬陣間至睇啲片呀?

View File

@@ -1,58 +1,57 @@
import browser from 'webextension-polyfill'
function handleMessage(message: any) {
// #region APP端api遺棄
/** Recommend Videos */
// if (message.contentScriptQuery === 'getRecommendVideos') {
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E8%8E%B7%E5%8F%96%E9%A6%96%E9%A1%B5%E5%86%85%E5%AE%B9
// const url = `${APP_URL}/x/feed/index?build=1&idx=${message.idx}&appkey=27eb53fc9058f8c3&access_key=${message.accessKey}`
// return fetch(url)
// .then(response => response.json())
// .then(data => data)
// .catch(error => console.error(error))
// }
// /** Submit a video that is not of interest */
// else if (message.contentScriptQuery === 'submitDislike') {
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%8F%90%E4%BA%A4%E4%B8%8D%E5%96%9C%E6%AC%A2
// let url = `https://app.bilibili.com/x/feed/dislike?access_key=${message.accessKey}
// &goto=${message.goto}
// &id=${message.id}
// &mid=${message.mid}
// &reason_id=${message.reasonID}
// &rid=${message.rid}
// &tag_id=${message.tagID}
// &build=5000000`
/** Submit a video that is not of interest */
if (message.contentScriptQuery === 'dislikeVideo') {
// https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%8F%90%E4%BA%A4%E4%B8%8D%E5%96%9C%E6%AC%A2
const url = `https://app.bilibili.com/x/feed/dislike?access_key=${message.access_key}`
+ `&goto=${message.goto}`
+ `&id=${message.id}`
+ `&idx=${message.idx}`
// + `&mid=${message.mid}`
+ `&reason_id=${message.reason_id}`
// + `&rid=${message.rid}`
// + `&tag_id=${message.tagId}`
+ `&device=${message.device}`
+ `&mobi_app=${message.mobi_app}`
+ `&build=${message.build}`
+ `&sign=${message.sign}`
+ `&appkey=${message.appkey}`
// // remove url empty spaces
// url = url.replace(/\s+/g, '')
// // remove url empty spaces
// url = url.replace(/\s+/g, '')
// return fetch(url)
// .then(response => response.json())
// .then(data => data)
// .catch(error => console.error(error))
// }
return fetch(url)
.then(response => response.json())
.then(data => data)
.catch(error => console.error(error))
}
// /** Undo a video that is not of interest */
// else if (message.contentScriptQuery === 'undoDislike') {
// // https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%92%A4%E9%94%80%E4%B8%8D%E5%96%9C%E6%AC%A2
// let url = `https://app.bilibili.com/x/feed/dislike/cancel?access_key=${message.accessKey}
// &goto=${message.goto}
// &id=${message.id}
// &mid=${message.mid}
// &reason_id=${message.reasonID}
// &rid=${message.rid}
// &tag_id=${message.tagID}
// &build=5000000`
/** Undo a video that is not of interest */
else if (message.contentScriptQuery === 'undoDislikeVideo') {
// https://github.com/indefined/UserScripts/blob/master/bilibiliHome/bilibiliHome.API.md#%E6%92%A4%E9%94%80%E4%B8%8D%E5%96%9C%E6%AC%A2
const url = `https://app.bilibili.com/x/feed/dislike/cancel?access_key=${message.access_key}`
+ `&goto=${message.goto}`
+ `&id=${message.id}`
+ `&idx=${message.idx}`
// + `&mid=${message.mid}`
+ `&reason_id=${message.reason_id}`
// + `&rid=${message.rid}`
// + `&tag_id=${message.tagId}`
+ `&device=${message.device}`
+ `&mobi_app=${message.mobi_app}`
+ `&build=${message.build}`
+ `&sign=${message.sign}`
+ `&appkey=${message.appkey}`
// // remove url empty spaces
// url = url.replace(/\s+/g, '')
// // remove url empty spaces
// url = url.replace(/\s+/g, '')
// return fetch(url)
// .then(response => response.json())
// .then(data => data)
// .catch(error => console.error(error))
// }
// #endregion
return fetch(url)
.then(response => response.json())
.then(data => data)
.catch(error => console.error(error))
}
if (message.contentScriptQuery === 'getRecommendVideos') {
const url = `https://api.bilibili.com/x/web-interface/index/top/feed/rcmd?fresh_idx=${message.refreshIdx}&feed_version=V2&fresh_type=4&ps=30&plat=1`

View File

@@ -113,7 +113,7 @@ function handleClick(evt: MouseEvent) {
}
&--custom-color {
--at-apply: hover:opacity-80;
--at-apply: hover:opacity-70;
}
&--strong {

172
src/components/Dialog.vue Normal file
View File

@@ -0,0 +1,172 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { onKeyStroke } from '@vueuse/core'
const props = withDefaults(defineProps<{
title: string
desc?: string
center?: boolean
appendToBewlyBody?: boolean
width?: string | number
height?: string | number
centerFooter?: boolean
loading?: boolean
preventCloseWhenLoading?: boolean
}>(), {
preventCloseWhenLoading: true,
})
const emit = defineEmits(['close', 'confirm'])
onKeyStroke('Enter', (e: KeyboardEvent) => {
e.preventDefault()
if (!props.loading)
handleConfirm()
})
onKeyStroke('Escape', (e: KeyboardEvent) => {
e.preventDefault()
if (props.loading && props.preventCloseWhenLoading)
return
handleClose()
})
const showShortcut = ref<boolean>(false)
const { mainAppRef } = useBewlyApp()
const showDialog = ref<boolean>(false)
const dialogWidth = computed(() => {
return typeof props.width === 'number' ? `${props.width}px` : props.width || '400px'
})
const dialogHeight = computed(() => {
return typeof props.height === 'number' ? `${props.height}px` : props.height || 'auto'
})
onKeyStroke('Alt', (e: KeyboardEvent) => {
e.preventDefault()
showShortcut.value = true
}, { eventName: 'keydown' })
onKeyStroke('Alt', (e: KeyboardEvent) => {
e.preventDefault()
showShortcut.value = false
}, { eventName: 'keyup' })
onMounted(() => {
showDialog.value = true
})
onBeforeUnmount(() => {
handleClose()
})
function handleClose() {
if (props.loading && props.preventCloseWhenLoading)
return
showDialog.value = false
nextTick(() => {
emit('close')
})
}
function handleConfirm() {
emit('confirm')
if (!props.loading)
handleClose()
}
</script>
<template>
<Teleport :to="mainAppRef" :disabled="!appendToBewlyBody">
<Transition name="modal">
<div
v-if="showDialog"
class="dialog"
pos="fixed top-0 left-0" w-full h-full z-100
>
<div
bg="black opacity-40 dark:opacity-40"
pos="absolute top-0 left-0" w-full h-full z-0
@click="handleClose"
/>
<div
style="
--un-shadow: var(--bew-shadow-3) var(--bew-shadow-edge-glow-2);
backdrop-filter: var(--bew-filter-glass-2);
"
:style="{ width: dialogWidth, height: dialogHeight }"
pos="absolute top-1/2 left-1/2" bg="$bew-content-2 dark:$bew-elevated-1" rounded="$bew-radius"
transform="translate--1/2" z-2 shadow overflow="x-hidden y-overlay"
antialiased
>
<!-- loading masking -->
<Transition name="fade">
<div
v-if="loading"
pos="absolute top-0 left-0" w-full h-full bg="white dark:black opacity-60 dark:opacity-60" flex="~ justify-center items-center"
z-2
>
<Icon icon="svg-spinners:ring-resize" text="4xl" />
</div>
</Transition>
<header
style="
text-shadow: 0 0 15px var(--bew-elevated-solid-1), 0 0 20px var(--bew-elevated-solid-1)
"
pos="sticky top-0 left-0" w-full h-80px px-8 flex
items-center justify-between
rounded="t-$bew-radius" z-1
>
<div
:style="{ textAlign: center ? 'center' : 'left' }"
w-full
>
<p text-xl fw-bold>
{{ title }}
</p>
<p text="sm $bew-text-2">
<slot name="desc">
{{ desc }}
</slot>
</p>
</div>
<div
style="backdrop-filter: var(--bew-filter-glass-1)"
text-2xl leading-0 bg="$bew-fill-1 hover:$bew-theme-color-30" w="32px" h="32px"
ml-8 p="1" rounded-8 cursor="pointer"
hover:ring="2 $bew-theme-color" hover:text="$bew-theme-color" duration-300
@click="handleClose"
>
<ic-baseline-clear />
</div>
</header>
<main p="x-8 t-0 b-4" relative>
<!-- <div h-80px mt--8 /> -->
<slot />
</main>
<footer
:style="{ justifyContent: centerFooter ? 'center' : 'flex-end' }"
flex="~ gap-2" p="8 t-0"
>
<Button type="secondary" size="large" @click="handleClose">
<div>
{{ $t('common.cancel') }}
<span v-show="showShortcut" text="xs $bew-text-2">(Esc)</span>
</div>
</Button>
<Button type="primary" size="large" @click="handleConfirm">
<div>
{{ $t('common.confirm') }}
<span v-show="showShortcut" text="xs">(Enter)</span>
</div>
</Button>
</footer>
</div>
</div>
</Transition>
</Teleport>
</template>
<style lang="scss" scoped>
</style>

View File

@@ -66,6 +66,10 @@ const themeOptions = computed<Array<{ value: string, label: string }>>(() => {
]
})
watch(() => settings.value.wallpaper, (newValue) => {
changeWallpaper(newValue)
})
function changeThemeColor(color: string) {
settings.value.themeColor = color
}

View File

@@ -69,6 +69,47 @@ const favoritesPopRef = ref<any>()
const scrollTop = ref<number>(0)
const oldScrollTop = ref<number>(0)
// Avatar
const avatar = useDelayedHover({
enter: () => openUserPanel(),
leave: () => closeUserPanel(),
})
// Notifications
const notifications = useDelayedHover({
enter: () => showNotificationsPop.value = true,
leave: () => showNotificationsPop.value = false,
})
// Moments
const moments = useDelayedHover({
enter: () => showMomentsPop.value = true,
leave: () => showMomentsPop.value = false,
})
// Favorites
const favorites = useDelayedHover({
enter: () => showFavoritesPop.value = true,
leave: () => showFavoritesPop.value = false,
})
// Upload
const upload = useDelayedHover({
enter: () => showUploadPop.value = true,
leave: () => showUploadPop.value = false,
})
// History
const history = useDelayedHover({
enter: () => showHistoryPop.value = true,
leave: () => showHistoryPop.value = false,
})
// Watch Later
const watchLater = useDelayedHover({
enter: () => showWatchLaterPop.value = true,
leave: () => showWatchLaterPop.value = false,
})
// More
const more = useDelayedHover({
enter: () => showMorePop.value = true,
leave: () => showMorePop.value = false,
})
watch(() => settings.value.autoHideTopBar, (newVal) => {
if (!newVal)
toggleTopBarVisible(true)
@@ -379,9 +420,8 @@ defineExpose({
<template v-if="isLogin">
<!-- Avatar -->
<div
ref="avatar"
class="avatar right-side-item"
@mouseenter="openUserPanel"
@mouseleave="closeUserPanel"
>
<a
ref="avatarImg"
@@ -447,10 +487,9 @@ defineExpose({
<div display="lg:flex none" gap-1>
<!-- Notifications -->
<div
ref="notifications"
class="right-side-item"
:class="{ active: showNotificationsPop }"
@mouseenter="showNotificationsPop = true"
@mouseleave="showNotificationsPop = false"
>
<template v-if="unReadMessageCount > 0">
<div
@@ -482,10 +521,9 @@ defineExpose({
<!-- Moments -->
<div
ref="moments"
class="right-side-item"
:class="{ active: showMomentsPop }"
@mouseenter="showMomentsPop = true"
@mouseleave="showMomentsPop = false"
>
<template v-if="newMomentsCount > 0">
<div
@@ -516,10 +554,9 @@ defineExpose({
<!-- Favorites -->
<div
ref="favorites"
class="right-side-item"
:class="{ active: showFavoritesPop }"
@mouseenter="showFavoritesPop = true"
@mouseleave="showFavoritesPop = false"
>
<a
:href="`https://space.bilibili.com/${mid}/favlist`"
@@ -542,10 +579,9 @@ defineExpose({
<!-- History -->
<div
ref="history"
class="right-side-item"
:class="{ active: showHistoryPop }"
@mouseenter="showHistoryPop = true"
@mouseleave="showHistoryPop = false"
>
<a
href="https://www.bilibili.com/account/history"
@@ -562,10 +598,9 @@ defineExpose({
<!-- Watch later -->
<div
ref="watchLater"
class="right-side-item"
:class="{ active: showWatchLaterPop }"
@mouseenter="showWatchLaterPop = true"
@mouseleave="showWatchLaterPop = false"
>
<a
href="https://www.bilibili.com/watchlater/#/list"
@@ -594,11 +629,10 @@ defineExpose({
<!-- More -->
<div
ref="more"
class="right-side-item"
:class="{ active: showMorePop }"
display="lg:!none flex"
@mouseenter="showMorePop = true"
@mouseleave="showMorePop = false"
>
<a title="More">
<mingcute:menu-line />
@@ -611,9 +645,8 @@ defineExpose({
<!-- Upload -->
<div
ref="upload"
class="upload right-side-item"
@mouseenter="showUploadPop = true"
@mouseleave="showUploadPop = false"
>
<a
href="https://member.bilibili.com/platform/upload/video/frame"

View File

@@ -17,12 +17,16 @@ const isLoading = ref<boolean>(false)
const noMoreContent = ref<boolean>(false)
const favoriteVideosWrap = ref<HTMLElement>() as Ref<HTMLElement>
const favoritesPageUrl = computed(() => {
const viewAllUrl = computed((): string => {
return `//space.bilibili.com/${getUserID()}/favlist?fid=${
activatedMediaId.value
}`
})
const playAllUrl = computed((): string => {
return `https://www.bilibili.com/list/ml${activatedMediaId.value}`
})
watch(activatedMediaId, (newVal: number, oldVal: number) => {
if (newVal === oldVal)
return
@@ -148,12 +152,20 @@ defineExpose({
{{ activatedFavoriteTitle }}
</h3>
<a
:href="favoritesPageUrl" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~" items="center"
>
<span text="sm">{{ $t('common.view_all') }}</span>
</a>
<div flex="~ gap-4">
<a
:href="playAllUrl" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~" items="center"
>
<span text="sm">{{ $t('common.play_all') }}</span>
</a>
<a
:href="viewAllUrl" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~" items="center"
>
<span text="sm">{{ $t('common.view_all') }}</span>
</a>
</div>
</header>
<main flex="~" overflow-hidden rounded="$bew-radius">

View File

@@ -380,8 +380,9 @@ function toggleWatchLater(aid: number) {
<div flex="~" justify="between" w="full">
<div>
<span v-if="selectedTab !== 1">{{ `${moment.name} ${t('topbar.moments_dropdown.uploaded')}` }}</span>
<span v-else>{{ `${moment.name} ${t('topbar.moments_dropdown.now_streaming')}` }}</span>
<!-- <span v-if="selectedTab !== 1">{{ `${moment.name} ${t('topbar.moments_dropdown.uploaded')}` }}</span> -->
<!-- <span v-else>{{ `${moment.name} ${t('topbar.moments_dropdown.now_streaming')}` }}</span> -->
<span font-bold>{{ moment.name }}</span>
<div overflow-hidden text-ellipsis break-anywhere>
{{ moment.title }}
</div>

View File

@@ -7,6 +7,13 @@ import { isHomePage, removeHttpFromUrl } from '~/utils/main'
const watchLaterList = reactive<VideoItem[]>([])
const isLoading = ref<boolean>()
const viewAllUrl = computed((): string => {
return 'https://www.bilibili.com/watchlater/#/list'
})
const playAllUrl = computed((): string => {
return 'https://www.bilibili.com/list/watchlater'
})
onMounted(() => {
getAllWatchLaterList()
})
@@ -65,12 +72,21 @@ function getAllWatchLaterList() {
{{ $t('topbar.watch_later') }}
</div>
</div>
<a
href="https://www.bilibili.com/watchlater/#/list" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~ items-center"
>
<span text="sm">{{ $t('common.view_all') }}</span>
</a>
<div flex="~ gap-4">
<a
:href="playAllUrl" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~" items="center"
>
<span text="sm">{{ $t('common.play_all') }}</span>
</a>
<a
:href="viewAllUrl" :target="isHomePage() ? '_blank' : '_self'" rel="noopener noreferrer"
flex="~" items="center"
>
<span text="sm">{{ $t('common.view_all') }}</span>
</a>
</div>
</header>
<!-- watchLater wrapper -->

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { Icon } from '@iconify/vue'
import { getCSRF, removeHttpFromUrl } from '~/utils/main'
import { calcCurrentTime, calcTimeSince, numFormatter } from '~/utils/dataFormatter'
import type { VideoPreviewResult } from '~/models/video/videoPreview'
@@ -37,9 +38,6 @@ interface Props {
moreBtn?: boolean
moreBtnActive?: boolean
removed?: boolean
showDislikeOptions?: boolean
feedbackReason?: { id: number, name: string }
dislikeReason?: { id: number, name: string }
}
const props = withDefaults(defineProps<Props>(), {
@@ -48,6 +46,8 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<{
(e: 'moreClick', event: MouseEvent): MouseEvent
(e: 'undo'): void
(e: 'tellUsWhy'): void
}>()
const videoUrl = computed(() => {
@@ -77,7 +77,6 @@ const wValue = computed((): string => {
return 'w-full'
})
const isDislike = ref<boolean>(false)
const isInWatchLater = ref<boolean>(false)
const isHover = ref<boolean>(false)
const contentVisibility = ref<'auto' | 'visible'>('auto')
@@ -152,6 +151,10 @@ function handelMouseLeave() {
function handleMoreBtnClick(event: MouseEvent) {
emit('moreClick', event)
}
function handleUndo() {
emit('undo')
}
</script>
<template>
@@ -162,19 +165,46 @@ function handleMoreBtnClick(event: MouseEvent) {
<div hidden w="xl:280px lg:250px md:200px 200px" />
<div hidden w="full" />
<template v-if="removed">
<div
:style="{ contentVisibility }"
pos="absolute top-0 left-0" w-full aspect-video
rounded="$bew-radius"
>
<img
:src="`${removeHttpFromUrl(cover)}@672w_378h_1c`" alt=""
w-full h-full object-cover pos="absolute top-0 left-0" aspect-video
z--1 rounded-inherit
>
<div
pos="absolute top-0 left-0" w-full h-full flex="~ col gap-2 items-center justify-center"
bg="$bew-fill-4" backdrop-blur-20px mix-blend-luminosity
>
<p mb-2 color-white text-lg>
{{ $t('home.video_removed') }}
</p>
<Button
color="rgba(255,255,255,.35)" text-color="white" size="small"
@click="handleUndo"
>
<template #left>
<Icon icon="mingcute:back-line" text-lg />
</template>
{{ $t('common.undo') }}
</Button>
</div>
</div>
</template>
<div
v-else
class="video-card group"
:class="isDislike ? 'is-dislike' : ''"
w="full" pos="absolute top-0 left-0"
rounded="$bew-radius" duration-300 ease-in-out
bg="hover:$bew-fill-2 active:$bew-fill-3" hover:ring="8 $bew-fill-2" active:ring="8 $bew-fill-3"
:style="{ contentVisibility }"
>
<template v-if="showDislikeOptions">
fdsflsd
</template>
<a
v-else
:style="{ display: horizontal ? 'flex' : 'block', gap: horizontal ? '1.5rem' : '0' }"
:href="videoUrl" target="_blank" rel="noopener noreferrer"
>
@@ -418,7 +448,7 @@ function handleMoreBtnClick(event: MouseEvent) {
<!-- skeleton -->
<template v-if="!horizontal">
<div
block mb-10 pointer-events-none select-none invisible
block mb-6 pointer-events-none select-none invisible
>
<!-- Cover -->
<div w-full shrink-0 aspect-video h-fit rounded="$bew-radius" />
@@ -450,7 +480,7 @@ function handleMoreBtnClick(event: MouseEvent) {
<template v-else>
<div
flex="~ gap-6"
mb-10 pointer-events-none select-none invisible
mb-6 pointer-events-none select-none invisible
>
<!-- Cover -->
<div
@@ -482,11 +512,11 @@ function handleMoreBtnClick(event: MouseEvent) {
</template>
<style lang="scss" scoped>
.video-card.is-dislike {
> *:not(#dislike-control) {
--at-apply: invisible pointer-events-none duration-0 transition-none;
}
}
// .video-card.is-dislike {
// > *:not(#dislike-control) {
// --at-apply: invisible pointer-events-none duration-0 transition-none;
// }
// }
.more-active {
--at-apply: opacity-100 bg-$bew-fill-3;

View File

@@ -15,7 +15,7 @@ const wValue = computed((): string => {
<template>
<div
v-if="!horizontal"
mb-8 pointer-events-none select-none
mb-6 pointer-events-none select-none
>
<div aspect-video bg="$bew-fill-4" rounded="$bew-radius" />
<div flex mt-4>
@@ -45,7 +45,7 @@ const wValue = computed((): string => {
<div
v-else
flex="~ gap-6"
mb-10 pointer-events-none select-none
mb-6 pointer-events-none select-none
>
<!-- By directly using predefined unocss width properties, it is possible to dynamically set the width attribute -->
<div hidden w="xl:280px lg:250px md:200px 200px" />

View File

@@ -0,0 +1,37 @@
export function useDelayedHover({ delay = 200, enter, leave }: { delay?: number, enter: Function, leave: Function }) {
const el = ref<HTMLElement>()
let timer: any | undefined
function handleMouseEnter() {
if (timer) {
clearTimeout(timer)
timer = undefined
}
timer = setTimeout(() => {
enter()
}, delay)
}
function handleMouseLeave() {
if (timer) {
leave()
clearTimeout(timer)
timer = undefined
}
}
watch(el, (el, _, onCleanup) => {
if (el) {
el.addEventListener('mouseenter', handleMouseEnter)
el.addEventListener('mouseleave', handleMouseLeave)
}
onCleanup(() => {
if (el) {
el.removeEventListener('mouseenter', handleMouseEnter)
el.removeEventListener('mouseleave', handleMouseLeave)
}
})
}, { flush: 'post' })
return el
}

View File

@@ -256,7 +256,10 @@ function handleUnfavorite(favoriteResource: FavoriteResource) {
pos="absolute top-0 left-0" w-full h-full bg-cover bg-center
z--1
>
<div absolute w-full h-full style="backdrop-filter: blur(60px) saturate(180%)" bg="$bew-fill-4" />
<div
absolute w-full h-full backdrop-blur-40px
bg="$bew-fill-4" mix-blend-luminosity
/>
<img
v-if="activatedCategoryCover"
:src="removeHttpFromUrl(`${activatedCategoryCover}@480w_270h_1c`)"
@@ -288,7 +291,7 @@ function handleUnfavorite(favoriteResource: FavoriteResource) {
<template #left>
<tabler:player-play />
</template>
{{ t('watch_later.play_all') }}
{{ t('common.play_all') }}
</Button>
</p>
<ul class="category-list" h-full overflow-overlay border="1 color-[rgba(255,255,255,.2)]" rounded="$bew-radius">

View File

@@ -1,12 +1,13 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import { onClickOutside } from '@vueuse/core'
import { onKeyStroke } from '@vueuse/core'
import { Type as ThreePointV2Type } from '~/models/video/appForYou'
import type { AppForYouResult, Item as AppVideoItem, ThreePointV2 } from '~/models/video/appForYou'
import type { Item as VideoItem, forYouResult } from '~/models/video/forYou'
import type { GridLayout } from '~/logic'
import { accessKey, settings } from '~/logic'
import { LanguageType } from '~/enums/appEnums'
import { TVAppKey } from '~/utils/authProvider'
import { TVAppKey, getTvSign } from '~/utils/authProvider'
const props = defineProps<{
gridLayout: GridLayout
@@ -36,19 +37,44 @@ const { handleReachBottom, handlePageRefresh, scrollbarRef } = useBewlyApp()
const showVideoOptions = ref<boolean>(false)
const videoOptions = ref<ThreePointV2[] | undefined>([])
const videoOptionsPosition = reactive<{ top: string, left: string }>({ top: '0', left: '0' })
// const activatedVideoId = ref<number>(0)
const activatedVideoIdx = ref<number>(0)
const activatedVideo = ref<AppVideoItem | null>()
const videoCardRef = ref(null)
const dislikedVideoIds = ref<number[]>([])
const showDislikeDialog = ref<boolean>(false)
const selectedDislikeReason = ref<number>(1)
const loadingDislikeDialog = ref<boolean>(false)
onKeyStroke((e: KeyboardEvent) => {
if (showDislikeDialog.value) {
const dislikeReasons = activatedVideo.value?.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons || []
if (e.key >= '0' && e.key <= '9') {
e.preventDefault()
dislikeReasons.forEach((reason) => {
if (dislikeReasons[Number(e.key) - 1] && reason.id === dislikeReasons[Number(e.key) - 1].id)
selectedDislikeReason.value = reason.id
})
}
else if (e.key === 'ArrowUp') {
e.preventDefault()
const currentIndex = dislikeReasons.findIndex(reason => selectedDislikeReason.value === reason.id)
if (currentIndex > 0)
selectedDislikeReason.value = dislikeReasons[currentIndex - 1].id
}
else if (e.key === 'ArrowDown') {
e.preventDefault()
const currentIndex = dislikeReasons.findIndex(reason => selectedDislikeReason.value === reason.id)
if (currentIndex < dislikeReasons.length - 1)
selectedDislikeReason.value = dislikeReasons[currentIndex + 1].id
}
}
})
watch(() => settings.value.recommendationMode, () => {
initData()
})
onClickOutside(videoCardRef, () => {
closeVideoOptions()
})
onMounted(async () => {
// Delay by 0.2 seconds to obtain the `settings.value.recommendationMode` value
// otherwise the `settings.value.recommendationMode` value will be undefined
@@ -180,12 +206,13 @@ async function getAppRecommendVideos() {
}
function handleMoreClick(e: MouseEvent, data: AppVideoItem) {
if (activatedVideo.value && activatedVideo.value.idx === data.idx) {
if (activatedVideo.value && activatedVideoIdx.value === data.idx) {
closeVideoOptions()
return
}
showVideoOptions.value = true
// activatedVideo.value.idx = data.idx
activatedVideoIdx.value = data.idx
activatedVideo.value = data
const osInstance = scrollbarRef.value?.osInstance()
const scrollTop = osInstance.elements().viewport.scrollTop || 0
@@ -194,26 +221,82 @@ function handleMoreClick(e: MouseEvent, data: AppVideoItem) {
videoOptionsPosition.left = `${e.clientX}px`
}
function handleMoreCommand(command: string) {
if (activatedVideo.value)
dislikedVideoIds.value.push(activatedVideo.value.idx)
function handleMoreCommand(command: ThreePointV2Type) {
closeVideoOptions()
// eslint-disable-next-line no-empty
switch (command) {}
// if (command === 'close')
// closeVideoOptions()
switch (command) {
case ThreePointV2Type.Feedback:
break
case ThreePointV2Type.Dislike:
openDislikeDialog()
break
}
}
function closeVideoOptions() {
showVideoOptions.value = false
activatedVideo.value = null
activatedVideoIdx.value = 0
}
// function handleVideoOptionsCommand(command: string) {
// if (command === 'close')
// closeVideoOptions()
// }
function openDislikeDialog() {
showDislikeDialog.value = true
}
function closeDislikeDialog() {
showDislikeDialog.value = false
selectedDislikeReason.value = 1
}
function handleDislike() {
loadingDislikeDialog.value = true
const params = {
access_key: accessKey.value,
goto: activatedVideo.value?.goto,
id: activatedVideo.value?.param,
// https://github.com/magicdawn/bilibili-app-recommend/blob/cb51f75f415f48235ce048537f2013122c16b56b/src/components/VideoCard/card.service.ts#L115
idx: (Date.now() / 1000).toFixed(0),
reason_id: selectedDislikeReason.value,
build: 74800100,
device: 'pad',
mobi_app: 'iphone',
appkey: TVAppKey.appkey,
}
browser.runtime.sendMessage({
contentScriptQuery: 'dislikeVideo',
...params,
sign: getTvSign(params),
}).then((res) => {
if (res.code === 0)
dislikedVideoIds.value.push(activatedVideo.value!.idx)
}).finally(() => {
loadingDislikeDialog.value = false
})
}
function handleUndoDislike(video: AppVideoItem) {
const params = {
access_key: accessKey.value,
goto: video.goto,
id: video.param,
// https://github.com/magicdawn/bilibili-app-recommend/blob/cb51f75f415f48235ce048537f2013122c16b56b/src/components/VideoCard/card.service.ts#L115
idx: (Date.now() / 1000).toFixed(0),
reason_id: 1, // 1 means dislike, e.g. {"id": 1, "name": "不感兴趣","toast": "将减少相似内容推荐"}
build: 74800100,
device: 'pad',
mobi_app: 'iphone',
appkey: TVAppKey.appkey,
}
browser.runtime.sendMessage({
contentScriptQuery: 'undoDislikeVideo',
...params,
sign: getTvSign(params),
}).then((res) => {
if (res.code === 0)
dislikedVideoIds.value = dislikedVideoIds.value.filter(currentIdx => currentIdx !== video.idx)
})
}
function jumpToLoginPage() {
location.href = 'https://passport.bilibili.com/login'
@@ -229,8 +312,12 @@ defineExpose({ initData })
<div hidden grid="~ cols-1 xl:cols-2 gap-4" />
<div hidden grid="~ cols-1 gap-4" />
<!-- dislike popup -->
<!-- more popup -->
<div v-show="showVideoOptions">
<div
pos="fixed top-0 left-0" w-full h-full z-1
@click="closeVideoOptions"
/>
<div
style="backdrop-filter: var(--bew-filter-glass-1);"
:style="{ transform: `translate(${videoOptionsPosition.left}, ${videoOptionsPosition.top})` }"
@@ -239,17 +326,54 @@ defineExpose({ initData })
shadow="$bew-shadow-1" z-10
>
<ul flex="~ col gap-1">
<li
v-for="option in videoOptions" :key="option.type"
bg="hover:$bew-fill-2" p="x-4 y-2" rounded="$bew-radius-half" cursor-pointer
@click="handleMoreCommand('')"
>
{{ option.title }}
</li>
<template v-for="option in videoOptions" :key="option.type">
<li
v-if="option.type !== ThreePointV2Type.WatchLater && option.type !== ThreePointV2Type.Feedback"
bg="hover:$bew-fill-2" p="x-4 y-2" rounded="$bew-radius-half" cursor-pointer
@click="handleMoreCommand(option.type)"
>
<span v-if="option.type === ThreePointV2Type.Dislike">{{ $t('home.not_interested') }}</span>
<span v-else>{{ option.title }}</span>
</li>
</template>
</ul>
</div>
</div>
<!-- dislike dialog -->
<Dialog
v-if="showDislikeDialog"
:title="$t('home.tell_us_why')"
width="400px"
append-to-bewly-body
center-footer
:loading="loadingDislikeDialog"
@close="closeDislikeDialog"
@confirm="handleDislike"
>
<ul flex="~ col gap-2">
<li
v-for="(reason, index) in activatedVideo?.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons"
:key="reason.id"
:class="{ 'activated-dislike-reason': selectedDislikeReason === reason.id }"
p="x-6 y-4" rounded="$bew-radius" cursor-pointer bg="$bew-fill-1 hover:$bew-fill-2"
flex="~ gap-2 items-center justify-between"
@click="selectedDislikeReason = reason.id"
>
<div flex="~ gap-2">
<div
bg="$bew-theme-color" color-white w-20px h-20px rounded-10
flex="~ justify-center items-center"
>
{{ index + 1 }}
</div>
{{ reason.name }}
</div>
<line-md:confirm v-if="selectedDislikeReason === reason.id" />
</li>
</ul>
</Dialog>
<Empty v-if="needToLoginFirst" mt-6 :description="$t('common.please_log_in_first')">
<Button type="primary" @click="jumpToLoginPage()">
{{ $t('common.login') }}
@@ -304,10 +428,13 @@ defineExpose({ initData })
show-preview
:horizontal="gridLayout !== 'adaptive'"
more-btn
:more-btn-active="video.idx === activatedVideo?.idx"
:show-dislike-options="dislikedVideoIds.includes(video.idx)"
:more-btn-active="video.idx === activatedVideoIdx"
:removed="dislikedVideoIds.includes(video.idx)"
:dislike-reasons="video.three_point_v2?.find(option => option.type === ThreePointV2Type.Dislike)?.reasons || []"
@more-click="(e) => handleMoreClick(e, video)"
@undo="handleUndoDislike(video)"
/>
<!-- :more-options="video.three_point_v2" -->
</template>
<!-- skeleton -->
@@ -329,4 +456,7 @@ defineExpose({ initData })
</template>
<style lang="scss" scoped>
.activated-dislike-reason {
--at-apply: bg-$bew-theme-color-20 color-$bew-theme-color;
}
</style>

View File

@@ -270,7 +270,10 @@ function jumpToLoginPage() {
pos="absolute top-0 left-0" w-full h-full bg-cover bg-center
z--1
>
<div absolute w-full h-full style="backdrop-filter: blur(60px) saturate(180%)" bg="$bew-fill-4" />
<div
absolute w-full h-full backdrop-blur-40px
bg="$bew-fill-4" mix-blend-luminosity
/>
<img
v-if="watchLaterList[0]"
:src="removeHttpFromUrl(`${watchLaterList[0].pic}@480w_270h_1c`)"
@@ -299,7 +302,7 @@ function jumpToLoginPage() {
<template #left>
<tabler:player-play />
</template>
{{ t('watch_later.play_all') }}
{{ t('common.play_all') }}
</Button>
<Button
color="rgba(255,255,255,.35)" block text-color="white" strong flex-1

View File

@@ -102,3 +102,20 @@ html.dark.bewly-design {
.dark::view-transition-new(root) {
z-index: 1;
}
// Adapt the colors of the two Bilibili Evolved's buttons on the left side to BewlyBewly theme
.bewly-design {
.be-settings > .sidebar > * {
background-color: var(--bew-elevated-solid-1) !important;
opacity: 0.4;
&:hover {
opacity: 1;
}
}
.be-settings > .sidebar > * .be-icon {
color: var(--bew-text-1) !important;
fill: var(--bew-text-1) !important;
}
}

View File

@@ -7,7 +7,7 @@
--at-apply: opacity-0;
}
.page-fade-leave-to {
--at-apply: hidden
--at-apply: hidden;
}
.fade-enter-active,
@@ -28,5 +28,14 @@
--at-apply: opacity-0 transform translate-y-6 transform-gpu;
}
.list-leave-to {
--at-apply: hidden
}
--at-apply: hidden;
}
.modal-enter-active,
.modal-leave-active {
--at-apply: transition-all duration-500;
}
.modal-enter-from,
.modal-leave-to {
--at-apply: opacity-0 scale-105 transform-gpu;
}

View File

@@ -75,7 +75,7 @@ export const TVAppKey = {
}
// https://github.com/magicdawn/bilibili-app-recommend/blob/e91722cc076fe65b98116fb0248236851ae6e1dc/src/utility/access-key/tv-qrcode/api.ts#L8
function tvSignSearchParams(params: Record<string, any>) {
export function tvSignSearchParams(params: Record<string, any>) {
const sign = appSign(params, TVAppKey.appkey, TVAppKey.appsec)
return new URLSearchParams({
...params,
@@ -83,6 +83,10 @@ function tvSignSearchParams(params: Record<string, any>) {
})
}
export function getTvSign(params: Record<string, any>) {
return appSign(params, TVAppKey.appkey, TVAppKey.appsec)
}
export function pollTVLoginQRCode(authCode: string): Promise<any> {
const url = 'https://passport.bilibili.com/x/passport-tv-login/qrcode/poll'