diff --git a/changedetectionio/content_fetchers/playwright.py b/changedetectionio/content_fetchers/playwright.py index 83c3f31e..458f239d 100644 --- a/changedetectionio/content_fetchers/playwright.py +++ b/changedetectionio/content_fetchers/playwright.py @@ -26,6 +26,7 @@ async def capture_full_page_async(page, screenshot_format='JPEG'): step_size = SCREENSHOT_SIZE_STITCH_THRESHOLD # Size that won't cause GPU to overflow screenshot_chunks = [] y = 0 + elements_locked = False if page_height > page.viewport_size['height']: @@ -35,6 +36,7 @@ async def capture_full_page_async(page, screenshot_format='JPEG'): with open(lock_elements_js_path, 'r') as f: lock_elements_js = f.read() await page.evaluate(lock_elements_js) + elements_locked = True logger.debug("Element dimensions locked before screenshot capture") @@ -72,6 +74,14 @@ async def capture_full_page_async(page, screenshot_format='JPEG'): # Restore original viewport size await page.set_viewport_size({'width': original_viewport['width'], 'height': original_viewport['height']}) + # Unlock element dimensions if they were locked + if elements_locked: + unlock_elements_js_path = os.path.join(os.path.dirname(__file__), 'res', 'unlock-elements-sizing.js') + with open(unlock_elements_js_path, 'r') as f: + unlock_elements_js = f.read() + await page.evaluate(unlock_elements_js) + logger.debug("Element dimensions unlocked after screenshot capture") + # If we have multiple chunks, stitch them together if len(screenshot_chunks) > 1: logger.debug(f"Screenshot stitching {len(screenshot_chunks)} chunks together") diff --git a/changedetectionio/content_fetchers/puppeteer.py b/changedetectionio/content_fetchers/puppeteer.py index 6a1ea660..70861106 100644 --- a/changedetectionio/content_fetchers/puppeteer.py +++ b/changedetectionio/content_fetchers/puppeteer.py @@ -49,6 +49,7 @@ async def capture_full_page(page, screenshot_format='JPEG'): step_size = SCREENSHOT_SIZE_STITCH_THRESHOLD # Something that will not cause the GPU to overflow when taking the screenshot screenshot_chunks = [] y = 0 + elements_locked = False if page_height > page.viewport['height']: # Lock all element dimensions BEFORE screenshot to prevent CSS media queries from resizing # capture_full_page() changes viewport height which triggers @media (min-height) rules @@ -56,6 +57,7 @@ async def capture_full_page(page, screenshot_format='JPEG'): with open(lock_elements_js_path, 'r') as f: lock_elements_js = f.read() await page.evaluate(lock_elements_js) + elements_locked = True logger.debug("Element dimensions locked before screenshot capture") if page_height < step_size: @@ -85,6 +87,14 @@ async def capture_full_page(page, screenshot_format='JPEG'): await page.setViewport({'width': original_viewport['width'], 'height': original_viewport['height']}) + # Unlock element dimensions if they were locked + if elements_locked: + unlock_elements_js_path = os.path.join(os.path.dirname(__file__), 'res', 'unlock-elements-sizing.js') + with open(unlock_elements_js_path, 'r') as f: + unlock_elements_js = f.read() + await page.evaluate(unlock_elements_js) + logger.debug("Element dimensions unlocked after screenshot capture") + if len(screenshot_chunks) > 1: from changedetectionio.content_fetchers.screenshot_handler import stitch_images_worker logger.debug(f"Screenshot stitching {len(screenshot_chunks)} chunks together") diff --git a/changedetectionio/content_fetchers/res/lock-elements-sizing.js b/changedetectionio/content_fetchers/res/lock-elements-sizing.js index 9b58e715..8954515a 100644 --- a/changedetectionio/content_fetchers/res/lock-elements-sizing.js +++ b/changedetectionio/content_fetchers/res/lock-elements-sizing.js @@ -49,11 +49,25 @@ */ (() => { + // Store original styles in a global WeakMap for later restoration + window.__elementSizingRestore = new WeakMap(); + // Lock ALL element dimensions to prevent media query layout changes document.querySelectorAll('*').forEach(el => { const computed = window.getComputedStyle(el); const rect = el.getBoundingClientRect(); + // Save original inline style values BEFORE locking + const properties = ['height', 'min-height', 'max-height', 'width', 'min-width', 'max-width']; + const originalStyles = {}; + properties.forEach(prop => { + originalStyles[prop] = { + value: el.style.getPropertyValue(prop), + priority: el.style.getPropertyPriority(prop) + }; + }); + window.__elementSizingRestore.set(el, originalStyles); + // Lock dimensions with !important to override media queries if (rect.height > 0) { el.style.setProperty('height', computed.height, 'important');