mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
feat: implement trending page
This commit is contained in:
@@ -91,6 +91,14 @@ function handleMessage(message: any) {
|
||||
.then(data => data)
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/def57d7a70ed1f39080069ba0f40648ce6ce2b90/docs/video_ranking/popular.md#%E8%8E%B7%E5%8F%96%E5%BD%93%E5%89%8D%E7%83%AD%E9%97%A8%E8%A7%86%E9%A2%91%E5%88%97%E8%A1%A8
|
||||
else if (message.contentScriptQuery === 'getPopularVideos') {
|
||||
const url = `https://api.bilibili.com/x/web-interface/popular?pn=${message.pn ?? 1}&ps=${message.ps ?? 20}`
|
||||
return fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => data)
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
}
|
||||
|
||||
function handleConnect() {
|
||||
|
||||
@@ -22,6 +22,7 @@ const props = defineProps<{
|
||||
aid?: number
|
||||
isFollowed?: boolean
|
||||
horizontal?: boolean
|
||||
tag?: string
|
||||
}>()
|
||||
|
||||
const videoUrl = computed(() => {
|
||||
@@ -64,6 +65,17 @@ function toggleWatchLater() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseMove() {
|
||||
contentVisibility.value = 'visible'
|
||||
}
|
||||
|
||||
function handelMouseLeave() {
|
||||
clearTimeout(mouseLeaveTimeOut.value)
|
||||
mouseLeaveTimeOut.value = setTimeout(() => {
|
||||
contentVisibility.value = 'auto'
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// function submitDislike(
|
||||
// reasonID: number,
|
||||
// goto: string,
|
||||
@@ -125,45 +137,12 @@ function toggleWatchLater() {
|
||||
<div
|
||||
class="video-card group"
|
||||
:class="isDislike ? 'is-dislike' : ''"
|
||||
p-20px m--20px w="[calc(100%+40px)]" pos="absolute top-0 left-0"
|
||||
rounded="$bew-radius" content-visibility-auto
|
||||
w="full" pos="absolute top-0 left-0"
|
||||
rounded="$bew-radius"
|
||||
:style="{ contentVisibility }"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseleave="handelMouseLeave"
|
||||
>
|
||||
<!-- Undo control -->
|
||||
<div :style="{ visibility: isDislike ? 'visible' : 'hidden' }" pos="absolute">
|
||||
<div
|
||||
id="dislike-control"
|
||||
pos="absolute top-0 left-0"
|
||||
w="full"
|
||||
h="auto"
|
||||
flex="~ col"
|
||||
justify="center"
|
||||
align="content-center"
|
||||
border="solid $bew-fill-1"
|
||||
text="$bew-text-3 sm center"
|
||||
rounded="$bew-radius"
|
||||
class="aspect-video"
|
||||
>
|
||||
{{ $t('home.video_removed') }}
|
||||
<!-- <button
|
||||
text="$bew-theme-color base"
|
||||
font="bold"
|
||||
m="t-4"
|
||||
@click="
|
||||
undoDislike(
|
||||
dislikeReasonId ? dislikeReasonId : 0,
|
||||
goto,
|
||||
param,
|
||||
mid,
|
||||
tid,
|
||||
tag.tag_id,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ $t('common.undo') }}
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :style="{ display: horizontal ? 'flex' : 'block', gap: horizontal ? '1.5rem' : '0' }">
|
||||
<!-- Cover -->
|
||||
<div
|
||||
@@ -178,7 +157,7 @@ function toggleWatchLater() {
|
||||
transition="all ease-in-out 300" group-hover:z-2
|
||||
@click.stop="openLinkToNewTab(videoUrl)"
|
||||
>
|
||||
<!-- Video duration -->
|
||||
<!-- Video Duration -->
|
||||
<div
|
||||
v-if="duration || durationStr"
|
||||
pos="absolute bottom-0 right-0"
|
||||
@@ -347,11 +326,10 @@ function toggleWatchLater() {
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Video Description -->
|
||||
<div
|
||||
v-if="desc"
|
||||
mt-2 text="base $bew-text-2" w-full max-h-12 overflow-y-scroll
|
||||
mt-2 text="base $bew-text-3" w-full max-h-12 overflow-y-scroll
|
||||
style="white-space: pre-line;"
|
||||
>
|
||||
{{ desc }}
|
||||
@@ -370,6 +348,10 @@ function toggleWatchLater() {
|
||||
<br>
|
||||
</template>
|
||||
|
||||
<!-- Tag -->
|
||||
<span v-if="tag" text="sm $bew-theme-color" p="x-2 y-1" rounded="$bew-radius" bg="$bew-theme-color-20" w-fit m="t-2 r-2">
|
||||
{{ tag }}
|
||||
</span>
|
||||
<!-- Capsule -->
|
||||
<span v-if="publishedTimestamp || capsuleText" text="$bew-text-3 sm" inline-block mt-2 p="x-2 y-1" bg="$bew-fill-1" rounded-4>
|
||||
{{ publishedTimestamp ? calcTimeSince(publishedTimestamp * 1000) : capsuleText }}
|
||||
@@ -381,36 +363,69 @@ function toggleWatchLater() {
|
||||
</div>
|
||||
|
||||
<!-- skeleton -->
|
||||
<div
|
||||
:style="{ display: horizontal ? 'flex' : 'block' }"
|
||||
mb-10 pointer-events-none select-none invisible
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div :style="{ width: horizontal ? '250px' : '100%' }" aspect-video bg="$bew-fill-4" rounded="$bew-radius" />
|
||||
<!-- Other Information -->
|
||||
<div flex mt-4>
|
||||
<template v-if="!horizontal">
|
||||
<div
|
||||
block
|
||||
mb-10 pointer-events-none select-none invisible
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div w-full shrink-0 aspect-video h-fit rounded="$bew-radius" />
|
||||
<!-- Other Information -->
|
||||
<div
|
||||
:style="{
|
||||
width: horizontal ? '100%' : 'unset',
|
||||
marginTop: horizontal ? '0' : '1rem'
|
||||
}"
|
||||
w="40px" h="40px" rounded="1/2" bg="$bew-fill-4" shrink-0
|
||||
/>
|
||||
<div w-full>
|
||||
<div grid gap-2>
|
||||
<div w-full h-5 bg="$bew-fill-3" />
|
||||
<div w="3/4" h-5 bg="$bew-fill-3" />
|
||||
</div>
|
||||
<div grid gap-2 mt-4>
|
||||
<div w="50%" h-4 bg="$bew-fill-2" />
|
||||
<div w="80%" h-4 bg="$bew-fill-2" />
|
||||
</div>
|
||||
<div text="transparent sm" inline-block mt-4 p="x-2 y-1" bg="$bew-fill-1" rounded-4>
|
||||
hello world
|
||||
mt-4
|
||||
flex="~ gap-4"
|
||||
>
|
||||
<div
|
||||
block
|
||||
w="40px" h="40px" rounded="1/2" shrink-0
|
||||
/>
|
||||
<div w-full>
|
||||
<div grid gap-2>
|
||||
<div w-full h-5 />
|
||||
<div w="3/4" h-5 />
|
||||
</div>
|
||||
<div grid gap-2 mt-4>
|
||||
<div w="50%" h-4 />
|
||||
<div w="80%" h-4 />
|
||||
</div>
|
||||
<div mt-4 flex>
|
||||
<div text="transparent sm" inline-block p="x-2 y-1" rounded-4>
|
||||
hello world
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
flex="~ gap-6"
|
||||
mb-10 pointer-events-none select-none invisible
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div w-250px shrink-0 aspect-video h-fit rounded="$bew-radius" />
|
||||
<!-- Other Information -->
|
||||
<div
|
||||
w-full
|
||||
flex="~ gap-4"
|
||||
>
|
||||
<div w-full>
|
||||
<div grid gap-2>
|
||||
<div w-full h-5 />
|
||||
<div w="3/4" h-5 />
|
||||
</div>
|
||||
<div grid gap-2 mt-4>
|
||||
<div w="70%" h-4 />
|
||||
</div>
|
||||
<div mt-4 flex>
|
||||
<div text="transparent sm" inline-block p="x-2 y-1" rounded-4>
|
||||
hello world
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { MomentModel } from '../types'
|
||||
import type { PopularVideoModel } from '../types'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const videoList = reactive<MomentModel[]>([])
|
||||
const videoList = reactive<PopularVideoModel[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
const needToLoginFirst = ref<boolean>(false)
|
||||
const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
const offset = ref<string>('')
|
||||
const updateBaseline = ref<string>('')
|
||||
const pn = ref<number>(1)
|
||||
|
||||
onMounted(async () => {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowingAuthorVideos()
|
||||
await getFollowingAuthorVideos()
|
||||
|
||||
emitter.off('reachBottom')
|
||||
emitter.on('reachBottom', async () => {
|
||||
if (!isLoading.value) {
|
||||
for (let i = 0; i < 3; i++)
|
||||
await getFollowingAuthorVideos()
|
||||
}
|
||||
if (!isLoading.value)
|
||||
await getFollowingAuthorVideos()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -31,19 +26,15 @@ async function getFollowingAuthorVideos() {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response = await browser.runtime.sendMessage({
|
||||
contentScriptQuery: 'getMoments',
|
||||
type: 'video',
|
||||
offset: offset.value,
|
||||
updateBaseline: updateBaseline.value,
|
||||
contentScriptQuery: 'getPopularVideos',
|
||||
pn: pn.value++,
|
||||
ps: 30,
|
||||
})
|
||||
|
||||
if (response.code === 0) {
|
||||
offset.value = response.data.offset
|
||||
updateBaseline.value = response.data.update_baseline
|
||||
if (response.code === 0 && !response.data.no_more) {
|
||||
const resData = [] as PopularVideoModel[]
|
||||
|
||||
const resData = [] as MomentModel[]
|
||||
|
||||
response.data.items.forEach((item: MomentModel) => {
|
||||
response.data.list.forEach((item: PopularVideoModel) => {
|
||||
resData.push(item)
|
||||
})
|
||||
|
||||
@@ -56,9 +47,6 @@ async function getFollowingAuthorVideos() {
|
||||
Object.assign(videoList, videoList.concat(resData))
|
||||
}
|
||||
}
|
||||
else if (response.code === -101) {
|
||||
needToLoginFirst.value = true
|
||||
}
|
||||
}
|
||||
finally {
|
||||
isLoading.value = false
|
||||
@@ -71,49 +59,71 @@ async function getFollowingAuthorVideos() {
|
||||
<div
|
||||
ref="containerRef"
|
||||
m="b-0 t-0" relative w-full h-full
|
||||
grid="~ cols-1 xl:cols-2 gap-4"
|
||||
>
|
||||
<VideoCard
|
||||
v-for="video in videoList"
|
||||
:id="Number(video.modules.module_dynamic.major.archive.aid)"
|
||||
:key="video.modules.module_dynamic.major.archive.aid"
|
||||
:duration-str="video.modules.module_dynamic.major.archive.duration_text"
|
||||
:title="video.modules.module_dynamic.major.archive.title"
|
||||
:desc="video.modules.module_dynamic.major.archive.desc"
|
||||
:cover="video.modules.module_dynamic.major.archive.cover"
|
||||
:author="video.modules.module_author.name"
|
||||
:author-face="video.modules.module_author.face"
|
||||
:mid="video.modules.module_author.mid"
|
||||
:view-str="video.modules.module_dynamic.major.archive.stat.play"
|
||||
:danmaku-str="video.modules.module_dynamic.major.archive.stat.danmaku"
|
||||
:capsule-text="video.modules.module_author.pub_time"
|
||||
:bvid="video.modules.module_dynamic.major.archive.bvid"
|
||||
:id="Number(video.aid)"
|
||||
:key="video.aid"
|
||||
:duration="video.duration"
|
||||
:title="video.title"
|
||||
:desc="video.desc"
|
||||
:cover="video.pic"
|
||||
:author="video.owner.name"
|
||||
:author-face="video.owner.face"
|
||||
:mid="video.owner.mid"
|
||||
:view="video.stat.view"
|
||||
:danmaku="video.stat.danmaku"
|
||||
:published-timestamp="video.pubdate"
|
||||
:bvid="video.bvid"
|
||||
:tag="video.rcmd_reason.content"
|
||||
horizontal
|
||||
w-full
|
||||
/>
|
||||
|
||||
<!-- skeleton -->
|
||||
<template v-for="item in 30" :key="item">
|
||||
<div
|
||||
v-if="isLoading"
|
||||
mb-8 pointer-events-none select-none
|
||||
>
|
||||
<div aspect-video bg="$bew-fill-4" rounded="$bew-radius" />
|
||||
<div flex mt-4>
|
||||
<div m="r-4" w="40px" h="40px" rounded="1/2" bg="$bew-fill-4" shrink-0 />
|
||||
<div w-full>
|
||||
<div grid gap-2>
|
||||
<div w-full h-5 bg="$bew-fill-3" />
|
||||
<div w="3/4" h-5 bg="$bew-fill-3" />
|
||||
</div>
|
||||
<div grid gap-2 mt-4>
|
||||
<div w="50%" h-4 bg="$bew-fill-2" />
|
||||
<div w="80%" h-4 bg="$bew-fill-2" />
|
||||
</div>
|
||||
<div text="transparent sm" inline-block mt-4 p="x-2 y-1" bg="$bew-fill-1" rounded-4>
|
||||
hello world
|
||||
<template v-if="isLoading">
|
||||
<template v-for="item in 30" :key="item">
|
||||
<div
|
||||
|
||||
:style="{
|
||||
display: 'flex',
|
||||
gap: '1.5rem',
|
||||
}"
|
||||
mb-10 pointer-events-none select-none
|
||||
>
|
||||
<!-- Cover -->
|
||||
<div :style="{ width: '250px' }" shrink-0 aspect-video h-fit bg="$bew-fill-4" rounded="$bew-radius" />
|
||||
<!-- Other Information -->
|
||||
<div
|
||||
:style="{
|
||||
width: '100%',
|
||||
marginTop: '0'
|
||||
}"
|
||||
flex="~ gap-4"
|
||||
>
|
||||
<div w-full>
|
||||
<div grid gap-2>
|
||||
<div w-full h-5 bg="$bew-fill-3" />
|
||||
<div w="3/4" h-5 bg="$bew-fill-3" />
|
||||
</div>
|
||||
<div grid gap-2 mt-4>
|
||||
<div :style="{ width: '70%' }" h-4 bg="$bew-fill-2" />
|
||||
<div w="80%" h-4 bg="$bew-fill-2" />
|
||||
</div>
|
||||
|
||||
<div mt-4 flex>
|
||||
<div v-if="item % 2 === 0" mr-2 text="transparent sm" inline-block p="x-2 y-1" bg="$bew-fill-1" rounded-4>
|
||||
hello world
|
||||
</div>
|
||||
<div text="transparent sm" inline-block p="x-2 y-1" bg="$bew-fill-1" rounded-4>
|
||||
hello world
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -256,3 +256,57 @@ export interface MomentModel {
|
||||
visible: boolean
|
||||
|
||||
}
|
||||
|
||||
export interface PopularVideoModel {
|
||||
aid: number
|
||||
videos: number
|
||||
tid: number
|
||||
tname: string
|
||||
copyright: number
|
||||
pic: string
|
||||
title: string
|
||||
pubdate: number
|
||||
ctime: number
|
||||
desc: string
|
||||
state: number
|
||||
duration: number
|
||||
mission_id: number
|
||||
owner: {
|
||||
mid: number
|
||||
name: string
|
||||
face: string
|
||||
}
|
||||
stat: {
|
||||
aid: number
|
||||
view: number
|
||||
danmaku: number
|
||||
reply: number
|
||||
favorite: number
|
||||
coin: number
|
||||
share: number
|
||||
now_rank: number
|
||||
his_rank: number
|
||||
like: number
|
||||
dislike: number
|
||||
vt: number
|
||||
vv: number
|
||||
}
|
||||
dynamic: string
|
||||
cid: number
|
||||
dimension: {
|
||||
width: number
|
||||
height: number
|
||||
rotate: number
|
||||
}
|
||||
short_link_v2: string
|
||||
up_from_v2: number
|
||||
first_frame: string
|
||||
bvid: string
|
||||
season_type: number
|
||||
is_ogv: boolean
|
||||
enable_vt: number
|
||||
rcmd_reason: {
|
||||
content: string
|
||||
corner_mark: number
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user