* PDF - Keep the original PDF of the newest two versions
The history snapshot of a PDF watch is only the pdftohtml text, and the "last fetched" .html.br
for a PDF is just a checksum, so the actual documents were never kept. Store {timestamp}.pdf
alongside the snapshot (same as {timestamp}.html.br does for HTML) and prune to the newest two
history entries, so the latest two versions are always available for a visual PDF compare.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Test - Browser PDF test, fix race with an immediate fallback recheck, check the original PDFs are kept
A duplicate queue entry (scheduler + import) can run the html_requests fallback check right
after the failed browser check, so by the time the test looks the error may already be cleared.
Accept both outcomes, the error message itself is covered in tests/test_pdf.py.
Also check both original PDF versions are stored, whichever fetcher got them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fetchers - Define raw_content on the base fetcher, give a clear error when a fetcher can't supply the PDF
Fetchers that never set raw_content (puppeteer, playwright, selenium) crashed with
"'fetcher' object has no attribute 'raw_content'" when the response turned out to be a PDF.
Browsers render PDFs in their built-in viewer, so all they have is the viewer HTML; now the
processor reports that plainly instead of crashing or feeding HTML to pdftohtml.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Puppeteer - Capture the real PDF body when the page turns out to be a PDF
Chrome hands a PDF straight to its built-in viewer, after which page.content and
Network.getResponseBody only return the viewer's HTML. Pause main-frame document responses
with the CDP Fetch domain and, when the Content-Type is application/pdf, copy the body out
before it is continued - a single download, and nothing needs to be known in advance.
content/raw_content then match the requests fetcher's binary handling (md5 + original bytes)
so the existing PDF processing and checksum skip work unchanged. Normal HTML pages are
continued immediately, no measurable difference in fetch time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fetchers - Add supports_pdf capability, only force html_requests for PDFs when the fetcher can't supply them
PDF watches were always forced onto html_requests. Now that puppeteer can capture the original
PDF (useful when the session needs a real browser, Cloudflare etc), only fetchers without
supports_pdf fall back - both at fetch time (resolve_content_fetcher) and in Watch.get_fetch_backend.
Also save the content-type when a fetcher couldn't supply the PDF (it was only saved on success),
otherwise watch.is_pdf never became true for URLs not ending in .pdf and the watch failed on
every check instead of falling back to html_requests on the next one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Multi-language / Translations Support (#3696)
- Complete internationalization system implemented
- Support for 7 languages: Czech (cs), German (de), French (fr), Italian (it), Korean (ko), Chinese Simplified (zh), Chinese Traditional (zh_TW)
- Language selector with localized flags and theming
- Flash message translations
- Multiple translation fixes and improvements across all languages
- Language setting preserved across redirects
Pluggable Content Fetchers (#3653)
- New architecture for extensible content fetcher system
- Allows custom fetcher implementations
Image / Screenshot Comparison Processor (#3680)
- New processor for visual change detection (disabled for this release)
- Supporting CSS/JS infrastructure added
UI Improvements
Design & Layout
- Auto-generated tag color schemes
- Simplified login form styling
- Removed hard-coded CSS, moved to SCSS variables
- Tag UI cleanup and improvements
- Automatic tab wrapper functionality
- Menu refactoring for better organization
- Cleanup of offset settings
- Hide sticky tabs on narrow viewports
- Improved responsive layout (#3702)
User Experience
- Modal alerts/confirmations on delete/clear operations (#3693, #3598, #3382)
- Auto-add https:// to URLs in quickwatch form if not present
- Better redirect handling on login (#3699)
- 'Recheck all' now returns to correct group/tag (#3673)
- Language set redirect keeps hash fragment
- More friendly human-readable text throughout UI
Performance & Reliability
Scheduler & Processing
- Soft delays instead of blocking time.sleep() calls (#3710)
- More resilient handling of same UUID being processed (#3700)
- Better Puppeteer timeout handling
- Improved Puppeteer shutdown/cleanup (#3692)
- Requests cleanup now properly async
History & Rendering
- Faster server-side "difference" rendering on History page (#3442)
- Show ignored/triggered rows in history
- API: Retry watch data if watch dict changed (more reliable)
API Improvements
- Watch get endpoint: retry mechanism for changed watch data
- WatchHistoryDiff API endpoint includes extra format args (#3703)
Testing Improvements
- Replace time.sleep with wait_for_notification_endpoint_output (#3716)
- Test for mode switching (#3701)
- Test for #3720 added (#3725)
- Extract-text difference test fixes
- Improved dev workflow
Bug Fixes
- Notification error text output (#3672, #3669, #3280)
- HTML validation fixes (#3704)
- Template discovery path fixes
- Notification debug log now uses system locale for dates/times
- Puppeteer spelling mistake in log output
- Recalculation on anchor change
- Queue bubble update disabled temporarily
Dependency Updates
- beautifulsoup4 updated (#3724)
- psutil 7.1.0 → 7.2.1 (#3723)
- python-engineio ~=4.12.3 → ~=4.13.0 (#3707)
- python-socketio ~=5.14.3 → ~=5.16.0 (#3706)
- flask-socketio ~=5.5.1 → ~=5.6.0 (#3691)
- brotli ~=1.1 → ~=1.2 (#3687)
- lxml updated (#3590)
- pytest ~=7.2 → ~=9.0 (#3676)
- jsonschema ~=4.0 → ~=4.25 (#3618)
- pluggy ~=1.5 → ~=1.6 (#3616)
- cryptography 44.0.1 → 46.0.3 (security) (#3589)
Documentation
- README updated with viewport size setup information
Development Infrastructure
- Dev container only built on dev branch
- Improved dev workflow tooling