142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import {
|
|
INSTALL_VIDEO_TYPE,
|
|
LIVE_CLIP_VIDEO_TYPE,
|
|
isVideoMaterialType,
|
|
normalizeVideoMaterialType,
|
|
} from './video-material-types'
|
|
|
|
export const VIDEO_DOWNLOAD_WAIT_DAYS = 15
|
|
export const VIDEO_DOWNLOAD_WAIT_MS = VIDEO_DOWNLOAD_WAIT_DAYS * 24 * 60 * 60 * 1000
|
|
|
|
/**
|
|
* 站内用户经超级管理员开通的额外豁免;
|
|
* 安装视频和直播切片的类型豁免对站内、外部接口都生效。
|
|
*/
|
|
export interface VideoPolicyOptions {
|
|
bypassWaitPeriod?: boolean
|
|
}
|
|
|
|
export interface VideoDownloadAvailability {
|
|
allowed: boolean
|
|
reason?: 'VIDEO_WAIT_PERIOD'
|
|
availableAt?: Date
|
|
remainingDays?: number
|
|
}
|
|
|
|
export interface VideoPreviewPolicyFields {
|
|
videoPreviewBlocked: boolean
|
|
videoPreviewUnavailableReason?: 'VIDEO_WAIT_PERIOD' | null
|
|
videoPreviewAvailableAt?: string | null
|
|
videoPreviewRemainingDays?: number | null
|
|
videoPreviewUnavailableMessage?: string | null
|
|
}
|
|
|
|
export function userHasVideoEarlyAccess(user?: unknown): boolean {
|
|
return Boolean((user as { videoEarlyAccess?: boolean } | null)?.videoEarlyAccess)
|
|
}
|
|
|
|
export function getVideoDownloadAvailability(
|
|
materialType?: string | null,
|
|
createdAt?: Date | string | null,
|
|
now: Date = new Date(),
|
|
options?: VideoPolicyOptions,
|
|
): VideoDownloadAvailability {
|
|
if (options?.bypassWaitPeriod) {
|
|
return { allowed: true }
|
|
}
|
|
|
|
const normalizedType = normalizeVideoMaterialType(materialType)
|
|
|
|
if (
|
|
!isVideoMaterialType(normalizedType) ||
|
|
normalizedType === INSTALL_VIDEO_TYPE ||
|
|
normalizedType === LIVE_CLIP_VIDEO_TYPE
|
|
) {
|
|
return { allowed: true }
|
|
}
|
|
|
|
const uploadedAt = createdAt instanceof Date ? createdAt : new Date(String(createdAt || ''))
|
|
if (Number.isNaN(uploadedAt.getTime())) {
|
|
return { allowed: true }
|
|
}
|
|
|
|
const availableAt = new Date(uploadedAt.getTime() + VIDEO_DOWNLOAD_WAIT_MS)
|
|
if (now.getTime() >= availableAt.getTime()) {
|
|
return { allowed: true }
|
|
}
|
|
|
|
const remainingMs = availableAt.getTime() - now.getTime()
|
|
return {
|
|
allowed: false,
|
|
reason: 'VIDEO_WAIT_PERIOD',
|
|
availableAt,
|
|
remainingDays: Math.max(1, Math.ceil(remainingMs / (24 * 60 * 60 * 1000))),
|
|
}
|
|
}
|
|
|
|
export function getVideoDownloadUnavailableMessage(availability: VideoDownloadAvailability): string {
|
|
const remainingDays = availability.remainingDays || VIDEO_DOWNLOAD_WAIT_DAYS
|
|
return `视频类素材上传后满 ${VIDEO_DOWNLOAD_WAIT_DAYS} 天才能下载,当前还需约 ${remainingDays} 天`
|
|
}
|
|
|
|
export function getVideoPreviewUnavailableMessage(availability: VideoDownloadAvailability): string {
|
|
const remainingDays = availability.remainingDays || VIDEO_DOWNLOAD_WAIT_DAYS
|
|
return `视频类素材上传后满 ${VIDEO_DOWNLOAD_WAIT_DAYS} 天才能预览和下载,当前还需约 ${remainingDays} 天`
|
|
}
|
|
|
|
export function getVideoPreviewPolicyFields(
|
|
materialType?: string | null,
|
|
createdAt?: Date | string | null,
|
|
now: Date = new Date(),
|
|
options?: VideoPolicyOptions,
|
|
): VideoPreviewPolicyFields {
|
|
const availability = getVideoDownloadAvailability(materialType, createdAt, now, options)
|
|
|
|
if (availability.allowed) {
|
|
return {
|
|
videoPreviewBlocked: false,
|
|
videoPreviewUnavailableReason: null,
|
|
videoPreviewAvailableAt: null,
|
|
videoPreviewRemainingDays: null,
|
|
videoPreviewUnavailableMessage: null,
|
|
}
|
|
}
|
|
|
|
return {
|
|
videoPreviewBlocked: true,
|
|
videoPreviewUnavailableReason: availability.reason || 'VIDEO_WAIT_PERIOD',
|
|
videoPreviewAvailableAt: availability.availableAt?.toISOString() || null,
|
|
videoPreviewRemainingDays: availability.remainingDays || VIDEO_DOWNLOAD_WAIT_DAYS,
|
|
videoPreviewUnavailableMessage: getVideoPreviewUnavailableMessage(availability),
|
|
}
|
|
}
|
|
|
|
export function applyVideoPreviewPolicy<T extends Record<string, any>>(
|
|
material: T,
|
|
now: Date = new Date(),
|
|
options?: VideoPolicyOptions,
|
|
): T & VideoPreviewPolicyFields {
|
|
const policyFields = getVideoPreviewPolicyFields(
|
|
material?.materialType,
|
|
material?.createdAt,
|
|
now,
|
|
options,
|
|
)
|
|
const nextMaterial: any = {
|
|
...material,
|
|
...policyFields,
|
|
}
|
|
|
|
if (policyFields.videoPreviewBlocked) {
|
|
nextMaterial.videoSourceUrl = null
|
|
nextMaterial.videoPreview540Url = null
|
|
nextMaterial.videoPreviewClipUrl = null
|
|
|
|
if ('sourceFileUrl' in nextMaterial) {
|
|
nextMaterial.sourceFileUrl = null
|
|
}
|
|
}
|
|
|
|
return nextMaterial
|
|
}
|