Jump to content

Aperture - AI-Powered Recommendations for Emby


Recommended Posts

Posted
On 4/2/2026 at 10:32 AM, Iceburnmarko said:

Read through all the comments , first what an excellent plug-in…. One thing I was thinking, could it be possible to add “JustWatch api” into it.  Thinking to capture what ppl watched / like / hate outside of emby.

Unfortunately that api is very expensive

Posted

Aperture v0.7.1 Release Notes

This release introduces Movie Collection Gap Analysis — a new admin tool that compares TMDb movie collections against your library to surface missing titles — plus Seerr request options so you can pick your Radarr/Sonarr server, quality profile, and root folder before submitting a request.

Baseline: follows v0.7.0 (commit 6893d05). Commit range: 6893d05..94337ae (8 commits).


At a glance

Theme What shipped
Gap Analysis Admin page comparing TMDb collections to your library; background scan persists all parts with library + Seerr status; collection-level sort & filter; movie detail modal with in-app Seerr requests
TMDb Collection Cache Persistent tmdb_collection_cache table avoids repeated TMDb API calls for the same collection data
Seerr Request Options Choose Radarr/Sonarr server, root folder, quality profile, and language profile before submitting requests — wired into Discovery, Person Detail, Top Picks, My Requests, and Gap Analysis

Summary for users

  • Gap Analysis is available in left nav menu under Admin → Gap Analysis. Run a scan, and Aperture fetches every TMDb movie collection referenced by your library, then shows which titles you own, which are already in Seerr's pipeline, and which are still missing.
  • Sort collections by most missing, most complete, or name. Filter by minimum missing count with a slider to focus on the biggest gaps.
  • Click any poster to open a detail modal with backdrop, metadata, and a Request via Seerr button — no need to leave Aperture.
  • Request options dialog — when requesting through Seerr from anywhere in Aperture, you now pick the destination server, root folder, and quality profile. Bulk requests (select all missing in a collection, or multi-select across collections) go through the same flow.

🔍 Movie Collection Gap Analysis

What it is

For every movie in your library that belongs to a TMDb collection (e.g. The Dark Knight Collection, Alien Collection), Aperture fetches the full collection and checks which released parts you own, which are in Seerr (requested/processing/available), and which are missing. Collections where all missing parts are already covered by Seerr are hidden entirely.

How it works

  • Background scan (refresh-library-gaps job): fetches TMDb collection data (cached in tmdb_collection_cache), cross-references your library and Seerr, and persists every released part with in_library and seerr_status into gap_analysis_results.
  • Display path is pure SQL — zero Seerr or TMDb HTTP calls at page-load time. Fast even with hundreds of collections.
  • UI groups results into collapsible collection cards with a responsive MoviePoster grid. Status overlays show "In Library", "Requested", "Processing", etc.

Key features

  • Collection-level sort: Most missing, Most complete, Name A–Z.
  • Min-missing slider: hide collections with fewer than N missing titles.
  • Search: filter by title or collection name.
  • Bulk select + request: select individual posters or all missing in a collection, then request via Seerr with your chosen options.
  • Detail modal: click any poster to see the TMDb detail view with backdrop, cast, overview, and request button.

Technical summary

  • Core module: packages/core/src/gap-analysis/index.tsrunLibraryGapAnalysis, getGapCollectionSummaries, listGapResults, getGapCollectionParts (all pure SQL reads).
  • TMDb cache: packages/core/src/tmdb/collection-cache.tsupsertCollectionCache, getCachedCollectionDataBatch, fetchCollectionDataAndCache.
  • API routes: apps/api/src/routes/gap-analysis/index.tsGET /latest, GET /results, GET /collection-parts, POST /refresh, POST /request.
  • Job: refresh-library-gaps registered in jobConfig and wired in the executor.

🎛️ Seerr Request Options

When submitting a request through Seerr, a new RequestSeerrOptionsDialog lets you choose:

  • Server (Radarr or Sonarr instance)
  • Root folder
  • Quality profile
  • Language profile (Sonarr)

The dialog is wired into every request flow: Discovery cards/list items, Person Detail gap rows, Top Picks preview, My Requests, and Gap Analysis. The Seerr provider now exposes listRadarrServers, getRadarrServerDetails, listSonarrServers, getSonarrServerDetails, and batchGetMediaStatus.


🗄️ Database migrations

Run your usual migration command so these apply in order:

Migration Purpose
0106_gap_analysis.sql gap_analysis_runs and gap_analysis_results tables; source column on discovery_requests
0107_tmdb_collection_cache.sql tmdb_collection_cache table for persistent TMDb collection data
0108_gap_results_status.sql in_library and seerr_status columns on gap_analysis_results; wipes prior runs due to schema change

🚀 Update instructions

For Docker users

docker compose pull
docker compose up -d

Database migrations run on startup (or use your usual pnpm db:migrate / deployment process).

Operator checklist

  • Migrate the database — the three new migrations must apply before starting the updated API.
  • TMDb API key must be configured (Admin → Settings) for gap analysis to work.
  • Seerr must be configured and enabled for the request flow. Without Seerr, gap analysis still shows owned vs missing but the request buttons are disabled.
  • Run an analysis — go to Admin → Gap Analysis and click "Run analysis" to populate the initial snapshot. Re-run after syncing new movies.

📋 API quick reference

  • Gap Analysis: GET /api/admin/gap-analysis/latest, GET /api/admin/gap-analysis/results, GET /api/admin/gap-analysis/collection-parts, POST /api/admin/gap-analysis/refresh, POST /api/admin/gap-analysis/request.
  • Seerr services: GET /api/seerr/radarr, GET /api/seerr/radarr/:serverId, GET /api/seerr/sonarr, GET /api/seerr/sonarr/:serverId.
  • Batch status: POST /api/seerr/batch-status.

📋 Full changelog (commits since v0.7.0)

eef6397 feat(seerr): add Radarr/Sonarr request options and batch media status
abea84a feat(tmdb): add persistent collection cache and helper types
952acdb feat(core): add gap analysis engine with pre-computed Seerr status
2f55407 feat(api): add gap analysis REST routes and background job executor
03b275d feat(web): add Gap Analysis admin page with detail modal
12d4676 docs: add gap analysis section to admin guide and API reference
94337ae feat(web): replace row-level sort with collection sort and min-missing filter
Posted

Good lord making this thing multi-language is a huge pain in the ass!

  • Haha 2
Posted
4 hours ago, TheGru said:

Good lord making this thing multi-language is a huge pain in the ass!

I am almost done, wrote some google translate scripts and i18n have made this a bit less painful... without Cursor and composer 2 fast for help I would not have ever done this.

Posted
10 minutes ago, TheGru said:

I am almost done, wrote some google translate scripts and i18n have made this a bit less painful... without Cursor and composer 2 fast for help I would not have ever done this.

3090 hardcoded english strings

Posted (edited)

Aperture v0.7.5 Release Notes

This release expands Discovery with JustWatch streaming charts, configurable TMDb genre strips (including exclude genres and flexible year ranges), a clearer Movies / TV browse layout, and multilingual UI improvements.


At a glance

Theme What shipped
Discovery browse layout On Movies and TV Series, sub-tabs separate TMDb popular overall (AI pool, filters, refresh) from Popular by genre (admin-configured TMDb Discover strips). Choice is remembered in the browser.
Streaming discovery Discover → Streaming tab (when enabled): JustWatch-backed charts; admin can enable the feature and choose provider strips.
Genre strips (admin) Per-strip genres (AND), exclude genres, from / to year (including Today for a rolling end at the current calendar year), heading, origin country, max titles, drag reorder.
Multilingual UI Fourteen locales; RTL for Arabic and Hebrew. Deployment-wide language defaults under System settings.

Summary for users

Where to find things

What Where
AI discovery pool + filters + Refresh DiscoverMovies or TV Series → sub-tab TMDb popular overall
Horizontal genre strips DiscoverMovies or TV Series → sub-tab Popular by genre
Streaming charts DiscoverStreaming (only if your admin has enabled streaming discovery for the server)
Your UI language User settings (language / locale preferences in the app)

Grid/list view and Refresh apply to TMDb popular overall, not to the genre-strip sub-tab. Redundant info alerts about clicking titles were removed from Discovery tab content.


Summary for admins

Genre strips (TMDb Discover)

Path: Admin → Settings → Setup → Genre discovery (third sub-tab under Setup). TMDb must be configured; the tab is disabled until then.

Deep link (same page): /admin/settings?tab=setup&setupSub=genre-discovery

Configure Movies and TV sections separately. For each strip you can set:

  • Genres — multiple genres are combined with AND (TMDb with_genres).
  • Exclude genres — TMDb without_genres (e.g. Science Fiction but not Animation).
  • From year / To year — optional bounds on release date (movies) or first air date (TV). To year can be No end, Today (current year) (rolling end-of-year bound at fetch time), or a specific year.
  • Optional heading, origin country, and max titles per strip.
  • Drag strips to reorder; use the save control on a strip (or your workflow) to persist.

Streaming discovery (JustWatch)

Path: Admin → Settings → Setup → IntegrationsStreaming discovery block (enable flag, provider strip codes).

Deep link: /admin/settings?tab=setup&setupSub=integrations

The Discover → Streaming tab appears for users when streaming discovery is enabled for the deployment and their account has Discovery enabled.

Language defaults (deployment)

Path: Admin → Settings → SystemLanguage defaults (default UI language and default AI language for new users / server-side defaults where applicable).


Discovery – TMDb popular overall vs Popular by genre

  • TMDb popular overall — The personalized discovery pool: language/genre/year filters, Refresh, grid or list view, job progress when a discovery job runs.
  • Popular by genre — Read-only horizontal strips driven by admin Genre discovery configuration. Strips load as you scroll (lazy loading).

Discovery – Streaming (JustWatch)

Charts are backed by JustWatch GraphQL data with server-side caching. TMDb poster paths may be resolved and cached to reduce API load. Exact provider codes depend on region; the admin UI references the bundled provider list.


Multilingual UI and RTL

Aperture’s web UI is multilingual. Users can work in a supported language (including via browser language detection and stored preference). We do not guarantee completeness or accuracy of translations; no promises are made as to language quality.

Supported UI locales (14): English (en), Spanish (es), German (de), French (fr), Italian (it), Portuguese (pt), Dutch (nl), Russian (ru), Japanese (ja), Chinese (zh), Korean (ko), Hindi (hi), Arabic (ar), Hebrew (he).

Right-to-left (RTL): The layout uses RTL for Arabic and Hebrew so navigation and mirroring behave correctly for those locales.


Update instructions

For Docker users

docker compose pull
docker compose up -d

Apply database migrations per your process (pnpm db:migrate or automatic on startup, depending on deployment).

Operator checklist

  • Run migrations01100112 must apply before relying on streaming discovery or poster cache. Should happen automatically.
  • TMDb — Required for genre strips and for sensible Discover behavior.
  • Streaming discovery — Enable and configure provider strips under Setup → Integrations if you want the Streaming tab.
  • Genre strips — Configure under Setup → Genre discovery; use Discover → Popular by genre to verify.

Thank you for running Aperture and feeding back on Discovery and translations in real environments.

Edited by TheGru
dvalenci03
Posted

Will this work on ARM Mac?

filzituero
Posted

During the initial setup wizard, the "Sync Movie Watch History" job fails with a 400 Bad Request error.

Error:
params/name must be equal to one of the allowed values
allowedValues: ["sync-movies","sync-series","enrich-movies","enrich-series","embed-movies","embed-series"]

Steps to reproduce:

  • Fresh install, follow setup wizard
  • Reach the "Initial Jobs" step
  • sync-movies and sync-series complete successfully
  • sync-movie-watch-history fails with the above error

Environment:

  • Image: ghcr.io/dgruhin-hrizn/aperture:latest
  • Media server: Emby (native install)
  • Docker on Linux
filzituero
Posted

I managed to fix it:

Modify /setup/schemas.ts and replace line 362 with:

enum: ['sync-movies', 'sync-series', 'sync-movie-watch-history', 'sync-series-watch-history', 'enrich-movies', 'enrich-series', 'embed-movies', 'embed-series', 'generate-movie-embeddings', 'generate-series-embeddings', 'generate-movie-recommendations', 'generate-series-recommendations', 'sync-movie-libraries', 'sync-series-libraries', 'refresh-top-picks']
It’s now up and running.

I did run into one bug though:
if I enable recommendations for a user and then disable that user in Emby, they still keep getting recommendation updates.

Another thing (more of a feature request than an issue):
would it be possible to automatically delete the libraries created for a user when they are disabled in Emby, or when recommendations are turned off for them?

I also noticed that it creates two “Continue Watching” sections in Emby. I remember reading somewhere that it might be related to how Emby handles duplicate names or something like that, but I’m not sure if there’s a proper fix or workaround.

Anyway, thanks for this awesome tool!

TheGru
Posted
39 minutes ago, filzituero said:

I managed to fix it:

Modify /setup/schemas.ts and replace line 362 with:

enum: ['sync-movies', 'sync-series', 'sync-movie-watch-history', 'sync-series-watch-history', 'enrich-movies', 'enrich-series', 'embed-movies', 'embed-series', 'generate-movie-embeddings', 'generate-series-embeddings', 'generate-movie-recommendations', 'generate-series-recommendations', 'sync-movie-libraries', 'sync-series-libraries', 'refresh-top-picks']
It’s now up and running.

I did run into one bug though:
if I enable recommendations for a user and then disable that user in Emby, they still keep getting recommendation updates.

Another thing (more of a feature request than an issue):
would it be possible to automatically delete the libraries created for a user when they are disabled in Emby, or when recommendations are turned off for them?

I also noticed that it creates two “Continue Watching” sections in Emby. I remember reading somewhere that it might be related to how Emby handles duplicate names or something like that, but I’m not sure if there’s a proper fix or workaround.

Anyway, thanks for this awesome tool!

thanks for the feedback, I will get it fixed and redeployed

TheGru
Posted
48 minutes ago, filzituero said:

I also noticed that it creates two “Continue Watching” sections in Emby. I remember reading somewhere that it might be related to how Emby handles duplicate names or something like that, but I’m not sure if there’s a proper fix or workaround.

this is an emby thing, you can solve for it with the latest beta, and their new Dynamic Content home row, selecting just the libraries you want to include (ie do not include the aperture libraries and you wont have dupes in continue watching)

TheGru
Posted

Aperture v0.7.6 Release Notes

This release improves deployment (multi-arch Docker images for amd64 and arm64), user sync with Emby (respect disabled accounts), setup wizard job triggers, and STRM library lifecycle (automatic removal of virtual libraries and output folders when recommendations are turned off).


Summary for Users

  • Apple Silicon Macs and 64-bit ARM servers (e.g. Raspberry Pi with a 64-bit OS) can run the same pre-built container image as x86_64; Docker pulls the matching architecture automatically.
  • If your server admin disables your Emby account, Aperture stops treating you as eligible for recommendation jobs once sync-users runs (default every 30 minutes).
  • Initial setup jobs such as Sync Movie Watch History no longer fail with a validation error when started from the wizard.

Summary for admins

Disabling recommendations and STRM output

When you turn off movies, series, or all recommendations for a user (Admin → Users), or when sync-users detects a user disabled on the media server, Aperture attempts to:

  • DELETE the corresponding virtual library in Emby (by library name).
  • Remove that user’s folders under /aperture-libraries/aperture and /aperture-libraries/aperture-tv (inside the Aperture container).
  • Delete matching rows in strm_libraries.

Failures talking to the media server (e.g. library already removed) are logged; database and filesystem cleanup still proceed where possible. The scheduled sync-movie-libraries / sync-series-libraries jobs also run a reconciliation pass to clean up stale library records.

Docker image architectures

Published images are multi-architecture manifests. No separate tag is required per CPU; use your existing ghcr.io/.../aperture image reference.

Note: 32-bit ARM (arm/v7) is not a build target; use a 64-bit OS on Raspberry Pi for parity with dependencies (e.g. native modules).


API notes (operators)

  • Setup jobs: POST /api/setup/jobs/:name/run — the documented allowed name values now match the server (includes watch-history, embedding, library, and related job ids).

Update instructions

For Docker users

docker compose pull
docker compose up -d

Apply database migrations per your process (pnpm db:migrate or automatic on startup, depending on deployment).

Operator checklist

Expect one-time sync — existing users get provider_disabled = false until sync-users runs and reads the current media-server flags.


Thank you for running Aperture and reporting setup and deployment edge cases in the wild.

Posted

Can I help you with the german translation of the UI? I just realized today that there were some updates to Aperture and I really like them so far. After changing the UI and AI language to German I also realized how bad Google or whatever you used for translation did the translation to German. I have no experience in coding etc. but pretty decent knowledge in IT in general and am fluent in German. Would be happy to help. 
 

also an additional question: is there a fix for the missing poster bug planned?

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...