{"id":48986318,"url":"https://github.com/GeiserX/smart-covers","last_synced_at":"2026-05-04T16:01:04.803Z","repository":{"id":340792734,"uuid":"1167556867","full_name":"GeiserX/smart-covers","owner":"GeiserX","description":"Jellyfin plugin for fallback cover extraction from PDF, EPUB, and audiobook files","archived":false,"fork":false,"pushed_at":"2026-04-22T16:05:25.000Z","size":126,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-22T18:08:52.803Z","etag":null,"topics":["audiobook","book","cover-art","csharp","docker","dotnet","ebook","epub","ffmpeg","hacktoberfest","homelab","jellyfin","jellyfin-plugin","library-management","media-management","media-server","metadata","open-source","pdf","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GeiserX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"geiserx","patreon":"geiser","buy_me_a_coffee":"geiser","thanks_dev":"u/gh/geiserx"}},"created_at":"2026-02-26T12:29:27.000Z","updated_at":"2026-04-22T16:04:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/GeiserX/smart-covers","commit_stats":null,"previous_names":["geiserx/jellyfin-plugin-pdf-cover","geiserx/jellyfin-plugin-book-cover","geiserx/smart-covers"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/GeiserX/smart-covers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2Fsmart-covers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2Fsmart-covers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2Fsmart-covers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2Fsmart-covers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GeiserX","download_url":"https://codeload.github.com/GeiserX/smart-covers/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2Fsmart-covers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32614385,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"ssl_error","status_checked_at":"2026-05-04T10:08:02.005Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audiobook","book","cover-art","csharp","docker","dotnet","ebook","epub","ffmpeg","hacktoberfest","homelab","jellyfin","jellyfin-plugin","library-management","media-management","media-server","metadata","open-source","pdf","self-hosted"],"created_at":"2026-04-18T13:00:27.678Z","updated_at":"2026-05-04T16:01:04.783Z","avatar_url":"https://github.com/GeiserX.png","language":"C#","funding_links":["https://github.com/sponsors/geiserx","https://patreon.com/geiser","https://buymeacoffee.com/geiser","https://thanks.dev/u/gh/geiserx"],"categories":["🧩 Plugins"],"sub_categories":["📚 Library Management"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/banner.svg\" alt=\"smart-covers banner\" width=\"900\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eCover extraction for Jellyfin libraries with online fallback\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/GeiserX/smart-covers/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/GeiserX/smart-covers?style=flat-square\u0026color=6B4C9A\" alt=\"Latest Release\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/GeiserX/smart-covers/actions/workflows/build.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/GeiserX/smart-covers/build.yml?branch=main\u0026style=flat-square\u0026label=tests\" alt=\"Tests\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/GeiserX/smart-covers/actions\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/GeiserX/smart-covers/build.yml?branch=main\u0026style=flat-square\" alt=\"Build Status\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/GeiserX/smart-covers/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/GeiserX/smart-covers?style=flat-square\u0026color=AA5CC3\" alt=\"License\"/\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Jellyfin-10.11%2B-6B4C9A?style=flat-square\" alt=\"Jellyfin 10.11+\"/\u003e\n  \u003cimg src=\"https://img.shields.io/badge/.NET-9.0-512BD4?style=flat-square\" alt=\".NET 9.0\"/\u003e\n  \u003ca href=\"https://github.com/awesome-jellyfin/awesome-jellyfin#readme\"\u003e\u003cimg src=\"https://img.shields.io/badge/listed%20on-awesome--jellyfin-00a4dc?style=flat-square\u0026logo=jellyfin\u0026logoColor=white\" alt=\"listed on awesome-jellyfin\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/GeiserX/smart-covers\"\u003e\u003cimg src=\"https://codecov.io/gh/GeiserX/smart-covers/graph/badge.svg\" alt=\"codecov\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nA Jellyfin plugin that provides **cover-image extraction** for books, audiobooks, comics, magazines, and music libraries. It works alongside built-in providers as a safety net: when they fail to find a cover -- or crash on mislabeled embedded art -- SmartCovers steps in. As a final fallback, it can search **Open Library** and **Google Books** for cover images automatically.\n\n## Supported Formats\n\n| Format | Type | Extraction Method |\n|--------|------|-------------------|\n| PDF | Book / Magazine / Comic | First-page rendering via built-in PDFium (no external tools needed) |\n| EPUB | Book | Archive introspection with 3-tier image search |\n| MP3 | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| M4A / M4B | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| FLAC | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| OGG / Opus | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| WMA | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| AAC | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| WAV | Audiobook / Music | Embedded art via `ffmpeg` raw stream copy |\n| Folder | Audiobook | Sidecar image lookup, then first-file embedded art |\n| Any | All | Online fallback via Open Library and Google Books |\n\n## How It Works\n\n### PDF -- First Page Rendering\n\nThe plugin renders the first page of a PDF as a JPEG using a bundled PDFium native library (via the [PDFtoImage](https://www.nuget.org/packages/PDFtoImage) NuGet package). No external tools like `poppler-utils` or `pdftoppm` are required. DPI is configurable, and a per-render timeout prevents hangs on malformed files. The native library is included for Linux (x64, arm64, musl), macOS, and Windows.\n\n### EPUB -- 3-Tier Archive Search\n\nEPUBs are ZIP archives. When other plugins fail to extract a cover, SmartCovers opens the archive and searches with three strategies, in order:\n\n1. **By filename** -- files explicitly named `cover`, `portada`, `front`, `frontcover`, or `book_cover` (with any image extension).\n2. **By path** -- any image file with `cover` in its full archive path (e.g., `OEBPS/Images/cover-image.jpg`).\n3. **By size** -- the largest image in the archive (above 5 KB, to skip icons and logos).\n\n### Audio -- Raw Stream Copy with Magic-Byte Detection\n\nJellyfin's built-in Image Extractor uses ffmpeg to *decode* embedded artwork. This fails when the codec tag does not match the actual data -- a common problem in MP3 files where JPEG cover art is tagged as PNG in ID3 metadata.\n\nSmartCovers sidesteps this entirely by using `ffmpeg -vcodec copy` to **raw-copy** the embedded image stream without decoding. It then identifies the actual format by inspecting magic bytes:\n\n| Magic Bytes | Detected Format |\n|-------------|-----------------|\n| `FF D8 FF` | JPEG |\n| `89 50 4E 47` | PNG |\n| `47 49 46` | GIF |\n| `52 49 46 46 ... 57 45 42 50` | WebP |\n\nAny leading null/padding bytes injected by the raw stream copy are stripped automatically.\n\n### Folder-Based Audiobooks\n\nFor multi-file audiobooks stored as a directory of chapter files, the plugin:\n\n1. Checks for sidecar images in the folder (`cover.jpg`, `folder.jpg`, `front.jpg`, `poster.jpg`, `thumb.jpg`).\n2. Falls back to extracting embedded art from the first audio file in the directory.\n\n### Online Cover Fetching (Last Resort)\n\nWhen all local extraction methods fail, the plugin can search online sources for a matching cover:\n\n1. **Open Library** (openlibrary.org) -- searched first, using title and author metadata.\n2. **Google Books** (books.google.com) -- searched as a fallback, preferring the highest-resolution image available.\n3. If author-qualified search finds nothing, a title-only retry is attempted on Open Library.\n\nThe plugin parses clean titles and authors from item metadata, stripping common audiobook filename noise (format tags like `(Mp3)`, locale tags like `[Castellano]`, Audible codes, year suffixes, and series indicators). No API keys are required. Fetched covers are cached by Jellyfin after the first scan, so online lookups only happen once per item.\n\nThis feature is **enabled by default** and can be toggled in the plugin settings.\n\n## Installation\n\n### From Plugin Repository (Recommended)\n\nAdd the following repository URL in **Dashboard \u003e Plugins \u003e Repositories**:\n\n```\nhttps://geiserx.github.io/smart-covers/manifest.json\n```\n\nThen install **SmartCovers** from the plugin catalog and restart Jellyfin.\n\n### From Releases\n\n1. Download `smart-covers_7.0.0.0.zip` from the [latest release](https://github.com/GeiserX/smart-covers/releases/latest).\n2. Extract the contents into your Jellyfin plugins directory:\n   ```\n   \u003cjellyfin-config\u003e/plugins/SmartCovers_7.0.0.0/\n   ```\n   The zip contains `SmartCovers.dll`, `PDFtoImage.dll`, and native PDFium libraries for all platforms under `runtimes/`.\n3. Restart Jellyfin.\n\n### Building from Source\n\n```bash\ndotnet publish SmartCovers/SmartCovers.csproj -c Release -o publish\n```\n\nThe output will be in the `publish/` directory. Copy `SmartCovers.dll`, `PDFtoImage.dll`, and the `runtimes/` folder containing native PDFium libraries to your plugins directory.\n\n## Requirements\n\n| Dependency | Required For | Notes |\n|------------|-------------|-------|\n| Jellyfin 10.11+ | All features | Minimum supported server version |\n| `ffmpeg` | Audio covers | Bundled with Jellyfin Docker images |\n| [Bookshelf plugin](https://github.com/jellyfin/jellyfin-plugin-bookshelf) v13+ | EPUB covers | Recommended; handles standard EPUB covers as primary provider |\n\nPDF rendering requires no external dependencies -- the native PDFium library is bundled with the plugin for all platforms (Linux x64/arm64, macOS, Windows).\n\n## Configuration\n\nAfter installation, configure the plugin in **Dashboard \u003e SmartCovers** (appears in the sidebar):\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| Online Cover Fetching | Enabled | Search Open Library and Google Books when local extraction fails. No API key needed. |\n| DPI | 150 | Resolution for PDF first-page rendering. Higher values produce sharper covers at the cost of speed. |\n| Timeout | 30 s | Maximum time allowed per extraction. Applies to both PDF rendering and `ffmpeg`. |\n\n### Per-Library Enable/Disable\n\nThe plugin settings page includes a **Libraries** section where you can enable or disable SmartCovers for each library directly -- no need to navigate to individual library settings. A **Refresh Images** button is available for enabled libraries.\n\n## Troubleshooting\n\n**PDF covers are not extracted**\n- Check the plugin config page -- it shows whether the PDFium native library loaded successfully.\n- Check the Jellyfin log for `PDFium native library failed to load`.\n\n**Audio covers are not extracted**\n- Confirm `ffmpeg` is available: run `which ffmpeg` inside the container.\n- Check the Jellyfin log for `ffmpeg not found`.\n\n**Covers appear for some items but not others**\n- The plugin only acts as a fallback. If a higher-priority provider already supplied a cover, this plugin will not run.\n- To force re-extraction, delete the existing cover image for the item in Jellyfin and rescan the library.\n\n**Extracted cover looks corrupted**\n- This is rare but can happen if the embedded art stream contains unusual padding. Open an issue with the file format details and the Jellyfin log output.\n\n**Online covers are not being fetched**\n- Check that \"Enable online cover fetching\" is toggled on in the plugin settings.\n- Verify the Jellyfin server has outbound internet access (the plugin queries `openlibrary.org` and `googleapis.com`).\n- Items that already have a cover from a higher-priority provider will not trigger online fetching. Delete the existing cover and rescan to force it.\n\n## Other Jellyfin Projects by GeiserX\n\n- [quality-gate](https://github.com/GeiserX/quality-gate) — Restrict users to specific media versions based on configurable path-based policies\n- [whisper-subs](https://github.com/GeiserX/whisper-subs) — Automatic subtitle generation using local AI models powered by whisper.cpp\n- [jellyfin-encoder](https://github.com/GeiserX/jellyfin-encoder) — Automatic 720p HEVC/AV1 transcoding service with hardware acceleration\n- [jellyfin-telegram-channel-sync](https://github.com/GeiserX/jellyfin-telegram-channel-sync) — Sync Jellyfin access with Telegram channel membership\n\n## License\n\nThis project is licensed under the [GNU General Public License v3.0](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGeiserX%2Fsmart-covers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGeiserX%2Fsmart-covers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGeiserX%2Fsmart-covers/lists"}