( html: string, fallbackId: string, fallbackWidth: number, fallbackHeight: number, )
| 106 | } |
| 107 | |
| 108 | export function parseSubComposition( |
| 109 | html: string, |
| 110 | fallbackId: string, |
| 111 | fallbackWidth: number, |
| 112 | fallbackHeight: number, |
| 113 | ): CompositionInfo { |
| 114 | const parser = new DOMParser(); |
| 115 | const doc = parser.parseFromString(html, "text/html"); |
| 116 | const template = doc.querySelector("template"); |
| 117 | const searchDocument = template?.content ?? doc; |
| 118 | |
| 119 | // Sub-compositions may use <template> wrappers or direct divs |
| 120 | const compDiv = searchDocument.querySelector("[data-composition-id]"); |
| 121 | |
| 122 | const id = compDiv?.getAttribute("data-composition-id") ?? fallbackId; |
| 123 | const width = parseInt(compDiv?.getAttribute("data-width") ?? String(fallbackWidth), 10); |
| 124 | const height = parseInt(compDiv?.getAttribute("data-height") ?? String(fallbackHeight), 10); |
| 125 | |
| 126 | // Count timed elements inside the sub-composition |
| 127 | const searchRoot = compDiv ?? searchDocument; |
| 128 | const timedChildren = searchRoot.querySelectorAll("[data-start], .clip, .caption-group"); |
| 129 | let elementCount = timedChildren.length; |
| 130 | if (elementCount === 0 && compDiv) { |
| 131 | elementCount = countRenderableDescendants(compDiv); |
| 132 | } |
| 133 | |
| 134 | // Parse duration from the composition's own data-duration attribute |
| 135 | let duration = 0; |
| 136 | const durationAttr = compDiv?.getAttribute("data-duration"); |
| 137 | if (durationAttr && !durationAttr.startsWith("__")) { |
| 138 | duration = parseFloat(durationAttr) || 0; |
| 139 | } |
| 140 | |
| 141 | // Also check timed children for max end time |
| 142 | if (compDiv) { |
| 143 | const timedEls = compDiv.querySelectorAll("[data-start]"); |
| 144 | timedEls.forEach((el) => { |
| 145 | elementCount = Math.max(elementCount, timedEls.length); |
| 146 | const start = parseFloat(el.getAttribute("data-start") ?? "0"); |
| 147 | const endAttr = el.getAttribute("data-end"); |
| 148 | const durAttr = el.getAttribute("data-duration"); |
| 149 | |
| 150 | let end: number; |
| 151 | if (endAttr) { |
| 152 | end = parseFloat(endAttr); |
| 153 | } else if (durAttr) { |
| 154 | end = start + parseFloat(durAttr); |
| 155 | } else { |
| 156 | end = start + 5; |
| 157 | } |
| 158 | if (end > duration) { |
| 159 | duration = end; |
| 160 | } |
| 161 | }); |
| 162 | } |
| 163 | if (duration <= 0) { |
| 164 | duration = estimateDurationFromScripts(searchRoot); |
| 165 | } |
no test coverage detected