mirror of
https://github.com/BewlyBewly/BewlyBewly.git
synced 2025-04-14 13:15:29 +00:00
refactor: move api response types to ~/models/apiModels
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
// import PopularAnimeCarousel from './components/PopularAnimeCarousel.vue'
|
||||
import AnimeTimeTable from './components/AnimeTimeTable.vue'
|
||||
import type { AnimeItem, PopularAnime } from './types'
|
||||
import { getUserID, openLinkToNewTab } from '~/utils/main'
|
||||
import { numFormatter } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { List as WatchListItem, WatchListResult } from '~/models/apiModels/anime/watchList'
|
||||
import type { List as PopularAnimeItem, PopularAnimeResult } from '~/models/apiModels/anime/popular'
|
||||
import type { ItemSubItem as RecommendationItem, RecommendationResult } from '~/models/apiModels/anime/recommendation'
|
||||
|
||||
const animeWatchList = reactive<AnimeItem[]>([])
|
||||
const recommendAnimeList = reactive<AnimeItem[]>([])
|
||||
const popularAnimeList = reactive<AnimeItem[]>([])
|
||||
const animeWatchList = reactive<WatchListItem[]>([])
|
||||
const recommendAnimeList = reactive<RecommendationItem[]>([])
|
||||
const popularAnimeList = reactive<PopularAnimeItem[]>([])
|
||||
const cursor = ref<number>(0)
|
||||
const isLoadingAnimeWatchList = ref<boolean>()
|
||||
const isLoadingPopularAnime = ref<boolean>()
|
||||
@@ -40,13 +41,13 @@ function getAnimeWatchList() {
|
||||
pn: 1,
|
||||
ps: 30,
|
||||
})
|
||||
.then((response) => {
|
||||
.then((response: WatchListResult) => {
|
||||
const {
|
||||
code,
|
||||
data: { list },
|
||||
} = response
|
||||
if (code === 0)
|
||||
Object.assign(animeWatchList, list as AnimeItem[])
|
||||
Object.assign(animeWatchList, list as WatchListItem[])
|
||||
})
|
||||
.catch(() => Object.assign(animeWatchList, []))
|
||||
.finally(() => {
|
||||
@@ -61,14 +62,14 @@ function getRecommendAnimeList() {
|
||||
contentScriptQuery: 'getRecommendAnimeList',
|
||||
coursor: cursor.value,
|
||||
})
|
||||
.then((response) => {
|
||||
.then((response: RecommendationResult) => {
|
||||
const {
|
||||
code,
|
||||
data: { items, coursor, has_next },
|
||||
} = response
|
||||
if (code === 0 && has_next) {
|
||||
if (recommendAnimeList.length === 0)
|
||||
Object.assign(recommendAnimeList, items[0].sub_items as AnimeItem[])
|
||||
Object.assign(recommendAnimeList, items[0].sub_items as RecommendationItem[])
|
||||
else recommendAnimeList.push(...items[0].sub_items)
|
||||
|
||||
cursor.value = coursor
|
||||
@@ -85,13 +86,13 @@ function getPopularAnimeList() {
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getPopularAnimeList',
|
||||
})
|
||||
.then((response) => {
|
||||
.then((response: PopularAnimeResult) => {
|
||||
const {
|
||||
code,
|
||||
result: { list },
|
||||
} = response
|
||||
if (code === 0)
|
||||
Object.assign(popularAnimeList, list as PopularAnime[])
|
||||
Object.assign(popularAnimeList, list as PopularAnimeItem[])
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => isLoadingPopularAnime.value = false)
|
||||
@@ -255,3 +256,4 @@ function getPopularAnimeList() {
|
||||
--at-apply: mb-8 mt-14 first:mt-0;
|
||||
}
|
||||
</style>
|
||||
~/models/apiModels/anime/watchList
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import browser from 'webextension-polyfill'
|
||||
import type { AnimeTimeTableItem } from '../types'
|
||||
import { removeHttpFromUrl } from '~/utils/main'
|
||||
import type { Result as TimetableItem, TimetableResult } from '~/models/apiModels/anime/timetable'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -31,10 +32,10 @@ function getAnimeTimeTable() {
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getAnimeTimeTable',
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: TimetableResult) => {
|
||||
const { code, result } = res
|
||||
if (code === 0)
|
||||
Object.assign(animeTimeTable, result as AnimeTimeTableItem[])
|
||||
Object.assign(animeTimeTable, result as TimetableItem[])
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopularAnime } from '../types'
|
||||
|
||||
const popularAnimeList = reactive<PopularAnime[]>([])
|
||||
const activatedAnime = ref<PopularAnime>()
|
||||
const bannerContent = ref<HTMLElement>()
|
||||
const bannerHeight = ref<number>(0)
|
||||
|
||||
onload = () => {
|
||||
bannerHeight.value = bannerContent.value?.offsetHeight as number
|
||||
}
|
||||
|
||||
onresize = () => {
|
||||
bannerHeight.value = bannerContent.value?.offsetHeight as number
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getPopularAnimeList()
|
||||
})
|
||||
|
||||
function getPopularAnimeList() {
|
||||
browser.runtime
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getPopularAnimeList',
|
||||
})
|
||||
.then((response) => {
|
||||
const {
|
||||
code,
|
||||
result: { list },
|
||||
} = response
|
||||
if (code === 0) {
|
||||
Object.assign(popularAnimeList, list.slice(0, 7) as PopularAnime[])
|
||||
activatedAnime.value = popularAnimeList[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="bannerContent" w-full h-800px pos="absolute top-0 left-0" z="-1">
|
||||
<!-- banner mask -->
|
||||
<div
|
||||
pos="absolute bottom-0 left-0"
|
||||
w-full
|
||||
h-300px
|
||||
bg="gradient-to-b gradient-from-transparent gradient-to-$bew-bg"
|
||||
z-1
|
||||
/>
|
||||
<div
|
||||
:style="{
|
||||
backgroundImage: `url(${activatedAnime?.ss_horizontal_cover})`,
|
||||
}"
|
||||
bg="cover center"
|
||||
duration-600
|
||||
w-full
|
||||
h-full
|
||||
pos="absolute"
|
||||
after:content-none
|
||||
after:pos="absolute top-0 left-0"
|
||||
after:w-full
|
||||
after:h-full
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- banner -->
|
||||
<div h-800px z-1 pos="relative" flex justify-center>
|
||||
<div>
|
||||
<!-- <img
|
||||
:src="activatedAnime?.ss_horizontal_cover.replace('https:', '')"
|
||||
pointer-events-none
|
||||
rounded="$bew-radius"
|
||||
h="[calc(100%-170px)]"
|
||||
> -->
|
||||
<!-- <div text="2xl white" p-4 pos="relative" bg="black opacity-60">
|
||||
{{ activatedAnime?.title }}
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div flex="~ justify-between" m="t--350px">
|
||||
<ul
|
||||
w="1/3"
|
||||
ml-auto
|
||||
flex
|
||||
overflow-hidden
|
||||
z-1
|
||||
relative
|
||||
>
|
||||
<li
|
||||
v-for="(item, index) in popularAnimeList"
|
||||
:key="index"
|
||||
pr-2
|
||||
flex="~ 1 gap-2 shrink-0"
|
||||
@mouseover="activatedAnime = item"
|
||||
>
|
||||
<img :src="item.cover.replace('https:', '')">
|
||||
<!-- <div flex items-center>
|
||||
{{ item.title }}
|
||||
</div> -->
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,90 +0,0 @@
|
||||
export interface PopularAnime {
|
||||
badge: '独家' | '会员抢先' | '会员专享' | '出品'
|
||||
badge_info: {
|
||||
bg_color: string
|
||||
bg_color_night: string
|
||||
text: string
|
||||
}
|
||||
badge_type: number
|
||||
copyright: string
|
||||
cover: string // 豎向封面
|
||||
new_ep: {
|
||||
cover: string
|
||||
index_show: string
|
||||
}
|
||||
rank: number // 排名
|
||||
rating: string // 評分
|
||||
season_id: number
|
||||
ss_horizontal_cover: string // 橫向封面
|
||||
stat: {
|
||||
danmaku: number // 彈幕
|
||||
follow: number // 訂閲
|
||||
series_follow: number // 當前系列訂閲???
|
||||
view: number // 觀看數
|
||||
}
|
||||
title: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface AnimeItem {
|
||||
cover: string
|
||||
horizontal_cover_16_9?: string
|
||||
episode_id: number
|
||||
evaluate: string
|
||||
hover: {
|
||||
img: string
|
||||
text: string[] // 番劇風格
|
||||
}
|
||||
link: string
|
||||
url: string
|
||||
rank_id: number
|
||||
rating: string
|
||||
rating_count: number
|
||||
report: object
|
||||
season_id: number
|
||||
season_type: number
|
||||
stat: {
|
||||
danmaku: number
|
||||
duration: number
|
||||
view: number
|
||||
series_follow: number
|
||||
}
|
||||
sub_title: string
|
||||
subtitle: string
|
||||
text: string[]
|
||||
title: string
|
||||
user_status: {
|
||||
follow: number
|
||||
}
|
||||
progress: string
|
||||
is_finish: 1 | 0 // 是否已經完結
|
||||
total_count: number // 當前集數
|
||||
styles: string[] // 番劇風格
|
||||
rank: number
|
||||
}
|
||||
|
||||
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/bangumi/timeline.md#%E8%8E%B7%E5%8F%96%E7%95%AA%E5%89%A7%E6%88%96%E5%BD%B1%E8%A7%86%E6%97%B6%E9%97%B4%E7%BA%BF
|
||||
export interface AnimeTimeTableItem {
|
||||
date: string
|
||||
date_ts: number
|
||||
day_of_week: number
|
||||
episodes: Array<{
|
||||
cover: string
|
||||
delay: number
|
||||
delay_id: number
|
||||
delay_index: string
|
||||
delay_reason: string
|
||||
ep_cover: string
|
||||
episode_id: number
|
||||
follows: string
|
||||
plays: string
|
||||
pub_index: string
|
||||
pub_time: string
|
||||
pub_ts: number
|
||||
published: number
|
||||
season_id: number
|
||||
square_cover: string
|
||||
title: string
|
||||
}>
|
||||
is_today: number
|
||||
}
|
||||
@@ -4,11 +4,13 @@ import { getCSRF, getUserID, openLinkToNewTab, removeHttpFromUrl } from '~/utils
|
||||
import type { FavoriteCategory, FavoriteResource } from '~/components/Topbar/types'
|
||||
import emitter from '~/utils/mitt'
|
||||
import { settings } from '~/logic'
|
||||
import type { Media as FavoriteItem, FavoritesResult } from '~/models/apiModels/video/favorite'
|
||||
import type { List as CategoryItem, FavoritesCategoryResult } from '~/models/apiModels/video/favoriteCategory'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const favoriteCategories = reactive<Array<FavoriteCategory>>([])
|
||||
const favoriteResources = reactive<Array<FavoriteResource>>([])
|
||||
const favoriteCategories = reactive<CategoryItem[]>([])
|
||||
const favoriteResources = reactive<FavoriteItem[]>([])
|
||||
const categoryOptions = reactive<Array<{ value: any; label: string }>>([])
|
||||
|
||||
const selectedCategory = ref<FavoriteCategory>()
|
||||
@@ -69,7 +71,7 @@ async function getFavoriteCategories() {
|
||||
contentScriptQuery: 'getFavoriteCategories',
|
||||
mid: getUserID(),
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: FavoritesCategoryResult) => {
|
||||
if (res.code === 0) {
|
||||
Object.assign(favoriteCategories, res.data.list)
|
||||
|
||||
@@ -99,7 +101,7 @@ async function getFavoriteResources(
|
||||
isFullPageLoading.value = true
|
||||
isLoading.value = true
|
||||
try {
|
||||
const res = await browser.runtime
|
||||
const res: FavoritesResult = await browser.runtime
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getFavoriteResources',
|
||||
mediaId,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { useDateFormat } from '@vueuse/core'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { HistoryType } from './types'
|
||||
import type { HistoryItem } from './types'
|
||||
|
||||
// import type { HistoryItem } from './types'
|
||||
import { getCSRF, openLinkToNewTab, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import { Business } from '~/models/apiModels/video/history'
|
||||
import type { List as HistoryItem, HistoryResult } from '~/models/apiModels/video/history'
|
||||
import type { List as HistorySearchItem, HistorySearchResult } from '~/models/apiModels/video/historySearch'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -16,6 +19,10 @@ const currentPageNum = ref<number>(1)
|
||||
const keyword = ref<string>()
|
||||
const historyStatus = ref<boolean>()
|
||||
|
||||
const HistoryBusiness = computed(() => {
|
||||
return Business
|
||||
})
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
(newValue, oldValue) => {
|
||||
@@ -76,7 +83,7 @@ function getHistoryList() {
|
||||
? historyList[historyList.length - 1].view_at
|
||||
: 0,
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: HistoryResult) => {
|
||||
if (res.code === 0) {
|
||||
if (Array.isArray(res.data.list) && res.data.list.length > 0)
|
||||
historyList.push(...res.data.list)
|
||||
@@ -101,7 +108,7 @@ function searchHistoryList() {
|
||||
pn: currentPageNum.value++,
|
||||
keyword: keyword.value,
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: HistorySearchResult) => {
|
||||
if (res.code === 0) {
|
||||
if (historyList.length !== 0 && res.data.list.length < 20) {
|
||||
isLoading.value = false
|
||||
@@ -109,8 +116,8 @@ function searchHistoryList() {
|
||||
return
|
||||
}
|
||||
|
||||
res.data.list.forEach((item: HistoryItem) => {
|
||||
historyList.push(item)
|
||||
res.data.list.forEach((item: HistorySearchItem) => {
|
||||
historyList.push(item as HistoryItem)
|
||||
})
|
||||
|
||||
noMoreContent.value = false
|
||||
@@ -162,7 +169,7 @@ function getHistoryUrl(item: HistoryItem) {
|
||||
|
||||
function getHistoryItemCover(item: HistoryItem) {
|
||||
if (item.history.business === 'article')
|
||||
return removeHttpFromUrl(item.covers[0])
|
||||
return removeHttpFromUrl(`${item.covers[0]}`)
|
||||
|
||||
return removeHttpFromUrl(item.cover)
|
||||
}
|
||||
@@ -322,7 +329,7 @@ function jumpToLoginPage() {
|
||||
>
|
||||
|
||||
<span
|
||||
v-if="historyItem.history.business !== HistoryType.Archive"
|
||||
v-if="historyItem.history.business !== HistoryBusiness.ARCHIVE"
|
||||
pos="absolute right-0 top-0"
|
||||
bg="$bew-theme-color"
|
||||
text="xs white"
|
||||
@@ -331,19 +338,19 @@ function jumpToLoginPage() {
|
||||
rounded="$bew-radius-half"
|
||||
>
|
||||
<template
|
||||
v-if="historyItem.history.business === HistoryType.Live"
|
||||
v-if="historyItem.history.business === HistoryBusiness.LIVE"
|
||||
>
|
||||
Livestreaming
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
historyItem.history.business === HistoryType.Article
|
||||
historyItem.history.business === HistoryBusiness.ARCHIVE
|
||||
"
|
||||
>
|
||||
Article
|
||||
</template>
|
||||
<template
|
||||
v-else-if="historyItem.history.business === HistoryType.PGC"
|
||||
v-else-if="historyItem.history.business === HistoryBusiness.PGC"
|
||||
>
|
||||
Anime
|
||||
</template>
|
||||
@@ -351,8 +358,8 @@ function jumpToLoginPage() {
|
||||
|
||||
<div
|
||||
v-if="
|
||||
historyItem.history.business === HistoryType.Archive
|
||||
|| historyItem.history.business === HistoryType.PGC
|
||||
historyItem.history.business === HistoryBusiness.ARCHIVE
|
||||
|| historyItem.history.business === HistoryBusiness.PGC
|
||||
"
|
||||
pos="absolute bottom-0 right-0"
|
||||
bg="black opacity-60"
|
||||
@@ -374,8 +381,8 @@ function jumpToLoginPage() {
|
||||
<div w-full pos="absolute bottom-0" bg="white opacity-60">
|
||||
<Progress
|
||||
v-if="
|
||||
historyItem.history.business === HistoryType.Archive
|
||||
|| historyItem.history.business === HistoryType.PGC
|
||||
historyItem.history.business === HistoryBusiness.ARCHIVE
|
||||
|| historyItem.history.business === HistoryBusiness.PGC
|
||||
"
|
||||
:percentage="
|
||||
(historyItem.progress / historyItem.duration) * 100
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/history&toview/history.md#%E8%8E%B7%E5%8F%96%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E5%88%97%E8%A1%A8_web%E7%AB%AF
|
||||
export enum HistoryType {
|
||||
Archive = 'archive', // archive:稿件
|
||||
PGC = 'pgc', // pgc:剧集 (番剧 / 影视)
|
||||
Live = 'live', // live:直播
|
||||
ArticleList = 'article-list', // article-list:文集
|
||||
Article = 'article', // article:文章
|
||||
}
|
||||
|
||||
export interface HistoryItem {
|
||||
title: string
|
||||
cover: string
|
||||
covers: Array<string>
|
||||
history: {
|
||||
business: HistoryType
|
||||
epid: number
|
||||
bvid: string
|
||||
part: string
|
||||
oid: number
|
||||
}
|
||||
author_name: string
|
||||
author_face: string
|
||||
author_mid: string
|
||||
view_at: number
|
||||
progress: number
|
||||
duration: number
|
||||
kid: number
|
||||
live_status: 0 | 1 // 0:未开播 1:已开播
|
||||
uri: string
|
||||
show_title: string
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { useDateFormat } from '@vueuse/core'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { WatchLaterModel } from './types'
|
||||
import { getCSRF, openLinkToNewTab, removeHttpFromUrl } from '~/utils/main'
|
||||
import { calcCurrentTime } from '~/utils/dataFormatter'
|
||||
import emitter from '~/utils/mitt'
|
||||
import type { List as VideoItem, WatchLaterResult } from '~/models/apiModels/video/watchLater'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const isLoading = ref<boolean>()
|
||||
const noMoreContent = ref<boolean>()
|
||||
const watchLaterList = reactive<Array<WatchLaterModel>>([])
|
||||
const watchLaterList = reactive<VideoItem[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
getAllWatchLaterList()
|
||||
@@ -36,7 +36,7 @@ function getAllWatchLaterList() {
|
||||
.sendMessage({
|
||||
contentScriptQuery: 'getAllWatchLaterList',
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: WatchLaterResult) => {
|
||||
if (res.code === 0)
|
||||
Object.assign(watchLaterList, res.data.list)
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
export interface WatchLaterModel {
|
||||
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
|
||||
rights: {}
|
||||
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
|
||||
}
|
||||
short_link_v2: string
|
||||
first_frame: string
|
||||
count: number
|
||||
cid: number
|
||||
progress: number
|
||||
add_at: number
|
||||
bvid: string
|
||||
uri: string
|
||||
viewed: boolean
|
||||
enable_vt: number
|
||||
}
|
||||
Reference in New Issue
Block a user