228 lines
6.1 KiB
TypeScript

import { ContentItem, LensType, SimpleCharacter } from '../domain/valueObject';
// 定义角色属性接口
interface CharacterAttributes {
name: string;
// gender: string;
// age: string;
avatar: string;
}
// 定义文本节点接口
interface TextNode {
type: 'text';
text: string;
}
// 定义角色标记节点接口
interface CharacterTokenNode {
type: 'characterToken';
attrs: CharacterAttributes;
}
// 定义内容节点类型(文本或角色标记)
type ContentNode = TextNode | CharacterTokenNode;
// 定义段落接口
interface Paragraph {
type: 'paragraph';
content: ContentNode[];
}
// 定义shot 接口
interface Shot {
name: string;
shotDescContent: Paragraph[];
shotDialogsContent: Paragraph[];
}
export class TextToShotAdapter {
/**
* 解析文本,识别角色并转换为节点数组
* @param text 要解析的文本
* @param roles 角色列表
* @returns ContentNode[] 节点数组
*/
public static parseText(text: string, roles: SimpleCharacter[]): ContentNode[] {
const nodes: ContentNode[] = [];
let currentText = text;
// 按角色名称长度降序排序,避免短名称匹配到长名称的一部分
const sortedRoles = [...roles].sort((a, b) => b.name.length - a.name.length);
while (currentText.length > 0) {
let matchFound = false;
// 尝试匹配角色
for (const role of sortedRoles) {
if (currentText.startsWith(role.name)) {
// 如果当前文本以角色名开头
if (currentText.length > role.name.length) {
// 添加角色标记节点
nodes.push({
type: 'characterToken',
attrs: {
name: role.name,
avatar: role.imageUrl
}
});
// 移除已处理的角色名
currentText = currentText.slice(role.name.length);
matchFound = true;
break;
}
}
}
if (!matchFound) {
// 如果没有找到角色匹配,处理普通文本
// 查找下一个可能的角色名位置
let nextRoleIndex = currentText.length;
for (const role of sortedRoles) {
const index = currentText.indexOf(role.name);
if (index !== -1 && index < nextRoleIndex) {
nextRoleIndex = index;
}
}
// 添加文本节点
const textContent = currentText.slice(0, nextRoleIndex);
if (textContent) {
nodes.push({
type: 'text',
text: textContent
});
}
// 移除已处理的文本
currentText = currentText.slice(nextRoleIndex);
}
}
return nodes;
}
private readonly ShotData: Shot;
constructor(shotData: Shot) {
this.ShotData = shotData;
}
toShot() {
return this.ShotData;
}
/**
* 将 LensType 转换为 Paragraph 格式
* @param lensType LensType 实例
* @returns Paragraph 格式的数据
*/
public static fromLensType(lensType: LensType, roles: SimpleCharacter[]): Shot {
const shotDescContent: Paragraph[] = [];
const shotDialogsContent: Paragraph[] = [];
// 处理镜头描述 通过roles name 匹配镜头描述中出现的角色 并添加到shotDescContent
if (lensType.script) {
const descNodes = TextToShotAdapter.parseText(lensType.script, roles);
shotDescContent.push({
type: 'paragraph',
content: descNodes
});
}
// 处理对话内容 通过roles name 匹配对话内容中出现的角色 并添加到shotDialogsContent
lensType.content.forEach(item => {
const dialogNodes = TextToShotAdapter.parseText(item.content, roles);
// 确保对话内容以角色标记开始
const roleMatch = roles.find(role => role.name === item.roleName);
if (roleMatch) {
const dialogContent: Paragraph = {
type: 'paragraph',
content: [{
type: 'characterToken',
attrs: {
name: roleMatch.name,
avatar: roleMatch.imageUrl
}},
...dialogNodes
]
};
shotDialogsContent.push(dialogContent);
}
});
return {
name: lensType.name,
shotDescContent,
shotDialogsContent
};
}
/**
* 将 Paragraph 转换为 LensType 格式
* @param paragraphData Paragraph 格式的数据
* @returns LensType 实例
*/
/**
* 将 Shot 格式转换为 LensType 格式
* @param shotData Shot 格式的数据
* @returns LensType 实例
*/
public static toLensType(shotData: Shot): LensType {
const content: ContentItem[] = [];
let currentScript = '';
// 处理镜头描述
if (shotData.shotDescContent.length > 0) {
// 合并所有描述段落的文本内容
shotData.shotDescContent.forEach(paragraph => {
paragraph.content.forEach(node => {
if (node.type === 'text') {
currentScript += node.text;
}
if (node.type === 'characterToken') {
currentScript += node.attrs.name;
}
});
});
}
// 处理对话内容
shotData.shotDialogsContent.forEach(paragraph => {
let dialogRoleName = '';
let dialogContent = '';
let firstFindRole = false;
// 遍历段落内容
if (paragraph.content) {
paragraph.content.forEach((node, index) => {
if (node.type === 'characterToken') {
// 记录说话角色的名称
if (!firstFindRole) {
dialogRoleName = node.attrs.name;
firstFindRole = true;
} else {
dialogContent += node.attrs.name;
}
} else if (node.type === 'text') {
// 累积对话内容
dialogContent += node.text;
}
});
}
// 如果有角色名和对话内容,添加到结果中
if (dialogRoleName && dialogContent) {
content.push({
roleName: dialogRoleName,
content: dialogContent.trim()
});
}
});
return new LensType(
shotData.name, // 使用 Shot 中的 name
currentScript.trim(),
content
);
}
}