동영상, 오프닝 자막 관련 문항 처리 방식 개선
This commit is contained in:
@@ -229,8 +229,9 @@
|
|||||||
"desc": "value값의 키값(VID___)은 이펙트의 속성종류에 따라 변경되므로 채점기준표작성시 확인 필요"
|
"desc": "value값의 키값(VID___)은 이펙트의 속성종류에 따라 변경되므로 채점기준표작성시 확인 필요"
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"ele": "//CRCUnitArr[@Name='{search}']/@Name",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr/@Name",
|
||||||
"ele2": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr/@Name",
|
"ele2": "//CRCUnitArr[@Name='{search}']/@Name",
|
||||||
|
"ele3": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr/@Name",
|
||||||
"type": "video",
|
"type": "video",
|
||||||
"value": "모래 촉감 놀이",
|
"value": "모래 촉감 놀이",
|
||||||
"search": "모래 촉감 놀이",
|
"search": "모래 촉감 놀이",
|
||||||
|
|||||||
@@ -211,7 +211,7 @@
|
|||||||
"media": "동영상.mp4",
|
"media": "동영상.mp4",
|
||||||
"value": {
|
"value": {
|
||||||
"start": "0",
|
"start": "0",
|
||||||
"end": "360"
|
"end": "380"
|
||||||
},
|
},
|
||||||
"point": 2,
|
"point": 2,
|
||||||
"desc": "시작시간과 재생시간 정답값 입력, 3번문항은 '동영상.mp4' 클립의 길이를 확인하는 문항이므로 media는 수정할 필요가 없다."
|
"desc": "시작시간과 재생시간 정답값 입력, 3번문항은 '동영상.mp4' 클립의 길이를 확인하는 문항이므로 media는 수정할 필요가 없다."
|
||||||
@@ -229,59 +229,56 @@
|
|||||||
"desc": "value값의 키값(VID___)은 이펙트의 속성종류에 따라 변경되므로 채점기준표작성시 확인 필요"
|
"desc": "value값의 키값(VID___)은 이펙트의 속성종류에 따라 변경되므로 채점기준표작성시 확인 필요"
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"ele": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr/@Name",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr/@Name",
|
||||||
"ele2": "//CRCUnitArr[@Name='{search}']/@Name",
|
|
||||||
"type": "video",
|
|
||||||
"value": "청량하고 시원한 폭포",
|
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
|
"type": "video.Text",
|
||||||
|
"value": "청량하고 시원한 폭포",
|
||||||
"point": 3
|
"point": 3
|
||||||
},
|
},
|
||||||
"6": {
|
"6": {
|
||||||
"ele": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr//GCUnitPool[@Type='1']/GCUnit/@VID102",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr//GCUnitPool[@Type='1']/GCUnit/@VID102",
|
||||||
"ele2": "//CRCUnitArr[@Name='{search}']//GCUnitPool[@Type='1']/GCUnit/@VID102",
|
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
"type": "video",
|
"type": "video.Text",
|
||||||
"value": "돋움체",
|
"value": "돋움체",
|
||||||
"point": 2
|
"point": 2
|
||||||
},
|
},
|
||||||
"7": {
|
"7": {
|
||||||
"ele": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr//GCUnitPool[@Type='1']/GCUnit/@VID101",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr//GCUnitPool[@Type='1']/GCUnit/@VID101",
|
||||||
"ele2": "//CRCUnitArr[@Name='{search}']//GCUnitPool[@Type='1']/GCUnit/@VID101",
|
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
"type": "video",
|
"type": "video.Text",
|
||||||
"value": "120",
|
"value": "120",
|
||||||
"point": 2
|
"point": 2
|
||||||
},
|
},
|
||||||
"8": {
|
"8": {
|
||||||
"ele": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr//GCUnitPool/GCUnit[@Type='4']/@VID100",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr//GCUnitPool/GCUnit[@Type='4']/@VID100",
|
||||||
"ele2": "//CRCUnitArr[@Name='{search}']//GCUnitPool/GCUnit[@Type='4']/@VID100",
|
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
"type": "video",
|
"type": "video.Text.Color",
|
||||||
"value": "-937955",
|
"value": "1db0f1",
|
||||||
"point": 2
|
"point": 2,
|
||||||
|
"desc": "컬러값은 RGB로 입력한다, [대소문자, #]허용 (#FFFFFF, ffffff 두 값 모두 허용)"
|
||||||
},
|
},
|
||||||
"9": {
|
"9": {
|
||||||
"ele": "//CROwneUnit[{subtitleIndex}]/CRCUnitArr/@*[name()='VID600' or name()='VID601']",
|
"ele": "//CROwneUnit[{index}]/CRCUnitArr/@*[name()='VID600' or name()='VID601']",
|
||||||
"ele2": "//CRCUnitArr[@Name='{search}']/@*[name()='VID600' or name()='VID601']",
|
|
||||||
"type": "video",
|
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
|
"type": "video.Location",
|
||||||
"value": [
|
"value": [
|
||||||
0.27395833,
|
"0.27395833",
|
||||||
0.9222222
|
"0.9222222"
|
||||||
],
|
],
|
||||||
"point": 2
|
"point": 2,
|
||||||
|
"desc": "정답 파일의 자막 좌표를 기준으로 프로그램 내부적으로 0.1까지 오차를 허용한다"
|
||||||
},
|
},
|
||||||
"10": {
|
"10": {
|
||||||
"ele": "{search}",
|
"ele": "",
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
"type": "videoStartTime",
|
"type": "video.StartTime",
|
||||||
"value": 170,
|
"value": 170,
|
||||||
"point": 2
|
"point": 2
|
||||||
},
|
},
|
||||||
"11": {
|
"11": {
|
||||||
"ele": "{search}",
|
"ele": "",
|
||||||
"search": "청량하고 시원한 폭포",
|
"search": "청량하고 시원한 폭포",
|
||||||
"type": "videoLength",
|
"type": "video.Length",
|
||||||
"value": 150,
|
"value": 150,
|
||||||
"point": 2
|
"point": 2
|
||||||
},
|
},
|
||||||
|
|||||||
124
psdExport_2.js
124
psdExport_2.js
@@ -172,12 +172,24 @@ outputExcelFiles.forEach((outputFile, index) => {
|
|||||||
// scoring.json 파일 내에 있는 type에 따라 비교하는 방식이 달라짐
|
// scoring.json 파일 내에 있는 type에 따라 비교하는 방식이 달라짐
|
||||||
// 채점 결과를 scoringResultList 배열에 저장
|
// 채점 결과를 scoringResultList 배열에 저장
|
||||||
function getGmepScore(gmepData, scoringJson, index) {
|
function getGmepScore(gmepData, scoringJson, index) {
|
||||||
function compareAndScore(userAnswer, rightAnswer, point, key, scoringResult) {
|
function compareAndScore(userAnswer, rightAnswer, point, key, scoringResult, tolerance = 0) {
|
||||||
let score = 0;
|
let score = 0;
|
||||||
|
|
||||||
const isEqual = (typeof rightAnswer === "object" && typeof userAnswer === "object")
|
let isEqual;
|
||||||
? JSON.stringify(userAnswer) === JSON.stringify(rightAnswer)
|
|
||||||
: userAnswer == rightAnswer;
|
if (Array.isArray(rightAnswer) && Array.isArray(userAnswer)) {
|
||||||
|
// 배열 길이 같아야 비교 가능
|
||||||
|
if (rightAnswer.length === userAnswer.length) {
|
||||||
|
isEqual = rightAnswer.every((val, idx) => Math.abs(val - userAnswer[idx]) <= tolerance);
|
||||||
|
} else {
|
||||||
|
isEqual = false;
|
||||||
|
}
|
||||||
|
} else if (typeof rightAnswer === "object" && typeof userAnswer === "object") {
|
||||||
|
// 객체일 때는 기존 방식 유지 (원하면 별도 로직 추가 가능)
|
||||||
|
isEqual = JSON.stringify(userAnswer) === JSON.stringify(rightAnswer);
|
||||||
|
} else {
|
||||||
|
isEqual = userAnswer == rightAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
if (isEqual) {
|
if (isEqual) {
|
||||||
score = point;
|
score = point;
|
||||||
@@ -192,6 +204,25 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertColorToHex(colorValue) {
|
||||||
|
// 문자열이면 정수로 변환
|
||||||
|
const intValue = typeof colorValue === 'string' ? parseInt(colorValue, 10) : colorValue;
|
||||||
|
|
||||||
|
// 부호 없는 32비트 정수로 변환 → 8자리 16진수 문자열로
|
||||||
|
const hex = (intValue >>> 0).toString(16).padStart(8, '0');
|
||||||
|
|
||||||
|
// 하위 6자리 추출 (BGR 순서)
|
||||||
|
const bgr = hex.slice(2); // 예: "fff1b01d" → "f1b01d"
|
||||||
|
|
||||||
|
// BGR → RGB로 재배열
|
||||||
|
const b = bgr.slice(0, 2);
|
||||||
|
const g = bgr.slice(2, 4);
|
||||||
|
const r = bgr.slice(4, 6);
|
||||||
|
|
||||||
|
// RGB 순서로 합치고 소문자로 반환
|
||||||
|
return (r + g + b).toLowerCase(); // e.g., "fd5721"
|
||||||
|
}
|
||||||
|
|
||||||
const gmepXmlDoc = gmepData;
|
const gmepXmlDoc = gmepData;
|
||||||
const scoringResult = {};
|
const scoringResult = {};
|
||||||
|
|
||||||
@@ -320,7 +351,7 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
const rightAnswer = scoringData[key].value;
|
const rightAnswer = scoringData[key].value;
|
||||||
const point = scoringData[key].point;
|
const point = scoringData[key].point;
|
||||||
const type = scoringData[key].type;
|
const type = scoringData[key].type;
|
||||||
const search = scoringData[key].search;
|
let search = scoringData[key].search;
|
||||||
const media = scoringData[key].media;
|
const media = scoringData[key].media;
|
||||||
const videoStartTime = scoringData.videoStartTime;
|
const videoStartTime = scoringData.videoStartTime;
|
||||||
const openingStartTime = scoringData.openingStartTime;
|
const openingStartTime = scoringData.openingStartTime;
|
||||||
@@ -333,15 +364,18 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
const trackClipNode = getTrackClipNode(gmepXmlDoc, type, videoStartTime, openingStartTime);
|
const trackClipNode = getTrackClipNode(gmepXmlDoc, type, videoStartTime, openingStartTime);
|
||||||
const subtitleIndex = trackClipNode ? parseInt(trackClipNode.getAttribute('ClipIndex'), 10) + 1 : null;
|
const subtitleIndex = trackClipNode ? parseInt(trackClipNode.getAttribute('ClipIndex'), 10) + 1 : null;
|
||||||
const textClipIndex = getTextClipIndex(gmepXmlDoc, search);
|
const textClipIndex = getTextClipIndex(gmepXmlDoc, search);
|
||||||
const typeToOrderMap = {
|
// const typeToOrderMap = {
|
||||||
opening: 1,
|
// opening: 1,
|
||||||
openingStartTime: 1,
|
// openingStartTime: 1,
|
||||||
openingLength: 1,
|
// openingLength: 1,
|
||||||
video: 2,
|
// openingText: 1,
|
||||||
videoStartTime: 2,
|
// video: 2,
|
||||||
videoLength: 2,
|
// videoStartTime: 2,
|
||||||
};
|
// videoLength: 2,
|
||||||
const subtitleOrder = typeToOrderMap[type] ?? null;
|
// videoText: 2,
|
||||||
|
// };
|
||||||
|
// const subtitleOrder = typeToOrderMap[type] ?? null;
|
||||||
|
const subtitleOrder = type.includes('opening') ? 1 : type.includes('video') ? 2 : null;
|
||||||
const startTime = type === 'video' ? videoStartTime : type === 'opening' ? openingStartTime : null;
|
const startTime = type === 'video' ? videoStartTime : type === 'opening' ? openingStartTime : null;
|
||||||
|
|
||||||
let xpathList = [ele, ele2, ele3, existEle];
|
let xpathList = [ele, ele2, ele3, existEle];
|
||||||
@@ -367,7 +401,8 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
let result = findSimilarString(gmepXmlDoc, search, 0.8);
|
let result = findSimilarString(gmepXmlDoc, search, 0.8);
|
||||||
if (result !== null) {
|
if (result !== null) {
|
||||||
result = result.replace(/"/g, "'");
|
result = result.replace(/"/g, "'");
|
||||||
[ele, ele2, ele3, existEle] = [ele, ele2, ele3, existEle].map(e => e?.replace(/{search}/g, result));
|
search = result;
|
||||||
|
[ele, ele2, ele3, existEle] = [ele, ele2, ele3, existEle].map(e => e?.replace(/{search}/g, search));
|
||||||
} else {
|
} else {
|
||||||
[ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.includes('{search}') ? null : e);
|
[ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.includes('{search}') ? null : e);
|
||||||
}
|
}
|
||||||
@@ -488,13 +523,21 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === 'openingStartTime' || type === 'openingLength'
|
// 자막관련 type검사하는 구문을 opening포함 video포함으로 변경
|
||||||
|| type === 'videoStartTime' || type === 'videoLength') {
|
// 6/16(월)시작 지점
|
||||||
|
// 1. JSON파일 10,11번 처럼 변경
|
||||||
|
else if (type.includes('opening') || type.includes('video')) {
|
||||||
|
function toHexColor(value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
// else if (type === 'openingStartTime' || type === 'openingLength'
|
||||||
|
// || type === 'videoStartTime' || type === 'videoLength') {
|
||||||
|
|
||||||
// 자막의 정보를 이용해 CROwneUnit의 인덱스를 구함
|
// 자막의 정보를 이용해 CROwneUnit의 인덱스를 구함
|
||||||
// 1. 텍스트
|
// 1. 텍스트
|
||||||
// 2. 순서
|
// 2. 순서
|
||||||
// 3. 시작시간
|
// 3. 시작시간
|
||||||
const indexByText = getClipIndexByText(ele);
|
const indexByText = getClipIndexByText(search);
|
||||||
const indexByOrder = getClipIndexByOrder(subtitleOrder);
|
const indexByOrder = getClipIndexByOrder(subtitleOrder);
|
||||||
|
|
||||||
if (type.includes('opening')) time = openingStartTime;
|
if (type.includes('opening')) time = openingStartTime;
|
||||||
@@ -505,27 +548,60 @@ function getGmepScore(gmepData, scoringJson, index) {
|
|||||||
// 1, 2, 3순으로 자막을 찾음
|
// 1, 2, 3순으로 자막을 찾음
|
||||||
const index = indexByText ?? indexByOrder ?? indexByStartTime;
|
const index = indexByText ?? indexByOrder ?? indexByStartTime;
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
// 자막 시작시간
|
// 자막 시작시간 [2-10] [2-28]
|
||||||
if (type.includes('StartTime')) {
|
if (type.includes('StartTime')) {
|
||||||
const crtrackClipIndex = getCrtrackClipIndex(index)
|
const crtrackClipIndex = getCrtrackClipIndex(index)
|
||||||
const startTimeList = getSubtitleStartTime();
|
const startTimeList = getSubtitleStartTime();
|
||||||
const startTime = startTimeList[crtrackClipIndex];
|
const startTime = startTimeList[crtrackClipIndex];
|
||||||
userAnswer = startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 자막 길이
|
userAnswer = startTime;
|
||||||
|
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||||
|
}
|
||||||
|
// 자막 길이 [2-11] [2-29]
|
||||||
else if (type.includes('Length')) {
|
else if (type.includes('Length')) {
|
||||||
const crtrackClipIndex = getCrtrackClipIndex(index) + 1 // XML 1-based index
|
const crtrackClipIndex = getCrtrackClipIndex(index) + 1 // XML 1-based index
|
||||||
const clipLength = xpath.select1(`//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[${crtrackClipIndex}]/@Length`, gmepXmlDoc);
|
const clipLength = xpath.select1(`//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[${crtrackClipIndex}]/@Length`, gmepXmlDoc);
|
||||||
|
|
||||||
userAnswer = parseInt(clipLength.value, 10);
|
userAnswer = parseInt(clipLength.value, 10);
|
||||||
|
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||||
|
}
|
||||||
|
// 자막 텍스트(글자, 폰트, 크기, 색상) [2-5, 6, 7, 8] [2-22, 23, 24, 25]
|
||||||
|
else if (type.includes('Text') || type.includes('Color')) {
|
||||||
|
const xmlIndex = index + 1 // XML 1-based index
|
||||||
|
const subtitleXpath = ele?.replace(/{index}/g, xmlIndex);
|
||||||
|
const subtitleResult = xpath.select1(subtitleXpath, gmepXmlDoc);
|
||||||
|
|
||||||
|
if (subtitleResult) {
|
||||||
|
if (type.includes('Color')) {
|
||||||
|
const hex = convertColorToHex(subtitleResult.value);
|
||||||
|
userAnswer = hex;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
userAnswer = subtitleResult.value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userAnswer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 자막 위치 [2-9] (화면 정가운데 아래)
|
||||||
|
// 정답 좌표를 기준으로
|
||||||
|
else if (type.includes('Location')) {
|
||||||
|
const xmlIndex = index + 1 // XML 1-based index
|
||||||
|
const subtitleXpath = ele?.replace(/{index}/g, xmlIndex);
|
||||||
|
const subtitleResult = xpath.select(subtitleXpath, gmepXmlDoc);
|
||||||
|
|
||||||
|
userAnswer = subtitleResult.map(r => r.value);
|
||||||
|
const errorRange = 0.1;
|
||||||
|
|
||||||
|
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult, tolerance = errorRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
userAnswer = null;
|
userAnswer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type == "color") {
|
else if (type == "color") {
|
||||||
|
|||||||
2
z.xbook
2
z.xbook
@@ -1 +1 @@
|
|||||||
[{"kind":2,"language":"xpath","value":"//Layer[Name[@value='Tracking']]/Effects/Item/Name/@value"},{"kind":2,"language":"xpath","value":"sum(//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[@ClipIndex=0]/preceding-sibling::CRTrackClip/@Length)"},{"kind":2,"language":"xpath","value":"//CROwneUnit/CRCUnitArr[@Name=\"아름다운 꽃 축제 (Happy Flower Festival)\"]/@Name"},{"kind":2,"language":"xpath","value":"//Layer[Name[@value='{layer}']]/Effects/Item[EffectData/{option}]/Name/@value | //Layer[Name[@value='{layer}']]/Effects/Item/EffectData/{option}/@value"},{"kind":2,"language":"xpath","value":"//Layer[Name[@value='{layer}']]/Effects/Item/Name/@value | //Layer[Name[@value='{layer}']]/Effects/Item/EffectData/{option}/@value\r\n"},{"kind":2,"language":"xpath","value":"//CRTransFilter[@ClipIndex=count(//CRTrackList[@Name='비디오1']/CRTrackClip[@ClipIndex=count(//CRClip[@Path='이미지3.jpg']/preceding-sibling::CRClip | //CRClip[@Type='11']/CRCUnitArr[@Path='이미지3.jpg']/../preceding-sibling::CRClip)][1]/preceding-sibling::CRTrackClip)][@Type='2']/@*[name()='ID' or name()='Range' or name()='Type']"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[1]/@Length"},{"kind":2,"language":"xpath","value":"//CRClipArr/CRClip[@Type='11']/CRCUnitArr/@Path | //CRClipArr/CRClip[not(@Type='11')]/@Path"}]
|
[{"kind":2,"language":"xpath","value":"//Layer[Name[@value='Tracking']]/Effects/Item/Name/@value"},{"kind":2,"language":"xpath","value":"sum(//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[@ClipIndex=0]/preceding-sibling::CRTrackClip/@Length)"},{"kind":2,"language":"xpath","value":"//CROwneUnit/CRCUnitArr[@Name=\"아름다운 꽃 축제 (Happy Flower Festival)\"]/@Name"},{"kind":2,"language":"xpath","value":"//Layer[Name[@value='{layer}']]/Effects/Item[EffectData/{option}]/Name/@value | //Layer[Name[@value='{layer}']]/Effects/Item/EffectData/{option}/@value"},{"kind":2,"language":"xpath","value":"//Layer[Name[@value='{layer}']]/Effects/Item/Name/@value | //Layer[Name[@value='{layer}']]/Effects/Item/EffectData/{option}/@value\r\n"},{"kind":2,"language":"xpath","value":"//CRTransFilter[@ClipIndex=count(//CRTrackList[@Name='비디오1']/CRTrackClip[@ClipIndex=count(//CRClip[@Path='이미지3.jpg']/preceding-sibling::CRClip | //CRClip[@Type='11']/CRCUnitArr[@Path='이미지3.jpg']/../preceding-sibling::CRClip)][1]/preceding-sibling::CRTrackClip)][@Type='2']/@*[name()='ID' or name()='Range' or name()='Type']"},{"kind":2,"language":"xpath","value":"//CROwneUnit[1]/CRCUnitArr/@*[name()='VID600' or name()='VID601']"},{"kind":2,"language":"xpath","value":"//CRClipArr/CRClip[@Type='11']/CRCUnitArr/@Path | //CRClipArr/CRClip[not(@Type='11')]/@Path"}]
|
||||||
Reference in New Issue
Block a user