RepoPilotOpen in app →

advplyr/audiobookshelf

Self-hosted audiobook and podcast server

Healthy

Healthy across the board

weakest axis
Use as dependencyFailing

copyleft license (GPL-3.0) — review compatibility

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 38+ active contributors
  • Distributed ownership (top contributor 46% of recent commits)
  • GPL-3.0 licensed
  • CI configured
  • Tests present
  • GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
  • Use as dependency FailingMixed if: relicense under MIT/Apache-2.0 (rare for established libs)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the “Healthy” badge

Paste into your README — live-updates from the latest cached analysis.

RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/advplyr/audiobookshelf)](https://repopilot.app/r/advplyr/audiobookshelf)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/advplyr/audiobookshelf on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: advplyr/audiobookshelf

Generated by RepoPilot · 2026-05-06 · Source

Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/advplyr/audiobookshelf shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

Verdict

GO — Healthy across the board

  • Last commit 1d ago
  • 38+ active contributors
  • Distributed ownership (top contributor 46% of recent commits)
  • GPL-3.0 licensed
  • CI configured
  • Tests present
  • ⚠ GPL-3.0 is copyleft — check downstream compatibility

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live advplyr/audiobookshelf repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/advplyr/audiobookshelf.

What it runs against: a local clone of advplyr/audiobookshelf — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in advplyr/audiobookshelf | Confirms the artifact applies here, not a fork | | 2 | License is still GPL-3.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>advplyr/audiobookshelf</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of advplyr/audiobookshelf. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/advplyr/audiobookshelf.git
#   cd audiobookshelf
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of advplyr/audiobookshelf and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "advplyr/audiobookshelf(\\.git)?\\b" \\
  && ok "origin remote is advplyr/audiobookshelf" \\
  || miss "origin remote is not advplyr/audiobookshelf (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
  && ok "license is GPL-3.0" \\
  || miss "license drift — was GPL-3.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "package.json" \\
  && ok "package.json" \\
  || miss "missing critical file: package.json"
test -f "client/components/app/Appbar.vue" \\
  && ok "client/components/app/Appbar.vue" \\
  || miss "missing critical file: client/components/app/Appbar.vue"
test -f "client/components/app/MediaPlayerContainer.vue" \\
  && ok "client/components/app/MediaPlayerContainer.vue" \\
  || miss "missing critical file: client/components/app/MediaPlayerContainer.vue"
test -f "client/components/content/LibraryItemDetails.vue" \\
  && ok "client/components/content/LibraryItemDetails.vue" \\
  || miss "missing critical file: client/components/content/LibraryItemDetails.vue"
test -f ".nuxt.config.js" \\
  && ok ".nuxt.config.js" \\
  || miss "missing critical file: .nuxt.config.js"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/advplyr/audiobookshelf"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Audiobookshelf is a self-hosted audiobook and podcast server written in Node.js/Nuxt that streams audio on-the-fly, manages multi-user libraries with per-device progress sync, and auto-downloads podcast episodes. It replaces commercial services like Audible by letting users host their own audiobook collection with metadata management, chapter editing via Audnexus API integration, and RSS feed generation. Monorepo structure: server backend (root level with Node.js/Express), Vue/Nuxt frontend in client/ directory with Tailwind CSS styling (client/assets/), component tests using Cypress (cypress/), build artifacts for deployment (build/debian for systemd packaging, Dockerfile for containerization), and devcontainer setup (.devcontainer/) for reproducible development. State management via Nuxt stores, routing via Nuxt conventions, and server API documented at api.audiobookshelf.org.

Who it's for

Self-hosted media enthusiasts and audiobook collectors who want to own their library infrastructure, manage multiple family members' listening progress across devices, and avoid DRM/subscription services. Also appeals to developers contributing to the open-source ecosystem (frontend Vue/Nuxt, backend Node.js, and mobile app developers).

Maturity & risk

Production-ready and actively maintained. The project has comprehensive CI/CD workflows (.github/workflows/ contains unit tests, integration tests, component tests, Docker builds, and CodeQL analysis), a stable v2.34.0 release cycle, multi-platform deployment options (Debian packages in build/debian/, Docker support), and demonstrates maturity through features like automated backups, PWA support, and Chromecast integration. Recent activity includes dedicated component testing infrastructure and i18n integration workflows.

Low-to-moderate risk for a self-hosted project. Dependencies are well-managed (Nuxt 2.18.1, Vue, Tailwind 4.0, hls.js, epubjs) but the project appears to be maintained by a small team (advplyr as primary author visible in repo). Risk factors: single-maintainer dependency on advplyr, reliance on third-party metadata APIs (Audnexus for chapters, metadata fetching), and the complexity of audio streaming across multiple formats. However, the comprehensive test suite and CI/CD pipeline mitigate regression risk.

Active areas of work

Active development on testing infrastructure (component-tests.yml, unit-tests.yml, integration-test.yml workflows are present), i18n integration (i18n-integration.yml), and Docker distribution (docker-build.yml). The project recently refined component testing with Cypress, supports Tailwind CSS v4 migration, and has automation for closing blank issues and releasing notifications.

Get running

git clone https://github.com/advplyr/audiobookshelf.git
cd audiobookshelf
npm install
npm run dev

Dev server starts with Nuxt on default port (3000); frontend-only development uses npm run dev. For full-stack, the backend Node.js server runs alongside.

Daily commands: Dev: npm run dev (Nuxt dev server on :3000). Production build: npm run build && npm run start. Generate static: npm run generate. Testing: npm run compile-tailwind && npm run test (Cypress component tests). Windows dev: use .devcontainer/ with VSCode dev containers, or run npm run dev2 on localhost:1337.

Map of the codebase

  • package.json — Defines Nuxt.js build config, Tailwind CSS compilation, and all client dependencies—required reading to understand the tech stack and build pipeline.
  • client/components/app/Appbar.vue — Main application shell and layout component; entry point for understanding how pages, modals, and the media player are composed together.
  • client/components/app/MediaPlayerContainer.vue — Core media playback logic for audiobooks/podcasts; critical for understanding streaming, progress tracking, and device sync.
  • client/components/content/LibraryItemDetails.vue — Central hub for displaying book/podcast metadata, chapters, and interactions; heavily used across the app and embeds complex state management.
  • .nuxt.config.js — Nuxt configuration defining routes, plugins, modules, and API proxy setup; essential for understanding how the frontend connects to the backend API.
  • client/components/controls/GlobalSearch.vue — Global search interface component; demonstrates data fetching patterns and real-time filtering used throughout the codebase.
  • client/assets/tailwind.css — Tailwind CSS entry point and design token definitions; necessary for styling consistency and understanding the visual design system.

How to make changes

Add a new Library view or filter

  1. Create a new Vue component in client/components/app/ that extends BookShelfCategorized.vue or LazyBookshelf.vue (client/components/app/YourNewViewName.vue)
  2. Add filter controls using FilterSelect.vue or LibraryFilterSelect.vue patterns (client/components/controls/FilterSelect.vue)
  3. Wire the component into Appbar.vue navigation and configure route in .nuxt.config.js (client/components/app/Appbar.vue)

Add a new media player control or feature

  1. Create a new control component in client/components/controls/ following the PlaybackSpeedControl.vue or VolumeControl.vue pattern (client/components/controls/YourNewControl.vue)
  2. Integrate the control into MediaPlayerContainer.vue and connect to the playback state (client/components/app/MediaPlayerContainer.vue)
  3. Add styling in client/assets/app.css or use Tailwind classes defined in client/assets/tailwind.css (client/assets/app.css)

Add a new card type or metadata display

  1. Create a new card component in client/components/cards/ following LazyBookCard.vue or ItemSearchCard.vue pattern (client/components/cards/YourNewCard.vue)
  2. Use BookCover.vue or AuthorImage.vue for asset rendering (client/components/covers/BookCover.vue)
  3. Integrate into appropriate parent component (e.g., BookShelfRow.vue) and add Tailwind styling (client/components/app/BookShelfRow.vue)

Add a new user modal or settings option

  1. Create a new modal component in client/components/modals/ following AccountModal.vue or AddCustomMetadataProviderModal.vue structure (client/components/modals/YourNewModal.vue)
  2. Add modal trigger button and state management in the parent component (e.g., SettingsContent.vue or Appbar.vue) (client/components/app/SettingsContent.vue)
  3. Implement form validation and API integration following axios patterns from .nuxt.config.js (.nuxt.config.js)

Why these technologies

  • Nuxt.js — Universal Vue.js framework providing SSR/SSG, automatic code-splitting, file-based routing, and middleware—ideal for a complex media app requiring SEO and fast initial loads.
  • Tailwind CSS — Utility-first CSS framework enabling rapid, consistent UI development without custom stylesheet maintenance; integrates seamlessly with Vue components via PostCSS.
  • Axios + Nuxt proxy — HTTP client with request interceptor support for auth headers and retry logic; proxy module decouples frontend from backend deployment topology.
  • Vue.js 2.x (via Nuxt) — Reactive component framework with scoped styles and composition patterns; simplifies state management for complex features like playback sync across devices.
  • Docker — Containerization for consistent deployment across Linux hosts; self-hosted nature requires reproducible environment isolation.

Trade-offs already made

  • Lazy-loaded card components (LazyBookCard, LazyBookshelf) with virtualization

    • Why: Audiobook libraries can contain thousands of items; virtualization prevents DOM bloat and keeps memory usage constant.
    • Consequence: Requires complex intersection observer logic; initially renders fewer items, then streams in on scroll—slightly slower initial paint but much better runtime performance.
  • Modular modal architecture (separate modal components per feature)

    • Why: Avoids giant monolithic settings/dialog component; cleaner composition and easier to test individual modals.
    • Consequence: Requires manual state lifting to parent containers; no centralized modal state machine, increasing boilerplate for coordinating multiple modals.
  • Cover image rendering via BookCover.vue component abstraction

    • Why: Centralizes fallback logic, lazy-loading, and error handling for cover art across the entire app.
    • Consequence: Adds a wrapping layer of indirection; every image render pays a small component overhead cost.
  • Axios HTTP client with proxy instead of direct backend calls

    • Why: undefined
    • Consequence: undefined

Traps & gotchas

WebSocket requirement: reverse proxies must support WebSocket connections (noted in README); misconfiguration breaks real-time features. Subfolder deployment: must use /audiobookshelf path exactly; other paths not supported. Metadata APIs: Audnexus API for chapter lookup and third-party metadata fetching can fail if external services are down. Audio format dependencies: streaming 'on the fly' requires FFmpeg or similar backend support (not visible in file list but essential). DEV environment: .devcontainer/post-create.sh hook may have setup requirements not obvious from package.json alone. No visible database schema files suggest data is stored in flat files or a lightweight embedded DB; this affects backup/restore procedures.

Architecture

Concepts to learn

  • HLS (HTTP Live Streaming) — Core to streaming audio on-the-fly without pre-encoding; hls.js library handles client-side HLS playback across browsers.
  • WebSocket (full-duplex communication) — Required for real-time progress sync across devices and multi-user features; reverse proxy misconfiguration breaks this.
  • Server-Sent Events (SSE) for progress syncing — Likely used alongside WebSocket for push updates of library changes and playback state; nuxt-socket-io handles this.
  • Progressive Web App (PWA) — Project supports PWA (@nuxtjs/pwa dependency), enabling offline access and installability; affects build pipeline and service worker setup.
  • RSS Feed Generation — Exposes podcasts and audiobooks as RSS feeds for third-party clients; critical for ecosystem interoperability.
  • Multi-user synchronization (CRDT-like state) — Keeping per-user progress consistent across devices without conflicts requires careful state management; Socket.IO handles real-time propagation.
  • Chromecast Protocol Integration — Casting support on web and Android apps requires Cast protocol implementation; affects how audio streaming endpoints are exposed.

Related repos

  • advplyr/audiobookshelf-app — Official mobile clients (Android/iOS) for this server; required to understand full platform scope and API contracts.
  • books-are-next/audiobookshelf-web-client — Community alternative web client; shows how to integrate with the same server API.
  • Podserve/podserve — Similar podcast/audio self-hosting solution; useful for comparing architecture choices and feature parity.
  • Vito0912/audiobookshelf-demo — Community-hosted demo instance referenced in README; shows production deployment example.
  • audnexus/audnexus-api — Third-party API integration point for chapter lookup and metadata enrichment; essential for understanding metadata features.

PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add component unit tests for client/components/app/*.vue files

The repo has Cypress component testing configured (cypress run --component) but only compiles Tailwind for tests. The client/components/app/ directory contains critical UI components (Appbar.vue, BookShelfRow.vue, MediaPlayerContainer.vue, etc.) with no visible unit test coverage. Adding Jest or Vitest unit tests for these Vue components would catch regressions early and improve code quality. This is especially valuable given the multi-user, streaming, and sync features that depend on these components working correctly.

  • [ ] Set up Jest or Vitest in package.json with Vue 2 support
  • [ ] Create test files matching client/components/app/ structure (e.g., client/components/app/tests/Appbar.spec.js)
  • [ ] Add tests for Appbar.vue (navigation state, user menu interactions)
  • [ ] Add tests for MediaPlayerContainer.vue (play/pause, progress sync)
  • [ ] Add GitHub Actions workflow (.github/workflows/component-unit-tests.yml) to run tests on PR
  • [ ] Update .github/pull_request_template.md to mention component test coverage

Add OpenAPI/Swagger integration tests with automated schema validation

The repo has a .github/workflows/lint-openapi.yml workflow but no visible integration tests that validate actual API responses against the OpenAPI spec. With features like multi-user support, progress syncing, and podcast auto-download, API contract stability is critical. Adding integration tests that call real endpoints and validate responses against the schema would catch breaking changes before release.

  • [ ] Create client/tests/api/ directory for API integration tests
  • [ ] Set up jest with supertest or axios for API testing against dev server
  • [ ] Write tests validating key endpoints: /api/users, /api/libraries, /api/podcasts, /api/progress
  • [ ] Use a tool like @apidevtools/swagger-parser to validate responses against OpenAPI schema
  • [ ] Add new GitHub Actions workflow (.github/workflows/api-integration-tests.yml) that starts server and runs tests
  • [ ] Reference existing .github/workflows/integration-test.yml pattern for consistency

Add client-side E2E tests for critical user flows using Cypress

While the repo has Cypress configured with component testing, there are no visible E2E test files covering critical user journeys (login, library browsing, playback sync across devices, podcast subscription). Given the multi-device sync and auto-download features, E2E tests would prevent regressions in core functionality. These would complement existing component tests.

  • [ ] Create cypress/e2e/ directory with test files (if not present)
  • [ ] Add cypress/e2e/auth.cy.js: login, user creation, permission tests
  • [ ] Add cypress/e2e/library.cy.js: browse books, search, filter by category
  • [ ] Add cypress/e2e/playback.cy.js: play audio, track progress, sync progress
  • [ ] Add cypress/e2e/podcasts.cy.js: subscribe, auto-download, episode management
  • [ ] Update .github/workflows/integration-test.yml or create new .github/workflows/cypress-e2e.yml to run E2E tests on PR with headless Chrome
  • [ ] Update README snippet or CONTRIBUTING guide with E2E test instructions

Good first issues

  • Add unit tests for client/assets/ CSS modules: the stylesheet files (app.css, defaultStyles.css, absicons.css) lack corresponding test coverage visible in the cypress/ directory.
  • Document the reverse proxy WebSocket setup: the README mentions WebSocket is critical but provides no example nginx/Apache configs; adding real examples to docs would help self-hosters.
  • Improve i18n coverage: the i18n-integration.yml workflow exists but no translation files are visible in the file list; identify missing locales and add string extraction guidance for new contributors.

Top contributors

Recent commits

  • 47ea6b5 — Update book/podcast scanner to sanitize description pulled from metadata (advplyr)
  • 4b060fe — Merge pull request #5221 from brandonfhall/fix/rss-feed-m4b-content-type (advplyr)
  • 40869bc — fix: set correct Content-Type for RSS feed audio files (brandonfhall)
  • 47457ee — Version bump v2.34.0 (advplyr)
  • cb6ff9e — Merge pull request #5204 from weblate/weblate-audiobookshelf-abs-web-client (advplyr)
  • 5dc0126 — Translated using Weblate (Dutch) (LvanAlphen)
  • cbc103c — Translated using Weblate (Japanese) (na3shkw)
  • e79256d — Translated using Weblate (Belarusian) (pavel-miniutka)
  • f8ef56c — Translated using Weblate (Hungarian) (ugyes)
  • 62d7097 — Add ApiCacheManager test for should remove recent-episodes cache entries (advplyr)

Security observations

  • High · Outdated and Vulnerable Dependencies — package.json - dependencies section. Multiple dependencies have known vulnerabilities: @nuxtjs/axios 5.13.6 (deprecated, has security issues), nuxt 2.18.1 (EOL version with unpatched vulnerabilities), epubjs 0.3.88 (outdated with potential XSS risks), and libarchive.js 1.3.0 (may have buffer overflow issues). Core-js 3.16.0 is significantly outdated. Fix: Update all dependencies to latest stable versions. Prioritize: nuxt to v3.x with security patches, axios to a maintained version, epubjs to 0.4.x+, and core-js to 3.35+. Run 'npm audit' and address all reported vulnerabilities.
  • High · Insecure External Binary Download in Docker Build — Dockerfile - Stage 1, curl command for libnusqlite3-linux-musl. The Dockerfile downloads pre-built binaries (libnusqlite3) from GitHub releases without verification. No checksum validation, signature verification, or HTTPS pinning is implemented. An attacker could perform MITM attacks or compromise the GitHub account to inject malicious binaries. Fix: Implement checksum verification using SHA256, store checksums separately, verify GPG signatures if available, or use package managers instead of direct binary downloads. Add: 'curl -L -o /tmp/library.zip ... && echo 'EXPECTED_HASH /tmp/library.zip' | sha256sum -c -'
  • High · Missing HEALTHCHECK in Docker Configuration — Dockerfile - missing HEALTHCHECK instruction. The Dockerfile lacks a HEALTHCHECK instruction, making it impossible for orchestration systems to detect container failures or unresponsive states. The docker-compose example also doesn't define health checks. Fix: Add HEALTHCHECK instruction to Dockerfile, e.g., 'HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:80/health || exit 1'. Add health_check to docker-compose service.
  • Medium · Potential XSS Vulnerabilities in Vue Components — client/components/cards/* and client/components/app/*. Multiple Vue components in client/components/ (LazyBookCard, ItemSearchCard, PodcastFeedSummaryCard, etc.) likely render user-generated or external content (book metadata, podcast descriptions). Without proper sanitization or v-text binding, this could allow XSS attacks, especially with epubjs and trix editor processing untrusted content. Fix: Use v-text or text interpolation instead of v-html for user content. Sanitize all external data with DOMPurify before rendering. Validate and escape podcast/book metadata server-side. Use Content Security Policy headers.
  • Medium · Unrestricted Port Exposure in Docker Compose Example — docker-compose.yml - ports section. The docker-compose.yml exposes port 13378:80 with no authentication mechanism documented at the compose level. The comment states 'ABS runs on port 13378 by default' but there's no mention of authentication or network segmentation in the provided config. Fix: Document requirement for authentication before exposing to internet. Add firewall rules in the example. Consider using a reverse proxy with authentication (nginx with OAuth2 proxy, Authelia, etc.). Recommend non-standard port changes in production documentation.
  • Medium · Incomplete Volume Permissions in Docker Compose — docker-compose.yml and Dockerfile. The docker-compose.yml mounts volumes without specifying ownership/permissions. Running as root (default in Alpine Node.js images) could lead to privilege escalation if containers are compromised. No USER directive in Dockerfile evident from provided snippet. Fix: Add 'RUN addgroup -S appgroup && adduser -S appuser -G appgroup' in Dockerfile. Set 'USER appuser' before running the application. Use volume mount options like ':z' for SELinux or ':ro' for read-only where appropriate.
  • Medium · Missing Security Headers Configuration — undefined. No evidence of security headers (CSP, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security) in the provided configuration files. This is critical for a web application handling media and user Fix: undefined

LLM-derived; treat as a starting point, not a security audit.

Where to read next


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · advplyr/audiobookshelf — RepoPilot