NextUp is a program that allows Jellyfin admins to generate user personalized movie and series recommendations directly in Jellyfin regardless of client using Jellyfin libraries. User are also able to generate media requests in Seer right from the Jellyfin UI by "favoriting" the media in their recommendations.
NextUp is also compatible with WatchState to allow for more long live watch history to improve recommendations and avoid getting recommendations for media the user has already watched but might have been removed from the server.
- Features
- How it Works
- Installation / Usage
- Configuration
- API
- Support
- License
- Acknowledgments
- AI Disclaimer
- Support for any number of users
- Can create popular movies, popular series, and upcoming movie recommendations
- CRON expression supported for flexible scheduling
- Optional WatchState integration for long lived watch history
- Optional Seerr integration to generate requests from Jellyfin UI
NextUp looks at a user's movie and series watch history and uses that to query TMDB for recommendations based on that watched piece of media. It then creates a minimal metadata.json file and saves it in a directory specific for that user in a place that Jellyfin can import along with a dummy video file. Jellyfin admins then create a library for that user's recommendations and allow then allow that user to see that library. Optionally, NextUp can create popular movies, upcoming movies, and popular series recommendations.
If the Seerr integration is enabled in NextUp users can favorite media from their recommendations to automatically request it for download in Seerr. Note that due to the way Seerr's api is implemented all requests are auto-approved. Also note that requested series only request the first season to avoid downloading a lot of data for shows with many seasons especially if the user decides they're not interested and don't want to watch it anymore.
NextUp never adds any recommendations if the user already has access to the media on the server. Additionally, NextUp does not guarantee or try in anyway to generate recommendations that adhere to any of the user's content restrictions as defined in their Jellyfin user profile (ex. age-rating).
The Placeholder video is created once in a NextUp directory of your recommendations path and is then hard-linked to recommendations directories to avoid duplicating data.
Jellyfin's API only returns watch history for media currently on the server therefore users with a small changing library would not get the benefit of a long standing watch history to pull recommendations from.
WatchState is a self-hosted tool that was originally designed to
sync your backends users play state without relying on third party services
However it also stores watch history for all media a user has watched regardless of the status on the media on the server. Using WatchState's API NextUp can fetch a complete watch history. WatchState stores actual watch history AND user-marked watched media. As a result users can "Mark as Watched" media in their recommendations to avoid recommending it in the future as well as use it as a seed for further recommendations.
The easiest way to use NextUp is to use the Docker image but you can also build from source or run using Python directly.
To allow NextUp to create recommendation files you must provide a directory as a volume mapped to /recommendations. Ensure the user used for the container has read and write permissions to the recommendations directory.
Additionally, you must provide a config file mapped to /config.env. Check the config.env.sample as a starting point.
The NextUp image defaults to port 5777 but accepts APP_PORT and APP_HOST to customize these values.
services:
server:
container_name: NextUp
image: ghcr.io/20bbrown14/nextup:latest
ports:
- 5777:5777
env_file: config.env
user: "1000:1000"
environment:
- APP_HOST=0.0.0.0
- APP_PORT=5777
volumes:
- /path/to/recommendations:/recommendations
- /path/to/config/config.env:/config.envdocker run -d \
--name NextUp \
--user "1000:1000" \
-p 5777:5777 \
--env-file config.env \
-e APP_HOST=0.0.0.0 \
-e APP_PORT=5777 \
-v /path/to/recommendations:/recommendations \
-v /path/to/config/config.env:/config.env \
ghcr.io/20bbrown14/nextup:latestFirst clone the repository
git clone https://github.com/20BBrown14/NextUp
cd into the directory: cd NextUp
Add a new compose.yaml file which will be used to build and run the image
services:
server:
build:
context: .
ports:
- 5777:5777
env_file: config.env
user: "1000:1000"
environment:
- APP_HOST=0.0.0.0
- APP_PORT=5777
volumes:
- /path/to/recommendations:/recommendations
- /path/to/config/config.env:/config.envNow run docker compose up --build -d
After NextUp has ran for the first time you should see some new directories in your /recommendations directory depending on your settings. The directories with usernames are that user's personalized recommendations and popular-series, popular-movies, and upcoming-movies are just as they seem.
You should add new libraries in Jellyfin that utilize these directories. Personally I name them Discover - Shows (username) (or movies) and then give only that user access to see that library.
Users can then browse the libraries and "mark as watched" media they already watched or "favorite" media to automatically request it in Seerr if that integration is enabled.
To allow NextUp to be notified when a user favorites a recommendation and create a recommendation in Seerr you must setup a webhook to send notifications to NextUp.
- In Jellyfin go to Dashboard -> Plugins
- Install the Webhook plugin and restart your instance
- After restart go to Dashboard -> Plugins -> Webhook -> Settings
- Click "Add Generic Destination"
- Provide a name that you will know what it means
- Webhook URL (Use NextUp's host/port)
- Use
http://{HOST}:{PORT}/webhook/jellyfin
- Use
- Status
- Ensure
Enabledis checked
- Ensure
- Notification Type
- Ensure
User Data Savedis checked
- Ensure
- User Filter
- Check all users that should be able to automatically generate requests in Seerr by favoriting items. Users can have recommendations generated without having the ability to request them in Seerr
- Item Type
- Ensure
MoviesandSeriesis checked
- Ensure
- Ensure
Send All Properties (ignores template)is checked - DO NOT FORGET TO SAVE
To enable WatchState in your install you must provide the WatchState configuration. Follow the WatchState installation instructions to setup your Jellyfin backend and users/sub-users. Don't forget to setup the Webhook for your backend.
NextUp uses a configuration file, config.env to customize NextUp Behavior.
| Name | Default | Description | Required |
|---|---|---|---|
| JELLYFIN_API_KEY | API key used to authenticate with Jellfyin | ✔️ | |
| JELLYFIN_URL | Base Jellyfin URL | ✔️ |
| Name | Default | Description | Required |
|---|---|---|---|
| SEERR_API_KEY | API key used to authenticate with Seerr | ❌ | |
| SEERR_URL | Base Seer URL | ❌ |
| Name | Default | Description | Required |
|---|---|---|---|
| TMDB_API_KEY | API key used to authenticate with TMDB. Check out this TMDB page to get a TMDB API key | ✔️ | |
| TMDB_URL | https://api.themoviedb.org/3 | Base TMDB URL. Only v3 has been tested. | ✔️ |
| Name | Default | Description | Required |
|---|---|---|---|
| NEXTUP_RECOMMENDATIONS_DIR | /recommendations |
Base directory for NextUp to create recommendations in | ❌ |
| PLACEHOLDER_FILE_PATH | /movie.mp4 |
The path to the video file to use as a placeholder for the recommendations. If using Docker the file must be passed to the container. If not passed the default video file will be used. | ❌ |
| Name | Default | Description | Required |
|---|---|---|---|
| WATCHSTATE_USERNAME | WatchState username to authenticate with | ❌ | |
| WATCHSTATE_PASSWORD | WatchState password to authenticate with | ❌ | |
| WATCHSTATE_URL | Base WatchState URL | ❌ | |
| WATCHSTATE_MAIN_USER_TO_JELLYFIN_MAP | See note below | ❌ |
The "default" or "main" user of WatchState is always named "main". Fetching user watch history for the "main" Jellyfin user will fail if the Jellyfin username is not also main.
For example, if the Jellyfin admin account username is "Fry" that is linked to the "main" WatchState user I would set WATCHSTATE_MAIN_USER_TO_JELLYFIN_MAP to "Fry"
| Name | Default | Description | Required |
|---|---|---|---|
| GENERATE_RECOS_FOR | (All users) | Comma separated list of users to generate recos for. Not providing this will generate recos for all Jellyfin users | ❌ |
| RECOMMENDATIONS_CRON_SCHEDULE | Cron expression used to schedule when recommendations are generated. If not supplied then no scheduler is started. Without a scheduler you can still manually invoke recommendation generation by using the API | ❌ |
| Name | Default | Description | Required |
|---|---|---|---|
| DISABLE_SERIES_RECOMMENDATIONS | false | Whether to disable generating recommendations from/for series | ❌ |
| SERIES_LIBRARY_IDS | Comma separated list of library IDs to search series watch history for. Using this option effectively ensures NextUp Recommendations are ignored by NextUp on further runs. | ❌ | |
| MAX_SERIES_DAYS_LOOKBACK | The number of days lookback at watch history to generate recommendations from. If unset uses entire history. | ❌ | |
| MAX_RECOMMENDATIONS_PER_SERIES | 10 | The max number of recommendations to add per watched series | ❌ |
| MIN_EPISODE_WATCH_COUNT | 2 | The min number of episodes of a series that need to be watched to consider it for recommendations | ❌ |
| MAX_TOTAL_SERIES_RECOMMENDATIONS | The max number of TOTAL series recommendations to generate per user.* | ❌ | |
| INCLUDE_POPULAR_SERIES_RECOMMENDATIONS | true | Whether to generate recommendations in Jellyfin for TMDB's popular series.** | ❌ |
| POPULAR_SERIES_COUNT | 100 | The number of popular series recommendations to generate. | ❌ |
*If MAX_TOTAL_SERIES_RECOMMENDATIONS is set recommendations will be weighted 90% towards a user's most watched genres and 10% towards the least watch. If not set then there will be a max of MAX_RECOMMENDATIONS_PER_SERIES recommendations per each series the user has watched and no weighting is used.
**Popular recommendations are generated directly from TMDB's api endpoint. They are added to a popular-series directory which can be added to Jellyfin as a library and displayed for users.
| Name | Default | Description | Required |
|---|---|---|---|
| DISABLE_MOVIE_RECOMMENDATIONS | false | Whether to disable generating recommendations from/for movies. | ❌ |
| MOVIE_LIBRARY_IDS | Comma separated list of library IDs to search movie watch history for. Using this option effectively ensures NextUp Recommendations are ignored by NextUp on further runs. | ❌ | |
| MAX_MOVIE_DAYS_LOOKBACK | The number of days to look back at watch history to generate recommendations from. If unset uses entire history. | ❌ | |
| MAX_RECOMMENDATIONS_PER_MOVIE | 5 | The max number of recommendations to add per watched movie | ❌ |
| MIN_MOVIE_WATCH_PERCENT | 90 | The percentage that a movie must be watched to be considered for recommendations. This setting helps for movies that are still "in progress" at the credits | ❌ |
| MAX_TOTAL_MOVIE_RECOMMENDATIONS | The max number of TOTAL movie recommendations to generate per user.* | ❌ | |
| INCLUDE_POPULAR_MOVIE_RECOMMENDATIONS | true | Whether to generate recommendations in Jellyfin for TMDB's popular movies.** | ❌ |
| INCLUDE_UPCOMING_MOVIE_RECOMMENDATIONS | true | Whether to generate recommendations in Jellyfin for TMDB's upcoming movies.** | ❌ |
| POPULAR_MOVIES_COUNT | 50 | The number of popular movie recommendations to generate. | ❌ |
| UPCOMING_MOVIES_COUNT | 50 | The number of upcoming movie recommendations to generate. | ❌ |
*If MAX_TOTAL_MOVIE_RECOMMENDATIONS is set recommendations will be weighted 90% towards a user's most watched genres and 10% towards the least watch. If not set then there will be a max of MAX_RECOMMENDATIONS_PER_MOVIE recommendations per each movie the user has watched and no weighting is used.
**Popular and upcoming recommendations are generated directly from TMDB's popular and upcoming api endpoints. They are added to popular-movie and upcoming-movie directories which can be added to Jellyfin as a libraries and displayed for users.
The API is not secured with any authorization. It's highly recommended to NOT expose this server to the internet or any networks/devices you do not implicitly trust.
Responds with a simple 200 and "{'message': 'Healthy!'}" message if the server is running.
Runs the recommendation engine. This is the same as the scheduler does if enabled.
If you experience issues, found a bug, or have other comments and concerns you can open an issue or start a discussion.
Special thanks to the developers and maintainers of Jellyfin, Seerr, WatchState, and TMDB.
Thanks to the developers and maintainers of Jellybridge Jellyfin Plugin for the inspiration for this project as well as the source of the placeholder video file
I am a software developer with nearly a decade of experience as of this writing. I used Gemini to help with Python syntax (mostly typing), writing utility/helper function, and API documentation parsing. All code generated by Gemini was verified to work as expected and nothing was copy-pasted directly.