Streamystats: Jellyfin analytics with an AI chat over your own library
Streamystats is a statistics service for Jellyfin, providing analytics and data visualization.
At a glance
- What is it?
- Streamystats is a TypeScript analytics service that sits next to a Jellyfin server, reads playback data straight from the Jellyfin API, stores it in Postgres with vectorchord, and adds an AI chat that can recommend titles from embeddings. Its own documentation asks you to expect bugs and slow development.
- Who is it for?
- Streamystats suits a self-hosted Jellyfin operator who wants watch statistics, per user history and a recommendation chat without writing anything, and who is comfortable running two containers and a Postgres based vectorchord database beside their media server.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Analytics that sit beside Jellyfin rather than inside it
Streamystats is a statistics service for Jellyfin, providing analytics and data visualization, and it is built as a separate application rather than as a Jellyfin plugin.
That separation shapes everything about it. There is a dashboard with overview statistics, live sessions and recommendations, user specific watch history and statistics, library statistics, watch time graphs with advanced filtering, and client statistics, which tells you which devices people are actually watching on. It supports multiple servers and multiple users, so a shared household server with several accounts can be viewed per user rather than as one aggregate.
The newer and more interesting part is the AI layer. There is an interactive chat interface over your own library that can return watch recommendations, and those recommendations can be embedded, meaning the suggestions can be shown inside your Jellyfin interface rather than only in the Streamystats dashboard. The project notes that it is supported by Janitorr in beta.
The stack is a Next.js, React and TypeScript frontend, a Hono backend running on Bun v1.3, vectorchord as the database for embeddings, and Docker for packaging. The package manifest is version 2.20.0, requires Bun 1.4.0 or newer, and is a workspace with packages and apps directories, with keywords that name postgres and drizzle alongside jellyfin and analytics.
For a Jellyfin operator, the pitch is straightforward: keep Jellyfin as the server, and put the analysis somewhere else.
It reads the Jellyfin API directly, so no plugin is needed
The compatibility story is stated unusually precisely, and it is the first thing to check before you install anything.
Streamystats uses only Jellyfin's current REST API with Authorization: MediaBrowser headers, and the documentation says this keeps it working on servers that have legacy authorization disabled. It is tested against Jellyfin 12.0 and 10.11, and it states that 10.10.7, the oldest release Jellyfin supports upgrading from, should work as well.
The design changed here at some point. The documentation states that the Playback Reporting Plugin is no longer needed and that Streamystats relies solely on the Jellyfin API for statistics. That plugin used to be the standard way to get playback data out of Jellyfin into an external tool, and removing the dependency is what makes the current API only approach possible.
If you are migrating, the same section gives you a way in: Streamystats can import data from Jellystat and from the Playback Reporting Plugin, so you can bring existing history across instead of starting from an empty dashboard on the day you deploy. The roadmap also lists only sync certain libraries as completed, which means the sync scope can be narrowed rather than pulling everything on the first run.
One operational note from the install section: the first load can take a while, depending on the size of your library.
Two containers and a vectorchord database
The compose file defines two services that talk to each other over a private network, and understanding the split explains most of the behaviour.
The first is the Next.js application, which serves the dashboard on port 3000 and is the only service exposed to your browser. Its environment includes the database URL, a JOB_SERVER_URL pointing at the job server, the SESSION_SECRET, and an INTERNAL_API_KEY that defaults to the session secret if you do not set it. The compose file also carries a NEXT_SERVER_ACTIONS_ENCRYPTION_KEY, with a comment explaining that a stable key prevents session breakage on redeploy.
The second is the job server, which does the background work. It runs on port 3005 and points back at the Next.js service through NEXTJS_URL. Its settings are the interesting ones: a session polling interval in milliseconds that defaults to 5000, and a set of cron schedules in standard cron syntax.
job-server:
image: ghcr.io/fredrikburmester/streamystats-job-server:${VERSION:-latest}
environment:
- NODE_ENV=production
- PORT=3005
- HOST=0.0.0.0The cron variables shown in the compose file cover activity sync, recent items sync, user sync and people sync, and several of them default to running every minute. That is what keeps the dashboard current without you doing anything, and it also means the job server is doing a lot of work in the background whether or not anyone is looking at the charts.
The database is vectorchord, a Postgres based system reached over the standard Postgres URL, and both services wait for it to report healthy before they start.
Migrations run on startup, and there is no downgrade path
The rollback section of the documentation is the most important operational text in the project, and it is unusually blunt.
Database migrations are automated and handled by the job-server container, which runs migrations on startup before becoming healthy. That is convenient, and it has a consequence: Streamystats does not perform automatic rollbacks or downgrades of the database schema for Docker users.
The consequence is spelled out. If you downgrade the Docker image version, for example changing the tag back to an earlier release, the database remains migrated to the newer version, and the older application version will likely fail to start or behave incorrectly because it does not understand the newer schema. The recommended strategy, described as the official and only supported method to roll back a deployment, is to restore a database backup. And the instruction that follows is unambiguous: always take a database backup before updating to a newer image version.
So the upgrade procedure is: back up first, pull the new tag, let the job server migrate, and if it goes wrong, restore the backup rather than downgrading the tag.
There is a second version choice to make. Version tags such as v1.2.3 are generated automatically on release and are described as stable, tested reference points for production use, with the recommendation to pin to a specific version tag. The :latest tag always points to the latest commit on the main branch, and while it is described as typically stable, it may occasionally contain breaking changes. For a deployment with a database that cannot be downgraded, the tagged image is the sensible choice.
Docker is described as the easiest and recommended way to run the service, but a Dockerless route exists and is documented in DOCKERLESS.md for people who cannot run containers.
Embeddings and vectorchord behind the chat
The recommendation machinery is built on embeddings, and the documentation explains how it is stored.
Library items are embedded using OpenAI-compatible APIs if enabled, supporting multiple models and custom configurations. The embeddings go into vectorchord with support for any dimension, which means you are not locked to the dimension of one particular model, and the system automatically creates vector indexes optimized for similarity search.
Recommendations use vector similarity with cosine distance to find content similar to your watch history, and the system analyses your viewing patterns to suggest movies and series. Each recommendation carries an explanation showing which watched items led to the suggestion, which is the difference between a recommendation and a black box.
The chat itself supports multiple providers out of the box with any OpenAI-compatible API, and includes function calling with thirteen specialized tools. The tools listed cover personalized recommendations from watch history, semantic library search using the embeddings, watch statistics and most watched content, shared recommendations across multiple users, genre filtering and top rated items, and recently added content discovery.
Two design points are worth pulling out. Semantic library search means the chat can answer questions about what you own by meaning rather than by title match, which is the part that would be hard to build without the vector store. And shared recommendations for multiple users implies the embedding and similarity work is done over a combined view when more than one account asks, which is how a household server gets a shared answer rather than six private ones.
Installing with Docker Compose and a real session secret
The documented install is a short sequence, and two of its steps are the ones people skip.
Install Docker and Docker Compose if you have not already. Copy the docker-compose.yml file to your desired location, using the :latest tag unless you have read the version tags section. Change any ports if needed, where the default web port is 3000. Then change the SESSION_SECRET in the compose file to a random string, and the documentation suggests generating one:
openssl rand -hex 64That step is not optional in practice, because the session secret signs sessions and the compose file also falls back to using it as the internal API key when INTERNAL_API_KEY is not set. The example environment file shows the same requirement with a second key, the Next.js server actions encryption key, which the comments explain rotates on each build if it is not fixed and breaks active sessions on redeploy. The example suggests openssl rand -hex 16 for that one.
After that, start the application and open it:
docker-compose up -dhttp://localhost:3000The final step is a setup wizard where you connect your Jellyfin server. That is where the Authorization: MediaBrowser authentication the project relies on gets configured, and it is also where you would point it at more than one server if you use that feature.
For working on the code rather than running the images, the package scripts expose the same split, with a combined dev command that runs both processes:
bun run dev:alland separate database tooling such as db:migrate and db:studio inside the packages/database workspace.
A hobby project that tells you it is AI-assisted
The project is unusually candid about itself, and the paragraph near the top of the documentation sets the terms.
It describes itself as a personal hobby project, asks you not to expect rapid development, states that the project is prone to bugs even though the author is a full-time experienced developer, and then says the important part: AI-assisted development is being used for tasks ranging from pull request reviews to refactoring and coding new features.
That disclosure matters more than it might seem for a service that sits on top of your media server and, with the AI features enabled, talks to an external model provider about what your household watches. It is not an accusation, it is a statement about how much human review sits between the code and your database, and you are entitled to weigh it.
The release record gives a sense of activity. Recent versions include v2.20.0 on 2026-09-12, with v2.19.2 and v2.19.1 published on the same day, and the last push was on 2026-09-14. Three releases in one day is a fast turnaround for a hobby project, which is consistent with a release-please driven workflow and the changelog file in the repository.
The licence is listed as unknown, and the repository has no licence file among its top level entries, which include the compose files, the Dockerfiles, the applications and packages workspaces, the documentation folder, SECURITY.md and CLAUDE.md. For a service that reads your viewing history, the terms are worth asking about before you put it on a public server, and that is a question for the author rather than a conclusion anyone else can draw.
Editorial conclusion
Streamystats suits a self-hosted Jellyfin operator who wants watch statistics, per user history and a recommendation chat without writing anything, and who is comfortable running two containers and a Postgres based vectorchord database beside their media server. It is the wrong choice for a Jellyfin instance where you cannot run an external service against the API, and the wrong choice for anyone who needs a supported product, since the project describes itself as a hobby project that is AI-assisted and prone to bugs. Verify three things before you commit: that your Jellyfin version is on the tested list, that you have a database backup process in place before the first upgrade, because there is no automatic downgrade path, and that you understand what leaving your watch history in an external AI provider's hands means if you enable embeddings and chat.
Frequently asked questions
How do I install Streamystats with Docker?
Copy the docker-compose.yml to your machine, set a random SESSION_SECRET with openssl rand -hex 64, adjust the web port if needed, then run docker-compose up -d and open http://localhost:3000 to follow the setup wizard that connects your Jellyfin server. The first load can take a while on a large library.
Does Streamystats still need the Jellyfin Playback Reporting Plugin?
No. The documentation states that the Playback Reporting Plugin is no longer needed and that Streamystats relies solely on the Jellyfin API for statistics. Existing data can still be imported from Jellystat and from the Playback Reporting Plugin.
Which Jellyfin versions does Streamystats support?
It is tested against Jellyfin 12.0 and 10.11, and 10.10.7, the oldest release Jellyfin supports upgrading from, should work as well. It uses only the current REST API with Authorization: MediaBrowser headers, which keeps it working on servers with legacy authorization disabled.
How do I roll back a Streamystats update if it goes wrong?
You cannot downgrade the image alone, because migrations run on startup and the database stays on the newer schema. The documented and only supported rollback method is to restore a database backup, so take one before every update and pin to a specific version tag rather than the latest commit.
Where are the AI embeddings stored and which models can I use?
Embeddings are stored in vectorchord, which supports any dimension, and the system creates vector indexes optimized for similarity search. Items are embedded through OpenAI-compatible APIs when that feature is enabled, so multiple models and custom configurations are supported.
Can I run Streamystats without Docker?
Yes, although Docker is described as the easiest and recommended route. A Dockerless setup is documented in the DOCKERLESS.md file in the repository, and the package scripts expose dev, build and start commands for running the job server and the Next.js app directly with Bun.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fredrikburmester-streamystats)Was this useful?