58 lines
4.1 KiB
TypeScript
58 lines
4.1 KiB
TypeScript
import type { Advertisement, Advertisements, Chapter, Comment, DramaDetail, PageData } from './api/types';
|
|
import type { PlayerInitialData } from './page-data';
|
|
import { recordList } from './api/protocol';
|
|
import { normalizeSeo } from './seo';
|
|
import { chapterAccess } from './client/playback';
|
|
|
|
function fields(value:object,strings:string[],numbers:string[],booleans:string[]=[]):Record<string,unknown> {
|
|
const data=value as Record<string,unknown>,result:Record<string,unknown>={};
|
|
for(const key of strings)if(typeof data[key]==='string')result[key]=data[key];
|
|
for(const key of numbers)if(typeof data[key]==='number'&&Number.isFinite(data[key]))result[key]=data[key];
|
|
for(const key of booleans)if(typeof data[key]==='boolean')result[key]=data[key];
|
|
return result;
|
|
}
|
|
|
|
function advertisements(data:Advertisements|undefined):Advertisements|undefined {
|
|
if(!data||typeof data!=='object'||Array.isArray(data))return;
|
|
const result:Advertisements={};
|
|
for(const key of ['nineGridList','detailbannerList'])result[key]=recordList<Advertisement>(data[key]).map(item=>({
|
|
...fields(item,['advertiseCode','advertiseLocationCode','advertiseLocationName'],['location']),
|
|
id:typeof item.id==='string'?item.id:'',title:typeof item.title==='string'?item.title:'',
|
|
images:typeof item.images==='string'?item.images:'',link:typeof item.link==='string'?item.link:'',
|
|
}));
|
|
return result;
|
|
}
|
|
|
|
export function publicDrama(data:DramaDetail):DramaDetail {
|
|
const result=fields(data,['mediaId','title','playName','slug','desc','description','cover','horizontalCover','banner','coverImg','onlineAt','mediaCreatedAt','mediaUpdatedAt','createdAt','updatedAt','addedTime','anchor','preview','recommendTraceId','recommendId','recommendTraceInfo'],['id','totalEpisode','countLike','countCollect','countBrowse','heatValue','isCollect','isLike','watchTimes','updateStatus','operateType'],['isNew','isFeatured','showOnBanner']) as unknown as DramaDetail;
|
|
for(const key of ['tagNames','themeNames','tagSlugs','themeSlugs'] as const)if(Array.isArray(data[key]))result[key]=data[key].filter(value=>typeof value==='string');
|
|
for(const key of ['tagIds','themeIds'] as const)if(Array.isArray(data[key]))result[key]=data[key].filter(Number.isSafeInteger);
|
|
if(data.seoInfo)result.seoInfo=normalizeSeo(data.seoInfo);
|
|
result.advitise=advertisements(data.advitise);
|
|
if(data.video)result.video=fields(data.video,['url','cover'],['videoTime','width','height']);
|
|
return result;
|
|
}
|
|
|
|
function publicChapter(data:Chapter):Chapter {
|
|
const result=fields(data,['title','mediaId'],['id','playletId','currentEpisode','mediaTime','width','height','mediaSize','countBrowse','countCollect','countComment','countLike','ratio','payType','singleCoins','costCoins','balance','isCollect','isLike'],['canWatch','needUnlock','needVip','isUnlocked','vipValid']) as unknown as Chapter;
|
|
if(data.seoInfo)result.seoInfo=normalizeSeo(data.seoInfo);
|
|
// Protected sources are serialized only when this identity may actually watch them.
|
|
if(chapterAccess(data).allowed&&typeof data.videoUrl==='string')result.videoUrl=data.videoUrl;
|
|
return result;
|
|
}
|
|
|
|
function publicComment(data:Comment,depth=0):Comment {
|
|
const result=fields(data,['text','userName','userAvatar','createdAt'],['id','isLike','countLike','parentsId']) as unknown as Comment;
|
|
if(depth<4)result.children=recordList<Comment>(data.children).map(item=>publicComment(item,depth+1));
|
|
return result;
|
|
}
|
|
|
|
/** Explicit DTO: no auth tokens, SDK configuration, or arbitrary upstream properties. */
|
|
export function playerInitialData(raw:DramaDetail,chapters:Chapter[],chapter:Chapter|null,comments?:PageData<Comment>):PlayerInitialData {
|
|
const detail=publicDrama(raw);
|
|
detail.relatedRecommendData=recordList<DramaDetail>(raw.relatedRecommendData).filter(item=>Number.isSafeInteger(item.id)&&item.id>0).slice(0,6).map(publicDrama);
|
|
return {detail,chapters:chapters.map(publicChapter),chapter:chapter?publicChapter(chapter):null,
|
|
...(comments?{comments:{list:recordList<Comment>(comments.list).map(item=>publicComment(item)),...(typeof comments.error==='string'?{error:comments.error.slice(0,300)}:{}),totalCount:typeof comments.totalCount==='number'?comments.totalCount:0}}:{}),
|
|
};
|
|
}
|