/cache holds the browser baked into the image. A tmpfs over it hides
that browser, so Byparr downloads it again on every start (the cause
behind #439). Also move shm_size and tmpfs in compose.yaml out of the
build section, where they never applied to the running container.
invisible-core 49.46+ looks up the egress IP and checks GitHub for a newer
GeoIP database on every browser launch, so a network that blocks those hosts
delayed every request by the lookup timeouts. Memoize both lookups (success for
the process lifetime, failures retried after a backoff, proxied egress lookups
uncached), warm them up in the FastAPI lifespan before the port opens, and gate
get_browser() on the same warm-up. Drop the unused --init mode.
* 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>
Every request builds a new InvisiblePlaywright, and its __aenter__ calls
prepare_session_geo(): with the default timezone="" and no proxy that
discovers the egress IP through api.ipify.org, icanhazip.com and
checkip.amazonaws.com, then calls ensure_geoip_mmdb(), which HEADs GitHub
for a newer GeoIP release, three times per session. On a network where
those hosts are slow or blocked every request paid for it before the
browser even started. Measured in the app image: ipify black-holed adds
~5s per request, all three echo hosts black-holed ~12.5s, GitHub
black-holed ~75s. Setting BROWSER_LOCALE alone did not help, because
the timezone was still "auto".
Without a proxy the egress is the host's own, so the answer does not
change between requests. Resolve it on the first request, off the event
loop and under a lock so concurrent first requests share one lookup,
then hand the zone and the locale's primary tag to InvisiblePlaywright
explicitly; an explicit zone and tag make prepare_session_geo touch no
network. The resulting browser is the same as with "auto": passing the
primary tag back reproduces the decided language list for every Firefox
build the locale table can pick, and without a proxy the egress IP,
coordinates and srflx are not used by InvisiblePlaywright anyway.
When the lookup fails upstream falls back to the host timezone by
passing "", but passing "" again would repeat the lookup on every
request, so the host zone is named explicitly (TZ, else the
/etc/localtime link, else UTC) and the lookup is retried after ten
minutes.
Sessions behind a proxy (PROXY_SERVER or X-Proxy-Server) still resolve
per request through the proxy: each proxy has its own egress, and
upstream deliberately fails the launch when it cannot be resolved.
BROWSER_TIMEZONE mirrors BROWSER_LOCALE and wins also behind a proxy;
with both set the lookup is skipped entirely.
Fixes#416
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add /load endpoint for Open WebUI's WEB_LOADER_ENGINE=external integration.
Uses document.body.innerText for content extraction.
Configure in Open WebUI:
WEB_LOADER_ENGINE=external
EXTERNAL_WEB_LOADER_URL=http://byparr:8191/load
EXTERNAL_WEB_LOADER_API_KEY=<OWUI_API_KEY env var>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Build Docker images for PRs with branch name labels
- Remove condition preventing PR builds
- Add branch name extraction and sanitization
- Add branch labels to Docker images (org.opencontainers.image.branch and branch)
- Enable pushing of PR Docker images
- Sanitize branch names for Docker tags (replace / with -)
This allows PR images to be built and tagged with their branch names,
making it easier to test specific PR builds.
* feat: Add path filters to Docker workflow
Only build Docker images when relevant files change:
- Source code (src/**, main.py, tests/**)
- Docker configuration (Dockerfile, compose.yaml)
- Dependencies (pyproject.toml, uv.lock)
- Workflow file itself
This prevents unnecessary builds when only documentation or
other non-functional files are changed.
* feat: Add automatic cleanup of PR Docker images
Create a new workflow that automatically deletes Docker images
when a PR is closed or merged. This prevents accumulation of
old PR images in the container registry.
Features:
- Triggers on PR close/merge events
- Deletes images tagged with PR number and SHA
- Handles both architecture variants (amd64, arm64)
- Supports both organization and user repositories
- Provides detailed logging of cleanup operations
* refactor: Remove redundant branch label preparation
Remove duplicate branch name preparation step in merge-and-push job.
Branch labels are already added during the build step, so no need
to add them again when creating the manifest.
* refactor: Remove redundant label configurations
Remove custom label configurations from docker-publish workflow.
The docker/metadata-action already sets standard OCI labels by
default, so explicit label configuration is unnecessary.
Also removed unused BRANCH_TAG variable preparation.
* refactor: Use PR number for Docker image tags instead of SHA
Changed Docker image tagging strategy for pull requests:
- Use pr-{number}-{arch} for individual platform builds
- Use pr-{number} for final manifest
- Non-PR builds still use SHA-based tags
Benefits:
- Simpler, more readable tags for PRs
- Easier to identify which PR an image belongs to
- Cleanup script simplified to match only PR number tags
Updated cleanup workflow to match new tag pattern.
* docs: Add PR Docker image tags to README
Document the pr-{number} tag pattern used for pull request images.
These images are automatically built for PRs and cleaned up when
the PR is closed.
---------
Co-authored-by: Claude <noreply@anthropic.com>