mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
feat: SubscribedSeries page
This commit is contained in:
@@ -53,7 +53,7 @@ function handleMessage(message: any) {
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
|
||||
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/17b7cb85cef19d7f2e94f8d896e68413f6217e26/docs/dynamic/all.md#%E8%8E%B7%E5%8F%96%E5%8A%A8%E6%80%81%E5%88%97%E8%A1%A8
|
||||
// https://socialsisteryi.github.io/bilibili-API-collect/docs/dynamic/all.html#%E8%8E%B7%E5%8F%96%E5%8A%A8%E6%80%81%E5%88%97%E8%A1%A8
|
||||
else if (message.contentScriptQuery === 'getMoments') {
|
||||
const url = `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=${message.type}&offset=${message.offset}&update_baseline=${message.updateBaseline}`
|
||||
return fetch(url)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getCSRF, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime, calcTimeSince, numFormatter } from '~/utils/dataFormatter'
|
||||
|
||||
const props = defineProps<{
|
||||
interface Props {
|
||||
id: number
|
||||
duration?: number
|
||||
durationStr?: string
|
||||
@@ -20,14 +20,27 @@ const props = defineProps<{
|
||||
capsuleText?: string
|
||||
bvid?: string
|
||||
aid?: number
|
||||
epid?: number
|
||||
isFollowed?: boolean
|
||||
horizontal?: boolean
|
||||
tag?: string
|
||||
rank?: number
|
||||
}>()
|
||||
topRightContent?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(),
|
||||
{
|
||||
topRightContent: true,
|
||||
},
|
||||
)
|
||||
|
||||
const videoUrl = computed(() => {
|
||||
return `https://www.bilibili.com/video/${props.bvid ?? `av${props.aid}`}`
|
||||
if (props.bvid || props.aid)
|
||||
return `https://www.bilibili.com/video/${props.bvid ?? `av${props.aid}`}`
|
||||
else if (props.epid)
|
||||
return `https://www.bilibili.com/bangumi/play/ep${props.epid}`
|
||||
else
|
||||
return ''
|
||||
})
|
||||
|
||||
const isDislike = ref<boolean>(false)
|
||||
@@ -197,6 +210,7 @@ function handelMouseLeave() {
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="topRightContent"
|
||||
pos="absolute top-0 right-0" z="2"
|
||||
p="x-2 y-1" m="1"
|
||||
rounded="$bew-radius"
|
||||
@@ -230,7 +244,7 @@ function handelMouseLeave() {
|
||||
<div
|
||||
:style="{
|
||||
width: horizontal ? '100%' : 'unset',
|
||||
marginTop: horizontal ? '0' : '1rem'
|
||||
marginTop: horizontal ? '0' : '1rem',
|
||||
}"
|
||||
flex="~"
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@ import ForYou from './components/ForYou.vue'
|
||||
import Following from './components/Following.vue'
|
||||
import Trending from './components/Trending.vue'
|
||||
import Ranking from './components/Ranking.vue'
|
||||
import Pgc from './components/Pgc.vue'
|
||||
import SubscribedSeries from './components/SubscribedSeries.vue'
|
||||
import type { HomeTab } from './types'
|
||||
import { HomeSubPage } from './types'
|
||||
import emitter from '~/utils/mitt'
|
||||
@@ -16,7 +16,7 @@ const handleBackToTop = inject('handleBackToTop') as (targetScrollTop: number) =
|
||||
|
||||
const recommendContentKey = ref<string>(`recommendContent${Number(new Date())}`)
|
||||
const activatedPage = ref<HomeSubPage>(HomeSubPage.ForYou)
|
||||
const pages = { ForYou, Following, Pgc, Trending, Ranking }
|
||||
const pages = { ForYou, Following, SubscribedSeries, Trending, Ranking }
|
||||
const showSearchPageMode = ref<boolean>(false)
|
||||
const shouldMoveTabsUp = ref<boolean>(false)
|
||||
|
||||
@@ -31,8 +31,8 @@ const tabs = computed((): HomeTab[] => {
|
||||
value: HomeSubPage.Following,
|
||||
},
|
||||
{
|
||||
label: 'Following Anime, Shows & Movies',
|
||||
value: HomeSubPage.Pgc,
|
||||
label: 'Subscribed Series',
|
||||
value: HomeSubPage.SubscribedSeries,
|
||||
},
|
||||
{
|
||||
label: t('home.trending'),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { MomentModel } from '../types'
|
||||
import type { DataItem as MomentItem, MomentResultModel } from '~/models/moment/momentResult'
|
||||
import emitter from '~/utils/mitt'
|
||||
|
||||
const videoList = reactive<MomentModel[]>([])
|
||||
const momentList = reactive<MomentItem[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
const needToLoginFirst = ref<boolean>(false)
|
||||
const containerRef = ref<HTMLElement>() as Ref<HTMLElement>
|
||||
@@ -34,7 +34,7 @@ async function getFollowedUsersVideos() {
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response = await browser.runtime.sendMessage({
|
||||
const response: MomentResultModel = await browser.runtime.sendMessage({
|
||||
contentScriptQuery: 'getMoments',
|
||||
type: 'pgc',
|
||||
offset: offset.value,
|
||||
@@ -51,19 +51,19 @@ async function getFollowedUsersVideos() {
|
||||
offset.value = response.data.offset
|
||||
updateBaseline.value = response.data.update_baseline
|
||||
|
||||
const resData = [] as MomentModel[]
|
||||
const resData = [] as MomentItem[]
|
||||
|
||||
response.data.items.forEach((item: MomentModel) => {
|
||||
response.data.items.forEach((item: MomentItem) => {
|
||||
resData.push(item)
|
||||
})
|
||||
|
||||
// when videoList has length property, it means it is the first time to load
|
||||
if (!videoList.length) {
|
||||
Object.assign(videoList, resData)
|
||||
if (!momentList.length) {
|
||||
Object.assign(momentList, resData)
|
||||
}
|
||||
else {
|
||||
// else we concat the new data to the old data
|
||||
Object.assign(videoList, videoList.concat(resData))
|
||||
Object.assign(momentList, momentList.concat(resData))
|
||||
}
|
||||
}
|
||||
else if (response.code === -101) {
|
||||
@@ -94,19 +94,19 @@ function jumpToLoginPage() {
|
||||
grid="~ 2xl:cols-5 xl:cols-4 lg:cols-3 md:cols-2 gap-5"
|
||||
>
|
||||
<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"
|
||||
: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"
|
||||
v-for="moment in momentList"
|
||||
:id="moment.modules.module_author.mid"
|
||||
:key="moment.modules.module_author.mid"
|
||||
:top-right-content="false"
|
||||
:title="`${moment.modules.module_dynamic.major.pgc?.title}`"
|
||||
:cover="`${moment.modules.module_dynamic.major.pgc?.cover}`"
|
||||
:author="moment.modules.module_author.name"
|
||||
:author-face="moment.modules.module_author.face"
|
||||
:mid="moment.modules.module_author.mid"
|
||||
:view-str="moment.modules.module_dynamic.major.pgc?.stat.play"
|
||||
:danmaku-str="moment.modules.module_dynamic.major.pgc?.stat.danmaku"
|
||||
:capsule-text="moment.modules.module_author.pub_time"
|
||||
:epid="moment.modules.module_dynamic.major.pgc?.epid"
|
||||
/>
|
||||
|
||||
<!-- skeleton -->
|
||||
@@ -6,7 +6,7 @@ export interface HomeTab {
|
||||
export enum HomeSubPage {
|
||||
ForYou = 'ForYou',
|
||||
Following = 'Following',
|
||||
Pgc = 'Pgc',
|
||||
SubscribedSeries = 'SubscribedSeries',
|
||||
Trending = 'Trending',
|
||||
Ranking = 'Ranking',
|
||||
}
|
||||
|
||||
452
src/models/moment/momentResult.ts
Normal file
452
src/models/moment/momentResult.ts
Normal file
@@ -0,0 +1,452 @@
|
||||
// https://app.quicktype.io/?l=ts
|
||||
|
||||
export interface MomentResultModel {
|
||||
code: number
|
||||
message: string
|
||||
ttl: number
|
||||
data: Data
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
has_more: boolean
|
||||
items: DataItem[]
|
||||
offset: string
|
||||
update_baseline: string
|
||||
update_num: number
|
||||
}
|
||||
|
||||
export interface DataItem {
|
||||
basic: Basic
|
||||
id_str: string
|
||||
modules: Modules
|
||||
type: ItemType
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
export interface Basic {
|
||||
comment_id_str: string
|
||||
comment_type: number
|
||||
like_icon: LikeIcon
|
||||
rid_str: string
|
||||
}
|
||||
|
||||
export interface LikeIcon {
|
||||
action_url: string
|
||||
end_url: string
|
||||
id: number
|
||||
start_url: string
|
||||
}
|
||||
|
||||
export interface Modules {
|
||||
module_author: ModuleAuthor
|
||||
module_dynamic: ModuleDynamic
|
||||
module_more: ModuleMore
|
||||
module_stat: ModuleStat
|
||||
module_interaction?: ModuleInteraction
|
||||
}
|
||||
|
||||
export interface ModuleAuthor {
|
||||
avatar?: Avatar
|
||||
face: string
|
||||
face_nft: boolean
|
||||
following: boolean
|
||||
jump_url: string
|
||||
label: ModuleAuthorLabel
|
||||
mid: number
|
||||
name: string
|
||||
official_verify?: OfficialVerify
|
||||
pendant?: Pendant
|
||||
pub_action: PubAction
|
||||
pub_location_text?: string
|
||||
pub_time: string
|
||||
pub_ts: number
|
||||
type: ModuleAuthorType
|
||||
vip?: Vip
|
||||
decorate?: Decorate
|
||||
}
|
||||
|
||||
export interface Avatar {
|
||||
container_size: ContainerSize
|
||||
fallback_layers: FallbackLayers
|
||||
mid: string
|
||||
layers?: AvatarLayer[]
|
||||
}
|
||||
|
||||
export interface ContainerSize {
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
|
||||
export interface FallbackLayers {
|
||||
is_critical_group: boolean
|
||||
layers: FallbackLayersLayer[]
|
||||
}
|
||||
|
||||
export interface FallbackLayersLayer {
|
||||
general_spec: GeneralSpec
|
||||
layer_config: LayerConfig
|
||||
resource: PurpleResource
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
export interface GeneralSpec {
|
||||
pos_spec: PosSpec
|
||||
render_spec: RenderSpec
|
||||
size_spec: ContainerSize
|
||||
}
|
||||
|
||||
export interface PosSpec {
|
||||
axis_x: number
|
||||
axis_y: number
|
||||
coordinate_pos: number
|
||||
}
|
||||
|
||||
export interface RenderSpec {
|
||||
opacity: number
|
||||
}
|
||||
|
||||
export interface LayerConfig {
|
||||
is_critical?: boolean
|
||||
tags: Tags
|
||||
}
|
||||
|
||||
export interface Tags {
|
||||
AVATAR_LAYER?: Layer
|
||||
GENERAL_CFG?: GeneralCFG
|
||||
ICON_LAYER?: Layer
|
||||
PENDENT_LAYER?: Layer
|
||||
}
|
||||
|
||||
export interface Layer {
|
||||
}
|
||||
|
||||
export interface GeneralCFG {
|
||||
config_type: number
|
||||
general_config: GeneralConfig
|
||||
}
|
||||
|
||||
export interface GeneralConfig {
|
||||
web_css_style: WebCSSStyle
|
||||
}
|
||||
|
||||
export interface WebCSSStyle {
|
||||
borderRadius: BorderRadius
|
||||
'background-color'?: BackgroundColor
|
||||
border?: Border
|
||||
boxSizing?: BoxSizing
|
||||
}
|
||||
|
||||
export enum BackgroundColor {
|
||||
RGB255255255 = 'rgb(255,255,255)',
|
||||
}
|
||||
|
||||
export enum Border {
|
||||
The2PxSolidRGBA2552552551 = '2px solid rgba(255,255,255,1)',
|
||||
}
|
||||
|
||||
export enum BorderRadius {
|
||||
The50 = '50%',
|
||||
}
|
||||
|
||||
export enum BoxSizing {
|
||||
BorderBox = 'border-box',
|
||||
}
|
||||
|
||||
export interface PurpleResource {
|
||||
res_image: ResImage
|
||||
res_type: number
|
||||
}
|
||||
|
||||
export interface ResImage {
|
||||
image_src: ImageSrc
|
||||
}
|
||||
|
||||
export interface ImageSrc {
|
||||
placeholder?: number
|
||||
remote?: Remote
|
||||
src_type: number
|
||||
local?: number
|
||||
}
|
||||
|
||||
export interface Remote {
|
||||
bfs_style: BFSStyle
|
||||
url: string
|
||||
}
|
||||
|
||||
export enum BFSStyle {
|
||||
WidgetLayerAvatar = 'widget-layer-avatar',
|
||||
}
|
||||
|
||||
export interface AvatarLayer {
|
||||
is_critical_group?: boolean
|
||||
layers: LayerLayer[]
|
||||
}
|
||||
|
||||
export interface LayerLayer {
|
||||
general_spec: GeneralSpec
|
||||
layer_config: LayerConfig
|
||||
resource: FluffyResource
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
export interface FluffyResource {
|
||||
res_image?: ResImage
|
||||
res_type: number
|
||||
res_animation?: ResAnimation
|
||||
}
|
||||
|
||||
export interface ResAnimation {
|
||||
webp_src: WebpSrc
|
||||
}
|
||||
|
||||
export interface WebpSrc {
|
||||
remote: Remote
|
||||
src_type: number
|
||||
}
|
||||
|
||||
export interface Decorate {
|
||||
card_url: string
|
||||
fan: Fan
|
||||
id: number
|
||||
jump_url: string
|
||||
name: string
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface Fan {
|
||||
color: string
|
||||
is_fan: boolean
|
||||
num_str: string
|
||||
number: number
|
||||
}
|
||||
|
||||
export enum ModuleAuthorLabel {
|
||||
Empty = '',
|
||||
番剧 = '番剧',
|
||||
}
|
||||
|
||||
export interface OfficialVerify {
|
||||
desc: string
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface Pendant {
|
||||
expire: number
|
||||
image: string
|
||||
image_enhance: string
|
||||
image_enhance_frame: string
|
||||
n_pid: number
|
||||
name: Name
|
||||
pid: number
|
||||
}
|
||||
|
||||
export enum Name {
|
||||
Empty = '',
|
||||
EveOneCat2 = 'EveOneCat2',
|
||||
}
|
||||
|
||||
export enum PubAction {
|
||||
投稿了视频 = '投稿了视频',
|
||||
更新了 = '更新了',
|
||||
}
|
||||
|
||||
export enum ModuleAuthorType {
|
||||
AuthorTypeNormal = 'AUTHOR_TYPE_NORMAL',
|
||||
AuthorTypePgc = 'AUTHOR_TYPE_PGC',
|
||||
}
|
||||
|
||||
export interface Vip {
|
||||
avatar_subscript: number
|
||||
avatar_subscript_url: string
|
||||
due_date: number
|
||||
label: LabelClass
|
||||
nickname_color: Color
|
||||
status: number
|
||||
theme_type: number
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface LabelClass {
|
||||
bg_color: Color
|
||||
bg_style: number
|
||||
border_color: string
|
||||
img_label_uri_hans: string
|
||||
img_label_uri_hans_static: string
|
||||
img_label_uri_hant: string
|
||||
img_label_uri_hant_static: string
|
||||
label_theme: LabelTheme
|
||||
path: string
|
||||
text: LabelText
|
||||
text_color: TextColorEnum
|
||||
use_img_label: boolean
|
||||
}
|
||||
|
||||
export enum Color {
|
||||
Empty = '',
|
||||
Fb7299 = '#FB7299',
|
||||
}
|
||||
|
||||
export enum LabelTheme {
|
||||
AnnualVip = 'annual_vip',
|
||||
Empty = '',
|
||||
TenAnnualVip = 'ten_annual_vip',
|
||||
}
|
||||
|
||||
export enum LabelText {
|
||||
Empty = '',
|
||||
十年大会员 = '十年大会员',
|
||||
年度大会员 = '年度大会员',
|
||||
}
|
||||
|
||||
export enum TextColorEnum {
|
||||
Empty = '',
|
||||
Ffffff = '#FFFFFF',
|
||||
}
|
||||
|
||||
export interface ModuleDynamic {
|
||||
additional: null
|
||||
desc: ModuleDynamicDesc | null
|
||||
major: Major
|
||||
topic: Topic | null
|
||||
}
|
||||
|
||||
export interface ModuleDynamicDesc {
|
||||
rich_text_nodes: PurpleRichTextNode[]
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface PurpleRichTextNode {
|
||||
orig_text: string
|
||||
text: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface Major {
|
||||
archive?: Archive
|
||||
type: MajorType
|
||||
pgc?: Pgc
|
||||
}
|
||||
|
||||
export interface Archive {
|
||||
aid: string
|
||||
badge: Badge
|
||||
bvid: string
|
||||
cover: string
|
||||
desc: string
|
||||
disable_preview: number
|
||||
duration_text: string
|
||||
jump_url: string
|
||||
stat: Stat
|
||||
title: string
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface Badge {
|
||||
bg_color: Color
|
||||
color: TextColorEnum
|
||||
icon_url?: null
|
||||
text: BadgeText
|
||||
}
|
||||
|
||||
export enum BadgeText {
|
||||
投稿视频 = '投稿视频',
|
||||
番剧 = '番剧',
|
||||
}
|
||||
|
||||
export interface Stat {
|
||||
danmaku: string
|
||||
play: string
|
||||
}
|
||||
|
||||
export interface Pgc {
|
||||
badge: Badge
|
||||
cover: string
|
||||
epid: number
|
||||
jump_url: string
|
||||
season_id: number
|
||||
stat: Stat
|
||||
sub_type: number
|
||||
title: string
|
||||
type: number
|
||||
}
|
||||
|
||||
export enum MajorType {
|
||||
MajorTypeArchive = 'MAJOR_TYPE_ARCHIVE',
|
||||
MajorTypePgc = 'MAJOR_TYPE_PGC',
|
||||
}
|
||||
|
||||
export interface Topic {
|
||||
id: number
|
||||
jump_url: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface ModuleInteraction {
|
||||
items: ModuleInteractionItem[]
|
||||
}
|
||||
|
||||
export interface ModuleInteractionItem {
|
||||
desc: ItemDesc
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface ItemDesc {
|
||||
rich_text_nodes: FluffyRichTextNode[]
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface FluffyRichTextNode {
|
||||
orig_text: string
|
||||
rid?: string
|
||||
text: string
|
||||
type: string
|
||||
emoji?: Emoji
|
||||
}
|
||||
|
||||
export interface Emoji {
|
||||
icon_url: string
|
||||
size: number
|
||||
text: string
|
||||
type: number
|
||||
}
|
||||
|
||||
export interface ModuleMore {
|
||||
three_point_items: ThreePointItem[]
|
||||
}
|
||||
|
||||
export interface ThreePointItem {
|
||||
label: ThreePointItemLabel
|
||||
type: ThreePointItemType
|
||||
}
|
||||
|
||||
export enum ThreePointItemLabel {
|
||||
举报 = '举报',
|
||||
取消关注 = '取消关注',
|
||||
}
|
||||
|
||||
export enum ThreePointItemType {
|
||||
ThreePointFollowing = 'THREE_POINT_FOLLOWING',
|
||||
ThreePointReport = 'THREE_POINT_REPORT',
|
||||
}
|
||||
|
||||
export interface ModuleStat {
|
||||
comment: Comment
|
||||
forward: Comment
|
||||
like: Like
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
count: number
|
||||
forbidden: boolean
|
||||
}
|
||||
|
||||
export interface Like {
|
||||
count: number
|
||||
forbidden: boolean
|
||||
status: boolean
|
||||
}
|
||||
|
||||
export enum ItemType {
|
||||
DynamicTypeAV = 'DYNAMIC_TYPE_AV',
|
||||
DynamicTypePgcUnion = 'DYNAMIC_TYPE_PGC_UNION',
|
||||
}
|
||||
Reference in New Issue
Block a user