refactor: improve image dimension inference logic

Enhances the algorithm for extracting image dimensions from Telegram widget styles by introducing a more robust fallback mechanism. Previously relied on partial style matching which could miss dimension data encoded across multiple DOM elements.

Now checks wrapper elements and padding-top percentage ratios more systematically to synthesize layout dimensions when explicit width/height are unavailable. This ensures more reliable image aspect ratio calculation for content rendering.

Also clarifies error messages in the sync workflow and updates code comments from Chinese to English for better maintainability.
This commit is contained in:
面条
2026-03-27 13:46:55 +08:00
parent 39b27a5aaf
commit b800869aa5
3 changed files with 38 additions and 14 deletions

View File

@@ -35,6 +35,6 @@ jobs:
- name: Sync check
if: failure()
run: |
echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次。"
echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork."
echo "[Error] GitHub paused this scheduled sync because the upstream workflow file changed."
echo "[Error] Manually sync your fork once to re-enable future automatic updates."
exit 1

View File

@@ -96,7 +96,7 @@ const commentsClass = 'ml-[3px] border-l-2 border-line pb-6 pl-[15px] pt-[6px] s
{
COMMENTS && isItem && (
<section class={commentsClass} aria-label="Comments">
{/* Telegram 评论是当前唯一保留的客户端 JS 例外。 */}
{/* Telegram comments are the only intentional client-side JS exception. */}
<script
is:inline
async

View File

@@ -13,6 +13,7 @@ const STYLE_DIMENSION_REGEX = {
height: /height:\s*(\d+(?:\.\d+)?)px/i,
} as const
const STYLE_PADDING_TOP_REGEX = /padding-top:\s*(\d+(?:\.\d+)?)%/i
const SYNTHETIC_IMAGE_DIMENSION = 1000
const TITLE_PREVIEW_REGEX = /^.*?(?=[。\n]|http\S)/g
const CONTENT_URL_REGEX = /(url\(["'])((https?:)?\/\/)/g
const UNNECESSARY_HEADERS = new Set(['host', 'cookie', 'origin', 'referer'])
@@ -115,32 +116,55 @@ function getStyleDimension(style: string | undefined, property: 'width' | 'heigh
return value ? Math.round(Number(value)) : null
}
function getImageDimensions(
function getStylePaddingTop(style: string | undefined): number | null {
const value = style?.match(STYLE_PADDING_TOP_REGEX)?.[1]
return value ? Number(value) : null
}
// Telegram widgets encode image ratios in styles, so this returns synthetic
// dimensions for layout reservation rather than real pixel dimensions.
function inferImageDimensions(
$: CheerioAPI,
node: AnyNode,
fallback = { width: 1200, height: 1200 },
fallback = { width: SYNTHETIC_IMAGE_DIMENSION, height: SYNTHETIC_IMAGE_DIMENSION },
): { width: number, height: number } {
const element = $(node)
const styles = [
element.attr('style'),
element.find('.tgme_widget_message_photo').first().attr('style'),
element.find('i').attr('style'),
element.parent().attr('style'),
]
let width: number | null = null
let height: number | null = null
let paddingTop: number | null = null
for (const style of styles) {
const width = getStyleDimension(style, 'width')
const height = getStyleDimension(style, 'height')
if (width === null) {
width = getStyleDimension(style, 'width')
}
if (height === null) {
height = getStyleDimension(style, 'height')
}
if (paddingTop === null) {
paddingTop = getStylePaddingTop(style)
}
if (width && height) {
return { width, height }
}
}
const paddingTop = style?.match(STYLE_PADDING_TOP_REGEX)?.[1]
if (width && paddingTop) {
return {
width,
height: Math.round(width * Number(paddingTop) / 100),
}
// Telegram commonly uses wrap width plus child padding-top to express image
// ratio instead of returning real pixel dimensions.
if (paddingTop !== null) {
const syntheticWidth = width ?? fallback.width
return {
width: syntheticWidth,
height: Math.max(1, Math.round(syntheticWidth * paddingTop / 100)),
}
}
@@ -225,7 +249,7 @@ function getImages($: CheerioAPI, message: MessageSelection, options: MessageAss
}
const popoverId = `modal-${id}-${photoIndex}`
const { width, height } = getImageDimensions($, photoNode)
const { width, height } = inferImageDimensions($, photoNode)
fragments.push(`
<button
type="button"