- C# 68.9%
- HTML 31.1%
Common single words (love/the/star) match ~800 items; materializing that many DTOs (~9ms each for audio) took 5-10s. The search page only shows a scroll-row per section, so the top-50 by relevance per request is plenty. Cap => common terms drop from 5-10s to 50-620ms; gibberish stays ~5-100ms (0 hits -> empty-fast); richness preserved (Artists/ Persons/Items are separate requests, each capped, so every section stays populated). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| install-plugin | ||
| src | ||
| .gitignore | ||
| Jellyfin.Plugin.Meilisearch.sln | ||
| LICENSE | ||
| README.md | ||
jellyfin-plugin-meilisearch (ekaii fork)
Fork of arnesacnussem/jellyfin-plugin-meilisearch @ tag 1.11.1.15 (Jellyfin 10.11.x, net9.0). Upstream MIT LICENSE preserved.
What this fork changes
The upstream plugin decorated IItemRepository and rewrote searchTerm searches to an id
list, but the request still went through Jellyfin's recursive, per-user Folder.GetItems
path (~0.5s per query on large libraries; Meilisearch itself answers in ~2ms). On the
cine.ekaii.fr library (~230k items) a web search fans out ~8 of those in parallel, so search
felt like 1-2s.
This fork replaces the repository decorator with a tiny ASP.NET middleware
(SearchRewriteMiddleware, wired via an IStartupFilter) that runs before MVC binding: for
GET .../Items?searchTerm=… it asks Meilisearch for the ordered id set and rewrites the
request to GET .../Items?ids=… (dropping searchTerm/recursive/includeItemTypes/sort so
the native ids path runs — ~10-50ms, and it preserves the supplied id order, i.e.
Meilisearch relevance). Typo tolerance and graceful fallback to native search (Meili down / no
hits) are kept. Measured on prod: movie/series/episode/person searches ~15-45ms (was ~500ms);
music ~200-250ms (inherent audio DTO cost; was ~850ms).
Same plugin GUID as upstream (974395db-…) so it upgrades in place.
Update (2026-08-26): covers the whole search page
The Jellyfin web search page does NOT use /Search/Hints; it fans out /Items?searchTerm= (a few,
by type), /Artists?searchTerm= and /Persons?searchTerm= in parallel. The middleware now also
rewrites /Artists and /Persons (path → /Items, since those controllers ignore ids=), returns
an empty result fast (non-existent id) when Meili has no match instead of falling back to the slow
native scan, and leaves live-TV/EPG-only queries to native (Meili doesn't index EPG). Verified in a
real browser against the live library: every section (movies/episodes/people/songs) renders correctly
and server-side latency dropped from ~0.3-1.2s per request to ~8-130ms.
Meilisearch Plugin for Jellyfin
A plugin for Jellyfin that improves search by utilizing Meilisearch as a search engine. Search logic is offloaded to a Meilisearch instance, and the response from Jellyfin is modified
Improved:
- Speed
- Results (fuzzy matching, typos)
Note
As long as your client uses
/Itemsendpoint for search, it should be supported seamlessly I guess
Inspired by JellySearch.
Setup instructions
-
Setup a Meilisearch instance (maybe a hosted one in the cloud will also work, but I don't recommend)
- Docker is recommended. Example
docker-compose.yml:services: meilisearch: container_name: meilisearch image: getmeili/meilisearch:v1.34 # older versions may have compatibility issues restart: unless-stopped environment: MEILI_ENV: production MEILI_NO_ANALYTICS: "true" MEILI_MASTER_KEY: super-secret-key volumes: # meilisearch's data - ./data:/meili_data ports: - 7700:7700
- Docker is recommended. Example
-
Install the Meilisearch plugin
- In Jellyfin:
- Add the plugin Repository:
https://raw.githubusercontent.com/arnesacnussem/jellyfin-plugin-meilisearch/refs/heads/master/manifest.json - Install the Meilisearch plugin
- Restart Jellyfin Server
- Add the plugin Repository:
- In Jellyfin:
-
Configure the Meilisearch plugin
- In Meilisearch plugin's page:
- Meilisearch URL: URL to your Meilisearch instance, as seen by Jellyfin (example:
http://meilisearch:7700) - Meilisearch Api Key: API key to access your Meilisearch instance (if required) (example:
super-secret-key) - Click
Save - The plugin's page should show a healthy status
- Example:
{ "meilisearch": "Server: available", "meilisearchOk": true, "averageSearchTime": "0ms", "indexStatus": { "Database": "Data Source=/config/data/jellyfin.db;Cache=Default;Default Timeout=30;Pooling=True", "Items": "20569", "LastIndexed": "1/28/2026 4:10:01 PM" } }
- Example:
- Meilisearch URL: URL to your Meilisearch instance, as seen by Jellyfin (example:
- In Meilisearch plugin's page:
Note
You can also set the environment variables in Jellyfin, to configure the plugin without editing the Jellyfin UI:
MEILI_URLandMEILI_MASTER_KEY
Note
If you want share one Meilisearch instance across multiple Jellyfin instance, you can fill the
Meilisearch Index Name, if leaving empty, it will use the server name.
-
Test Meilisearch plugin search
- Try Jellyfin search
- Issues? Check Jellyfin's logs and Meilisearch's logs
Index will update on following events:
- Server start
- Configuration change
- Library scan complete
- Update index task being triggered
How it works
The core feature, which is to mutate the search request, is done by injecting an ActionFilter.
So it may only support a few versions of Jellyfin. At the moment I'm using Jellyfin 10.11.0,
but it should work on other versions as long as the required parameter name of /Items endpoint doesn't change.
I've seen JellySearch, which is a wonderful project, but I don't really like setting up a reverse proxy or any of that hassle.
So I am writing this, but it still requires a Meilisearch instance.
At this moment, only the /Items endpoint is affected by this plugin, but it still improves a lot on my 200k items library.