diff --git a/src/_locales/cmn-CN.yml b/src/_locales/cmn-CN.yml index 0e7213e6..51b8586d 100644 --- a/src/_locales/cmn-CN.yml +++ b/src/_locales/cmn-CN.yml @@ -396,6 +396,7 @@ home: trending: 热门视频 ranking: 排行 subscribed_series: 订阅剧集 + live: 直播 ranking: all: 全部 diff --git a/src/_locales/cmn-TW.yml b/src/_locales/cmn-TW.yml index c9a302a7..536066c5 100644 --- a/src/_locales/cmn-TW.yml +++ b/src/_locales/cmn-TW.yml @@ -397,6 +397,7 @@ home: trending: 發燒影片 ranking: 排行 subscribed_series: 訂閱劇集 + live: 直播 ranking: all: 全部 diff --git a/src/_locales/en.yml b/src/_locales/en.yml index e0c6a52d..ca8828c0 100644 --- a/src/_locales/en.yml +++ b/src/_locales/en.yml @@ -399,6 +399,7 @@ home: trending: Trending ranking: Ranking subscribed_series: Subscribed Series + live: Live ranking: all: All diff --git a/src/_locales/jyut.yml b/src/_locales/jyut.yml index b3088e62..d18602f7 100644 --- a/src/_locales/jyut.yml +++ b/src/_locales/jyut.yml @@ -396,6 +396,7 @@ home: trending: 時下至 Hit ranking: 排行 subscribed_series: 訂閱劇集 + live: 直播 ranking: all: 全部 diff --git a/src/background/messageListeners/api/index.ts b/src/background/messageListeners/api/index.ts index c6457ff6..d2ac98fe 100644 --- a/src/background/messageListeners/api/index.ts +++ b/src/background/messageListeners/api/index.ts @@ -5,6 +5,7 @@ import API_ANIME from './anime' import API_AUTH from './auth' import API_FAVORITE from './favorite' import API_HISTORY from './history' +import API_LIVE from './live' import API_MOMENT from './moment' import API_NOTIFICATION from './notification' import API_RANKING from './ranking' @@ -25,6 +26,7 @@ export const API_COLLECTION = { USER: API_USER, VIDEO: API_VIDEO, WATCHLATER: API_WATCHLATER, + LIVE: API_LIVE, [Symbol.iterator]() { return Object.values(this).values() diff --git a/src/background/messageListeners/api/live.ts b/src/background/messageListeners/api/live.ts new file mode 100644 index 00000000..a8854c98 --- /dev/null +++ b/src/background/messageListeners/api/live.ts @@ -0,0 +1,22 @@ +import type { APIMAP } from '../../utils' +import { AHS } from '../../utils' + +const API_LIVE = { + // https://socialsisteryi.github.io/bilibili-API-collect/docs/live/follow_up_live.html#%E7%94%A8%E6%88%B7%E5%85%B3%E6%B3%A8%E7%9A%84%E6%89%80%E6%9C%89up%E7%9A%84%E7%9B%B4%E6%92%AD%E6%83%85%E5%86%B5 + getFollowingLiveList: { + url: 'https://api.live.bilibili.com/xlive/web-ucenter/user/following', + _fetch: { + method: 'get', + }, + params: { + page: 1, + page_size: 9, + ignoreRecord: 1, + hit_ab: true, + }, + afterHandle: AHS.J_D, + }, + +} satisfies APIMAP + +export default API_LIVE diff --git a/src/components/VideoCard/VideoCardContextMenu/VideoCardContextMenu.vue b/src/components/VideoCard/VideoCardContextMenu/VideoCardContextMenu.vue index 7d0b3a1b..01c25808 100644 --- a/src/components/VideoCard/VideoCardContextMenu/VideoCardContextMenu.vue +++ b/src/components/VideoCard/VideoCardContextMenu/VideoCardContextMenu.vue @@ -68,7 +68,7 @@ const commonOptions = computed((): { command: VideoOption, name: string, icon: s // { command: VideoOption.ViewThisUserChannel, name: t('video_card.operation.view_this_user_channel'), icon: 'i-solar:user-bold-duotone' }, ], ] - if (getVideoType() === 'bangumi') { + if (getVideoType() === 'bangumi' || getVideoType() === 'live') { result = result.map((group) => { return group.filter((opt) => { return opt.command !== VideoOption.CopyBVNumber && opt.command !== VideoOption.CopyAVNumber && opt.command !== VideoOption.ViewThisUserChannel diff --git a/src/contentScripts/views/App.vue b/src/contentScripts/views/App.vue index 35e324ab..e70cae4d 100644 --- a/src/contentScripts/views/App.vue +++ b/src/contentScripts/views/App.vue @@ -7,7 +7,7 @@ import { useDark } from '~/composables/useDark' import { BEWLY_MOUNTED, DRAWER_VIDEO_ENTER_PAGE_FULL, DRAWER_VIDEO_EXIT_PAGE_FULL, OVERLAY_SCROLL_BAR_SCROLL } from '~/constants/globalEvents' import { AppPage } from '~/enums/appEnums' import { settings } from '~/logic' -import { isHomePage, queryDomUntilFound, scrollToTop } from '~/utils/main' +import { isHomePage, openLinkToNewTab, queryDomUntilFound, scrollToTop } from '~/utils/main' import emitter from '~/utils/mitt' import { setupNecessarySettingsWatchers } from './necessarySettingsWatchers' @@ -136,6 +136,17 @@ function handleOsScroll() { } function openIframeDrawer(url: string) { + const isSameOrigin = (origin: URL, destination: URL) => + origin.protocol === destination.protocol && origin.host === destination.host && origin.port === destination.port + + const currentUrl = new URL(location.href) + const destination = new URL(url) + + if (!isSameOrigin(currentUrl, destination)) { + openLinkToNewTab(url) + return + } + iframeDrawerUrl.value = url showIframeDrawer.value = true } diff --git a/src/contentScripts/views/Home/Home.vue b/src/contentScripts/views/Home/Home.vue index 3b31c8a9..465ec945 100644 --- a/src/contentScripts/views/Home/Home.vue +++ b/src/contentScripts/views/Home/Home.vue @@ -22,6 +22,7 @@ const pages = { [HomeSubPage.SubscribedSeries]: defineAsyncComponent(() => import('./components/SubscribedSeries.vue')), [HomeSubPage.Trending]: defineAsyncComponent(() => import('./components/Trending.vue')), [HomeSubPage.Ranking]: defineAsyncComponent(() => import('./components/Ranking.vue')), + [HomeSubPage.Live]: defineAsyncComponent(() => import('./components/Live.vue')), } const showSearchPageMode = ref(false) const shouldMoveTabsUp = ref(false) diff --git a/src/contentScripts/views/Home/components/Live.vue b/src/contentScripts/views/Home/components/Live.vue new file mode 100644 index 00000000..10549ae5 --- /dev/null +++ b/src/contentScripts/views/Home/components/Live.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/contentScripts/views/Home/types.ts b/src/contentScripts/views/Home/types.ts index 1235ab05..9d597de6 100644 --- a/src/contentScripts/views/Home/types.ts +++ b/src/contentScripts/views/Home/types.ts @@ -6,6 +6,7 @@ export enum HomeSubPage { SubscribedSeries = 'SubscribedSeries', Trending = 'Trending', Ranking = 'Ranking', + Live = 'Live', } export interface RankingType { diff --git a/src/models/live/getFollowingLiveList.ts b/src/models/live/getFollowingLiveList.ts new file mode 100644 index 00000000..a386d2ff --- /dev/null +++ b/src/models/live/getFollowingLiveList.ts @@ -0,0 +1,47 @@ +// https://app.quicktype.io/?l=ts + +export interface FollowingLiveResult { + code: number + message: string + ttl: number + data: Data +} + +export interface Data { + title: string + pageSize: number + totalPage: number + list: List[] + count: number + never_lived_count: number + live_count: number + never_lived_faces: any[] +} + +export interface List { + roomid: number + uid: number + uname: string + title: string + face: string + live_status: number + record_num: number + recent_record_id: string + is_attention: number + clipnum: number + fans_num: number + area_name: string + area_value: string + tags: string + recent_record_id_v2: string + record_num_v2: number + record_live_time: number + area_name_v2: string + room_news: string + switch: boolean + watch_icon: string + text_small: string + room_cover: string + parent_area_id: number + area_id: number +} diff --git a/src/stores/mainStore.ts b/src/stores/mainStore.ts index 6a83fc1d..51d8bf6d 100644 --- a/src/stores/mainStore.ts +++ b/src/stores/mainStore.ts @@ -49,6 +49,10 @@ export const useMainStore = defineStore('main', () => { i18nKey: 'home.ranking', page: HomeSubPage.Ranking, }, + { + i18nKey: 'home.live', + page: HomeSubPage.Live, + }, ], ) diff --git a/src/utils/main.ts b/src/utils/main.ts index a039350f..dcc2f11b 100644 --- a/src/utils/main.ts +++ b/src/utils/main.ts @@ -43,8 +43,8 @@ export function removeHttpFromUrl(url: string): string { return url.replace(/^https?:/, '') } -export function openLinkToNewTab(url: string) { - window.open(url, '_blank', 'noopener noreferrer') +export function openLinkToNewTab(url: string, features: string = '') { + window.open(url, '_blank', features) } /**