New @umt/mapping (depends on domain + contracts only): download status + pause reason, evidence level / media kind, identity resolution status, slot status / spec columns / evaluation, ParsedRelease <-> source_release.parsed, SiteRelease <-> source_release, PathBinding, OrganizePlan -> organization_record / file results / organize.plan output. Tables are Record<union, ...> so adding a value on either side fails typecheck or tests. Co-Authored-By: Claude Opus 5.5 <[email protected]>
73 lines
2.9 KiB
TypeScript
73 lines
2.9 KiB
TypeScript
import type { ContentType as ContractContentType, Segment as ContractSegment, JsonObject } from "@umt/contracts";
|
|
import {
|
|
type ContentType as DomainContentType,
|
|
type Segment as DomainSegment,
|
|
PARSER_VERSION,
|
|
type ParsedRelease,
|
|
} from "@umt/domain";
|
|
|
|
// 标题解析结果 ↔ source_release.parsed / parser_version 列;综艺内容类型与分段 ↔ content_unit 列。
|
|
|
|
/** ParsedRelease → source_release 的 parsed(jsonb)与 parser_version(text) */
|
|
export function toParsedColumns(p: ParsedRelease): { parsed: JsonObject; parserVersion: string } {
|
|
return { parsed: JSON.parse(JSON.stringify(p)) as JsonObject, parserVersion: String(p.parserVersion) };
|
|
}
|
|
|
|
const REQUIRED_ARRAYS = ["audio", "hdr", "variantTags", "explicitIds", "evidence"] as const;
|
|
|
|
/**
|
|
* source_release 列 → ParsedRelease。版本不是当前 PARSER_VERSION 或形状不对时返回 null,
|
|
* 调用方应按原标题重新解析(规则变化后旧结果作废)。
|
|
*/
|
|
export function fromParsedColumns(cols: { parsed: JsonObject; parserVersion: string | null }): ParsedRelease | null {
|
|
if (cols.parserVersion !== String(PARSER_VERSION)) return null;
|
|
const p = cols.parsed;
|
|
if (p.parserVersion !== PARSER_VERSION || typeof p.raw !== "string" || typeof p.isEncode !== "boolean") return null;
|
|
if (typeof p.contentType !== "string" || typeof p.confidence !== "string") return null;
|
|
if (REQUIRED_ARRAYS.some((k) => !Array.isArray(p[k]))) return null;
|
|
return p as unknown as ParsedRelease;
|
|
}
|
|
|
|
const CONTENT_TYPE: Record<DomainContentType, ContractContentType> = {
|
|
unknown: "unknown",
|
|
main: "main",
|
|
extra: "extra",
|
|
behind_the_scenes: "behind_the_scenes",
|
|
performance_only: "performance_only",
|
|
preview: "preview",
|
|
reaction: "reaction",
|
|
};
|
|
const CONTENT_TYPE_BACK: Record<ContractContentType, DomainContentType> = {
|
|
main: "main",
|
|
extra: "extra",
|
|
behind_the_scenes: "behind_the_scenes",
|
|
performance_only: "performance_only",
|
|
preview: "preview",
|
|
reaction: "reaction",
|
|
unknown: "unknown",
|
|
};
|
|
export const DOMAIN_CONTENT_TYPES = Object.keys(CONTENT_TYPE) as DomainContentType[];
|
|
|
|
export const toContractContentType = (t: DomainContentType): ContractContentType => CONTENT_TYPE[t];
|
|
export const toDomainContentType = (t: ContractContentType): DomainContentType => CONTENT_TYPE_BACK[t];
|
|
|
|
const SEGMENT: Record<DomainSegment, ContractSegment> = {
|
|
full: "full",
|
|
upper: "upper",
|
|
middle: "middle",
|
|
lower: "lower",
|
|
};
|
|
const SEGMENT_BACK: Record<ContractSegment, DomainSegment | undefined> = {
|
|
full: "full",
|
|
upper: "upper",
|
|
middle: "middle",
|
|
lower: "lower",
|
|
// domain 用"字段缺失"表示未知
|
|
unknown: undefined,
|
|
};
|
|
export const DOMAIN_SEGMENTS = Object.keys(SEGMENT) as DomainSegment[];
|
|
|
|
/** 解析结果没有分段信息(undefined)时落为 unknown */
|
|
export const toContractSegment = (s: DomainSegment | undefined): ContractSegment => (s ? SEGMENT[s] : "unknown");
|
|
export const toDomainSegment = (s: ContractSegment): DomainSegment | undefined => SEGMENT_BACK[s];
|