Skip to content

Preserve embedded media through extraction: iframes, video aspect ratio, raw-text escaping - #23

Draft
SunChJ wants to merge 1 commit into
onevcat:mainfrom
SunChJ:feat/preserve-video-embeds
Draft

Preserve embedded media through extraction: iframes, video aspect ratio, raw-text escaping#23
SunChJ wants to merge 1 commit into
onevcat:mainfrom
SunChJ:feat/preserve-video-embeds

Conversation

@SunChJ

@SunChJ SunChJ commented Aug 7, 2026

Copy link
Copy Markdown

Problem

Two silent content-loss bugs in the HTML→Markdown extraction pipeline:

  1. Embedded videos disappeared. @mozilla/readability strips <iframe> from its output, so video demos / interactive embeds never reached the translated page. A translated page would say "Here's Kitesurf running Doom" and show nothing — the article just ended mid-sentence.
  2. A literal <script> in prose truncated the page. The article text mentions <script> as a tag name; extraction decoded the source's &lt;script&gt; into a bare tag, which the renderer emitted as real HTML. Browsers then treated the rest of the page as a script element and swallowed all following content.

Both are reproducible on a single real-world page: https://blog.cloudflare.com/kitesurf/ (2 Cloudflare Stream video embeds + a literal <script> mention in prose).

Changes

Preserve embeds through extraction (scripts/transcrab-core.mjs)

  • Keep iframe/video/audio in the Turndown keep list.
  • Work around Readability: swap each embed (with its bare wrapper chain) for a 1px placeholder image carrying the original HTML as base64, parse, then restore.

Adapt video display to the real aspect ratio

  • Emit a standard responsive container (<div class="video-wrap" style="position:relative;width:100%;padding-top:…%"> + iframe 100%×100%) instead of carrying the source site's fixed layout (Cloudflare's padding-top:53.46%/width:84% relies on external CSS that extraction doesn't keep, so absolute iframes mis-layout).
  • Probe the video's real aspect ratio from its poster frame (JPEG/PNG header parse). Fallback chain: probed ratio → ratio implied by the original container → 16:9.
  • src/layouts/Base.astro: .video-wrap CSS fallback so absolute iframes always have a positioned container.

Escape raw-text tags in prose (scripts/transcrab-core.mjs)

  • script/style/textarea/title/… mentioned as literal text are escaped to &lt;…&gt; (fenced code blocks untouched).

Supporting changes

  • scripts/lib/translate-prompt.mjs: translation contract asks the model to keep media embed HTML unchanged.
  • scripts/verify-embed-extraction.mjs: reproducible verification scenarios.
  • test/core.test.mjs: regression tests (embed preservation, wrapper normalization, ratio computation, JPEG/PNG header parsing, raw-text escaping). npm test → 41 pass.

Before / After (extraction of https://blog.cloudflare.com/kitesurf/)

Before                          After
iframes: 0                      iframes: 2
video area: empty               <div class="video-wrap" style="position:relative;width:100%;padding-top:74.63%;…">
                                <iframe …></iframe></div>   (804×600 poster → real 4:3 ratio)
figures: 10                     figures: 10
bare <script> in prose          &lt;script&gt;  (page renders to the end)

Verification

npm test
# 41 tests pass

node scripts/verify-embed-extraction.mjs https://blog.cloudflare.com/kitesurf/ --expect-iframes 2
# -> iframes: 2, videoWraps: ["74.63%", "74.63%"], figures: 10, escapedRawTextTags: 1,
#    hasBareScriptOutsideFences: false

node scripts/verify-embed-extraction.mjs https://blog.cloudflare.com/kitesurf/ --expect-iframes 99
# -> FAIL: expected 99 iframe(s), got 2; exit code 1 (regression guard)

node scripts/verify-embed-extraction.mjs /path/to/saved-page.html
# offline / local check with any saved HTML file

Notes

  • Poster probing is best-effort and network-dependent; on failure the ratio falls back to the source container's implied ratio (e.g. 63.65%), then 16:9. The container is always valid responsive markup regardless.
  • Kept HTML blocks (figures/embeds) never contain raw-text tags, so the escaping pass cannot corrupt them.

…t ratio

- Keep iframe/video/audio embeds across the Readability + Turndown pipeline
  (Readability strips iframes, so swap them for placeholder images around
  parsing and restore them afterwards, including their sizing wrappers).
- Probe the video's real aspect ratio from its poster frame (JPEG/PNG header)
  and emit a responsive .video-wrap container (position:relative + padding-top
  + iframe 100%x100%) instead of carrying the source site's fixed layout
  (e.g. Cloudflare's padding-top:53.46% / width:84%, which depends on external
  CSS that extraction does not keep).
- Escape bare raw-text tags (script/style/textarea/title/...) that appear as
  literal prose so a browser cannot treat them as real elements and swallow the
  rest of the rendered page.
- Ask the translation model to keep media embed HTML unchanged.
- Add regression tests and a verify script with reproducible scenarios.
@SunChJ SunChJ changed the title Preserve embedded video through extraction and adapt it to real aspect ratio Preserve embedded media through extraction: iframes, video aspect ratio, raw-text escaping Aug 7, 2026
@SunChJ
SunChJ marked this pull request as draft August 7, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant