* fix(deps): pin dependencies
* fix: stop passing log_level to FastAPI
FastAPI never had a log_level parameter; it went into **extra and was
ignored until 0.162 removed **extra, where it raises TypeError at import.
uvicorn.run already receives the log level.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ThePhaseless <kukubaorch@gmail.com>
* chore(deps): lock file maintenance
* fix: fall back to the host timezone when the egress lookup raises
invisible-core 49.46 always looks up the egress IP and raises
GeoTimezoneError when an "auto" lookup fails, instead of returning an
empty zone. Catch it so a failed lookup uses the host timezone again
rather than failing the request, update the explicit-settings test to
the new always-lookup behaviour, and correct the README.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ThePhaseless <kukubaorch@gmail.com>
The fallback zone used after a failed egress lookup came from hand-rolled
TZ and /etc/localtime parsing. That parsing passed values such as
TZ=UTC0, TZ=CET-1CEST,M3.5.0,M10.5.0/3 or TZ=:/usr/share/zoneinfo/... to
the browser unchanged, and Playwright rejects them as an invalid
timezone ID. tzlocal.get_localzone_name() handles those forms and returns
only names it could load, or None. A None result still falls back to UTC.
tzlocal already came in through trafilatura (htmldate -> dateparser).
It is now a direct dependency because Byparr imports it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
invisible_playwright drives Firefox through a vendored copy of Playwright
(invisible_playwright._pw), and its Error/TimeoutError are not subclasses
of playwright.async_api's classes or of the builtin TimeoutError. Every
except and suppress in Byparr named the playwright.async_api classes, so
in production none of them matched:
- a Turnstile widget read that outlasted its 1s timeout escaped the
suppress() in the solve loop and turned the whole request into a 500
(the Locator.bounding_box traceback in #419), even with budget left
- an unreachable host was a 500 instead of a 502
- a goto/domcontentloaded timeout was a 500 instead of a 408
- a page that never reaches networkidle was a 500 instead of returning
the loaded page, and /load returned empty content for it
Nothing in Byparr runs the real playwright package at runtime any more;
it is only used for type hints. So the handlers now import the classes
invisible_playwright re-exports from its public async_api, and nothing
else. That module exports them since 0.15.0, hence the raised floor.
The unit-test fakes raised the playwright.async_api classes too, which
is why the suite stayed green. They now raise the vendored classes, and
a new test covers a widget read timing out mid-solve.
Refs #419
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host that will not resolve arrived as a plain PlaywrightError, which
nothing caught, so Byparr answered 500 and read as its own defect. It is
the upstream that failed, and the failure is usually transient, so 502
also lets clients retry where a 4xx would tell them not to. The timeout
branch stays ahead of it, since PlaywrightTimeoutError subclasses Error.
Run trafilatura server-side on the rendered DOM (page.content()), so
JS-rendered pages stay fully visible to the extractor; fall back to
innerText when trafilatura cannot score any main content.
- Add pydantic-settings as direct dependency
- Replace os.getenv calls with typed Settings class
- Add BLOCK_MEDIA and RETURN_ONLY_COOKIES env defaults
- Replace camoufox[geoip] with invisible_playwright git dependency
- Switch playwright-captcha framework from CAMOUFOX to PLAYWRIGHT
- Remove camoufox addon path from consts
- Add git to Docker base image; fetch invisible_playwright binary
- Make /cache writable for runtime USER 1000
cloverlabs-camoufox 0.6.0 was a confirmed regression (3/6 tests failed
with 'Cloudflare iframes not found' vs 6/6 passing on camoufox 0.4.11).
Revert to camoufox[geoip]==0.4.* and pin playwright==1.60.* (exact pin
to avoid the 1.61 protocol error).