Skip to main content

LibreSignage

LibreSignage is an open-source digital signage web application built with Flask. The project uses Flask's application factory pattern and blueprints so features can be developed and tested independently.

Requirements

  • Python 3.10 or newer
  • Node.js 20 or newer
  • uv (recommended) or another Python package installer

Development

Clone the repository, install the locked development environment, and start Flask:

uv sync --dev
npm install
npm run build
uv run flask --app libresignage run --debug

Then open http://127.0.0.1:5000.

The content library at http://127.0.0.1:5000/content/ accepts images, video, PDFs, text, QR codes, clocks, web pages, and RSS or Atom feed URLs. Each item has a default display duration. QR codes can encode a validated website or a vCard assembled from contact name, organization, phone, email, and website fields plus an optional structured street, city, state/region, postal code, and country address. LibreSignage generates high-error-correction SVG codes locally without a third-party QR service, and caches them for offline players. Clock widgets use an IANA timezone and continue updating without a network connection. Feed widgets render safe, readable headline cards, refresh every five minutes, and retain the last successful response when the upstream provider fails. Feed downloads reject redirects, oversized responses, private-network targets, invalid XML, and unsafe link schemes. Web-page widgets remain available for sites that permit iframe embedding.

Use http://127.0.0.1:5000/content/playlists to arrange reusable content into ordered playlists, override an entry's duration, choose subtle fade, slide, or soft-zoom transitions, and configure continuous looping or one-time playback. Players skip failed media and continue through the sequence. Metadata and cached feed responses are stored in src/instance/libresignage.sqlite and uploaded files in src/instance/uploads/ by default. Deployments can set the DATABASE, UPLOAD_FOLDER, and MAX_CONTENT_LENGTH Flask configuration values to change those locations and the upload-size limit.

Uploaded videos are automatically normalized to muted WebM/VP9 so the same asset plays in browser and Linux Qt screen clients. Install ffmpeg on the LibreSignage server before accepting video uploads. Conversion runs in a background worker: the library shows its status, refreshes while work is in progress, and excludes unfinished videos from playback. Conversion behavior, the FFmpeg executable, and the 30-minute conversion timeout can be changed with LIBRESIGNAGE_VIDEO_TRANSCODING_ENABLED, LIBRESIGNAGE_FFMPEG_BINARY, LIBRESIGNAGE_FFPROBE_BINARY, and LIBRESIGNAGE_VIDEO_TRANSCODE_TIMEOUT.

The library supports case-insensitive search across titles, text or URLs, folders, and tags, plus exact folder filtering. Assets can have a folder, up to ten normalized tags, and an optional expiry date. Expired assets remain visible for auditing and cleanup but are automatically skipped during playlist, scheduled, layout, preview, and managed-screen playback. Usage badges identify every referencing playlist; deleting an in-use asset requires explicit confirmation. Edit / replace updates metadata or replaces the underlying file, text, URL, feed, QR payload, or clock timezone without changing the asset ID or its playlist positions. SHA-256 checksums reject duplicate uploaded files, and successful replacement removes the superseded managed file.

Use http://127.0.0.1:5000/content/schedules to assign playlists to date ranges, recurring weekdays, and local-time dayparts. Overnight windows are supported, and rules can target all screens or one screen group. Whole-date blackouts and playlist overrides handle holidays and one-off programming, with Bootstrap month and week calendar views for operators. The scheduled player checks for selection changes every 30 seconds while continuing its current playback if the server is temporarily unavailable.

The centralized CMS at http://127.0.0.1:5000/dashboard/ summarizes content, playlists, schedules, and registered screens. Each screen receives a permanent player URL and can use either the scheduled channel or a directly assigned playlist. Assignment changes remain staged until an operator publishes them. Connected players check for new published revisions every two seconds and atomically reload the new content. Failed checks back off to 15 seconds while players continue the last available revision. Every publish is recorded as a fleet batch on the dashboard.

Administrators can activate a full-screen emergency override from the CMS dashboard for selected screens, a reusable screen group, or the entire fleet. Critical, warning, and information styles use large high-contrast messaging and interrupt playback within the normal two-second player polling interval. The override is cached as the screen's current offline revision. The explicit Restore published playback action clears it without changing the screen's playlist, schedule, or multi-zone assignment.

Every managed screen has a Preview draft action on the CMS dashboard. Preview renders the staged playlist, currently active scheduled channel, or multi-zone layout with the same templates used by the player, without publishing, advancing its revision, or recording device contact. Operators can inspect Full HD landscape, Full HD portrait, 4K landscape, and HD landscape canvases; each uses its actual logical resolution and scales to fit the browser. External web content may block iframe previews through its own security policy.

Managed players also prepare the complete published playlist for offline use with a per-screen service worker. Local images, video, PDFs, text, and player assets are stored in revisioned browser caches with no application expiry; failed cache updates retain the last complete revision. Cached video supports byte-range playback. RSS feeds include their last successful publish-time snapshot. Web items can opt into a static publish-time screenshot when the optional Playwright capture service is enabled; live web items without one retain the explicit offline message. The player checks the revision estimate against available browser storage before caching and reports capacity failures to the dashboard. Browser storage quotas and device-level cache eviction still apply. See Reboot-safe offline player for provisioning, cold-start verification, and kiosk browser-profile requirements.

LibreSignage includes a standalone PyWebView screen client for native kiosk deployment. It stores an end-user-provided server base URL and registered screen slug, runs the resulting managed-player URL fullscreen, and uses a persistent browser profile so service workers and offline revisions survive client and system restarts. See PyWebView screen client for installation, configuration, diagnostics, renderer selection, and graphical-session autostart.

Managed players report a health heartbeat every 5 seconds with their cache revision, service-worker state, browser storage usage, network state, last played item, and last cache error. The dashboard classifies screens as online, degraded, offline, or never connected and opens persistent offline alerts after LIBRESIGNAGE_DEVICE_OFFLINE_AFTER_SECONDS (90 seconds by default). Operators can remotely restart the browser player, request Sync now, or clear its LibreSignage caches.

Managed players also retain client-reported proof-of-play events in IndexedDB while offline and upload them with authenticated heartbeats after reconnecting. The Proof of play report filters by date, screen, asset, zone, and outcome and exports the same result as CSV. A natural duration completion is recorded as played, an interrupted attempt as skipped, and a detectable media failure as an error. This is playback lifecycle evidence rather than pixel or audience verification. History is retained for 90 days by default; configure LIBRESIGNAGE_PLAYBACK_LOG_RETENTION_DAYS to use another positive number of days.

Run uv run libresignage-monitor beside the web server to deliver durable screen-health transitions, recovery, 15-minute fleet digests, hourly reminders, publish failures, video-transcode failures, and emergency activations through configured SMTP, HMAC-signed webhook, and Telegram destinations. --once performs one cycle for diagnostics, cron, or a systemd timer. A supervised long-running worker should use the same environment, working directory, user, and database as the web app. Delivery attempts are stored in SQLite and retried with bounded exponential backoff, so restarting the worker does not lose alerts.

Alert destinations and credentials use the LIBRESIGNAGE_ALERT_* variables in .env.example. SMTP supports starttls, tls, and none; webhook URLs must resolve exclusively to public addresses and require a shared signing secret; Telegram requires a bot token and comma-separated chat IDs. Webhook requests sign the exact JSON body in X-LibreSignage-Signature as sha256=<hex HMAC-SHA256>, and expose the event name in X-LibreSignage-Event. Run the following after changing alert configuration:

uv run libresignage-monitor --once

The native client can opt into independent OS command polling with configure --allow-os-reboot. The dashboard shows the reboot action only while that agent is connected. Linux uses systemctl reboot and Windows uses shutdown.exe /r /t 5; the local client account must have reboot permission.

To enable optional web screenshots, install the extra and its Chromium binary, then set LIBRESIGNAGE_WEB_SNAPSHOTS_ENABLED=true:

uv sync --extra web-snapshots
uv run playwright install chromium

The screen inventory at http://127.0.0.1:5000/dashboard/screens stores each display's stable identity, location, operator notes, and reusable group membership. Operators can stage a scheduled channel or direct playlist for explicitly selected screens, one group, or the complete fleet. Bulk targeting resolves to concrete screen IDs when staged; the existing publish action then delivers those assignments to players.

Each inventory record also stores a logical display profile: width, height, derived landscape or portrait orientation, and media scaling. Common Full HD, 4K, and HD presets are available alongside custom dimensions from 320 to 7,680 pixels. Fit with letterboxing preserves the whole image, Fill and crop covers the display, and Stretch to fill matches its bounds. Managed playback exposes this metadata, applies the chosen image/video scaling, and rearranges split multi-zone layouts vertically for portrait screens. Draft preview starts with the saved profile and can temporarily compare other resolutions and scaling modes.

Multi-zone layouts at http://127.0.0.1:5000/content/layouts use fixed, predictable templates: 70/30 split, main with ticker, and 70/30 split with ticker. Each zone owns an independent playlist, duration loop, transition, and failure-recovery cycle. Layouts can be staged to individual screens, groups, or the full fleet and their complete local media set is included in offline caching.

Accessible content templates at http://127.0.0.1:5000/content/templates provide announcement, promotion, and information-notice starting points. Operators substitute labeled text fields, brand and text colors, and an optional image—no design software is required. LibreSignage enforces WCAG AA 4.5:1 text contrast, requires descriptive alternative text for uploaded template images, and safely limits text lengths. Created designs become normal library items, so they can be reused in playlists, schedules, and multi-zone layouts and are included in managed-player offline caches. Restaurant-focused presets add a structured six-item menu board, daily special, combo deal, and food-truck stop announcement with validated prices and menu rows.

The CMS requires an individual user account. On a new installation, visit http://127.0.0.1:5000/auth/setup once to create the first administrator. Administrators manage accounts at http://127.0.0.1:5000/auth/users. Passwords must contain at least 12 characters and are stored as salted hashes. The built-in roles are:

  • Admin: manage users, devices, groups, remote commands, and publishing.
  • Editor: create and update content, playlists, layouts, schedules, and staged screen assignments, but cannot publish or administer users/devices.
  • Viewer: inspect the CMS without making changes.

Managed player URLs, media delivery, offline assets, and status checks remain available without an interactive login so kiosk devices can recover after a reboot. Health heartbeats and remote-command delivery require the per-screen bearer token shown to administrators in the screen inventory. Existing native clients must be reconfigured with that token after upgrading. Other CMS routes require authentication, state-changing browser requests use CSRF protection, and failed logins are throttled by username and client address. Production startup fails unless LIBRESIGNAGE_SECRET_KEY is explicitly configured.

Configuration profiles live in src/libresignage/config.py. LibreSignage uses the development profile by default; set LIBRESIGNAGE_CONFIG to development, testing, or production, or pass config_name to the factory:

app = create_app(config_name="production")

The shared configuration also reads LIBRESIGNAGE_SECRET_KEY, LIBRESIGNAGE_DATABASE, LIBRESIGNAGE_UPLOAD_FOLDER, and LIBRESIGNAGE_MAX_CONTENT_LENGTH from the environment. Template-bound Google Sheets rows use a last-known-good cache. Configure its refresh interval, HTTP timeout, and maximum downloaded CSV size with LIBRESIGNAGE_TEMPLATE_DATA_REFRESH_SECONDS, LIBRESIGNAGE_TEMPLATE_DATA_TIMEOUT_SECONDS, and LIBRESIGNAGE_TEMPLATE_DATA_MAX_BYTES. LIBRESIGNAGE_TIMEZONE sets the default IANA timezone shown when creating a schedule. LIBRESIGNAGE_DEVICE_OFFLINE_AFTER_SECONDS controls the device heartbeat timeout. LIBRESIGNAGE_TRUSTED_PROXY_COUNT tells login throttling how many controlled reverse-proxy hops to trust when resolving the client address; leave it at zero for direct connections. Copy .env.example to .env for local development, replace the sample secret, and start Flask from the project root. LibreSignage automatically loads that file; variables already exported by the operating system take precedence.

cp .env.example .env

Local CI verification

Before pushing, run the same locked dependency, test, lint, and package checks used by GitHub Actions with one command:

./scripts/check-ci.sh

The script stops at the first failure and builds packages in a temporary directory that it removes automatically. To run or troubleshoot individual checks:

uv lock --check
uv sync --locked --dev
uv run --locked pytest
uv run --locked ruff check .
uv build --out-dir /tmp/libresignage-dist
uv run --locked twine check /tmp/libresignage-dist/*

If uv lock --check reports that the lockfile needs to be updated after a dependency or project-version change, run uv lock, review and commit the resulting uv.lock change, then repeat the verification commands.

Frontend styles live in src/libresignage/static/scss/app.scss. It imports Bootstrap's Sass source so Bootstrap variables can be overridden before the import. Run npm run watch:css while editing styles, or npm run build to compile the production CSS and copy Bootstrap's bundled JavaScript.

The package also exposes libresignage:create_app for WSGI servers:

from libresignage import create_app

app = create_app()

Building and publishing

Build both distribution formats with uv build. GitHub Actions validates every push to a supported working branch and every pull request into main. Work in short-lived feature/* or fix/* branches and merge them through a pull request. See CONTRIBUTING.md for the branch and release workflow.

Publishing is deliberately release-driven so ordinary pushes can never upload an immutable package version. After the version change has passed CI and been merged to main, publish a GitHub Release whose tag is exactly v followed by the version in pyproject.toml (for example, v0.5.0). The Publish to PyPI workflow verifies that the tagged commit belongs to main, builds and checks the distributions, and uploads them using PyPI Trusted Publishing through the protected pypi GitHub environment.

License

LibreSignage is distributed under the MIT License. See LICENSE.

Release files for libresignage 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for libresignage 0.7.0
File Size Uploaded
libresignage-0.7.0.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for libresignage 0.7.0
File Interpreter ABI Platform
libresignage-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.4 MB

Release files / libresignage-0.7.0.tar.gz

Download URL libresignage-0.7.0.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
5225229b0d09fbeef511fc02a28252d2afa79f16e5e6eb1253c0febf09add8de
BLAKE2b-256 checksum
How to use checksums
3062a8d6280b9dbf9bbd9fb689a20ce0cf018aeddcc2e856db871fb5b4a144e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / libresignage-0.7.0-py3-none-any.whl

Download URL libresignage-0.7.0-py3-none-any.whl
Size 1.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
87a0f3297a1e12221af079c9d75df8bc7e172b0f0c330b72f46629a967a46992
BLAKE2b-256 checksum
How to use checksums
32cc951cdef562863788f6e57696a6f20f41709dfe8e490359295183f9495c8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page