107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import { RELATED_DISPLAY_COUNT } from "@/components/drama-detail/data";
|
|
import { pickVideoCardTags } from "@/components/common/card-tags";
|
|
import { formatHeat } from "@/components/ranking/data";
|
|
import type { ShortDramaItem } from "@/components/short-drama/data";
|
|
import { mapVideoListItem } from "@/components/video/data";
|
|
import type {
|
|
QueryVideoInfoResp,
|
|
VideoData,
|
|
VideoDetailInfo,
|
|
VideoRecommendListResp,
|
|
} from "@/lib/api/types";
|
|
import { resolvePlayURL } from "@/lib/media/playback";
|
|
|
|
/** 详情页:直接使用接口 videoInfo 字段 */
|
|
export type VideoDetail = VideoDetailInfo;
|
|
|
|
/** H5 / 侧栏热门推荐条数 */
|
|
export const VIDEO_RECOMMEND_DISPLAY_COUNT = 10;
|
|
|
|
export type VideoPlayView = {
|
|
detail: VideoDetail;
|
|
src: string;
|
|
related: ShortDramaItem[];
|
|
};
|
|
|
|
function mapHotVideoItem(item: VideoData): ShortDramaItem {
|
|
return {
|
|
kind: "drama",
|
|
id: String(item.id),
|
|
slug: item.slug?.trim() || undefined,
|
|
title: (item.title || "").trim() || "未命名",
|
|
subtitle: "",
|
|
tags: pickVideoCardTags(item),
|
|
heat: formatHeat(item.watchNum),
|
|
episodes: "",
|
|
cover: (item.coverImg || "").trim(),
|
|
date: item.addedTime || undefined,
|
|
};
|
|
}
|
|
|
|
/** 热门推荐:保留广告位;排除当前视频 */
|
|
export function mapVideoRecommendList(
|
|
data: VideoRecommendListResp | null | undefined,
|
|
options?: { excludeId?: number | string | null; limit?: number },
|
|
): ShortDramaItem[] {
|
|
const excludeId = options?.excludeId != null ? Number(options.excludeId) : NaN;
|
|
const limit = options?.limit ?? VIDEO_RECOMMEND_DISPLAY_COUNT;
|
|
return (data?.list ?? [])
|
|
.filter((item) => {
|
|
if (item.type === 2) return true;
|
|
if (!item.id) return false;
|
|
if (Number.isFinite(excludeId) && Number(item.id) === excludeId) return false;
|
|
return true;
|
|
})
|
|
.map(mapVideoListItem)
|
|
.filter((item): item is ShortDramaItem => Boolean(item))
|
|
.slice(0, limit);
|
|
}
|
|
|
|
/** 透传接口字段,仅拼播放地址 / 根级点赞收藏态 */
|
|
export function mapVideoInfo(data: QueryVideoInfoResp | null | undefined): VideoPlayView | null {
|
|
const info = data?.videoInfo;
|
|
if (!info?.id) return null;
|
|
|
|
const media = info.video;
|
|
const enableH265 = Boolean(data?.config?.enableH265);
|
|
const path = (enableH265 && media?.h265Url?.trim() ? media.h265Url : media?.url) || media?.h265Url || "";
|
|
const src = resolvePlayURL(path);
|
|
|
|
return {
|
|
detail: {
|
|
...info,
|
|
isLike: Boolean(data?.isLike ?? info.isLike),
|
|
isFavorite: Boolean(data?.isFavorite ?? info.isFavorite),
|
|
},
|
|
src,
|
|
related: (data?.hotVideoList ?? [])
|
|
.filter((item) => Boolean(item.id))
|
|
.slice(0, RELATED_DISPLAY_COUNT)
|
|
.map(mapHotVideoItem),
|
|
};
|
|
}
|
|
|
|
export function formatWatchNum(count?: number | null) {
|
|
const value = Number(count) || 0;
|
|
if (value >= 10000) {
|
|
const wan = value / 10000;
|
|
const text = wan >= 100 ? String(Math.round(wan)) : wan.toFixed(1).replace(/\.0$/, "");
|
|
return `${text}万次播放`;
|
|
}
|
|
return `${value}次播放`;
|
|
}
|
|
|
|
export function formatCount(count?: number | null) {
|
|
const value = Number(count) || 0;
|
|
if (value >= 10000) {
|
|
const wan = value / 10000;
|
|
const text = wan >= 100 ? String(Math.round(wan)) : wan.toFixed(1).replace(/\.0$/, "");
|
|
return `${text}万`;
|
|
}
|
|
if (value >= 1000) {
|
|
const k = value / 1000;
|
|
return `${k.toFixed(1).replace(/\.0$/, "")}k`;
|
|
}
|
|
return String(value);
|
|
}
|