Flixpatrol-Top-10-on-Trakt

Flixpatrol-Top-10-on-Trakt

Docker app from grtgbln's Repository

Overview

This tool get TODAY top 10 from flixpatrol and push the result on trakt list (useful for syncing with Kometa)

Flixpatrol Top 10 on Trakt

Scrape today's top 10 from FlixPatrol and sync them to Trakt.tv, Floppy or mdblist lists.
Supports 74 streaming platforms, 199 countries/regions, and is compatible with Kometa.

CI Docker Image FlixPatrol markup

Docker Windows Linux macOS Discord Stars License

Downloads Release Commits in Develop Issues Pull Requests

Getting Started • Choosing Your Platform • Migrating from 2.x • Configuration • Supported Platforms • Scheduling • Daemon Mode • Troubleshooting • Development


Warning Running at your own risk of being IP banned from FlixPatrol.

Due to FlixPatrol limitations, titles are matched on the target backend by name and release year. This may occasionally cause bad matching.

When a FlixPatrol detail page exposes no usable premiere date, the year is reported as unknown and the match falls back to the title alone. That is deliberate: a missing year degrades the match, whereas a guessed one would silently select the wrong title.

Features

  • Sync Top 10 lists from 74 streaming platforms (Netflix, Disney+, HBO Max, Amazon Prime, etc.)
  • Sync Top 10 Kids lists from Netflix (country-specific)
  • Sync Popular lists from 2 sources (Wikipedia and Youtube)
  • Sync Netflix Most Watched annual rankings
  • Sync Netflix Most Hours rankings (total, first week, first month)
  • Support for 199 countries/regions
  • Two-level caching to reduce scraping and API calls (7-day TTL by default)
  • Automatic list management (create, update, sync) on Trakt, Floppy or mdblist
  • Dry-run mode for safe testing
  • Compatible with Kometa (formerly Plex Meta Manager)

Getting Started

The first run writes a template ./config/default.json and exits. Get that far with your platform below, then follow the next steps — they are the same for all three.

Docker

docker run --rm -v "/path/to/config:/app/config" ghcr.io/navino16/flixpatrol-top10-on-trakt:latest

Linux / macOS

  1. Download the latest release for your platform
  2. Make the binary executable and run it:
    chmod +x flixpatrol-top10-linux-x64
    ./flixpatrol-top10-linux-x64
    

Windows

  1. Download the latest release for Windows
  2. Run the binary from the command line (double-clicking will close the window automatically)

Next steps (all platforms)

  1. Choose the backend your lists are written to and fill in its Target block in ./config/default.json. Target is mandatory and carries the backend's credentials — the app refuses to start while it still holds the template placeholders. See Choosing Your Platform to pick between Trakt, Floppy and mdblist, then Trakt Setup, Floppy Setup or mdblist Setup to obtain the credentials.
  2. Edit the list blocks (FlixPatrolTop10, FlixPatrolPopular, …) to the lists you actually want — see Configuration.
  3. Run again. Then schedule periodic runs, either with an external scheduler or with the built-in daemon mode.

Choosing Your Platform

The lists this tool builds can be written to three different backends. Pick one with the Target block, which carries both the backend name and its credentials; everything else in the configuration stays the same.

Warning The Target block is new in 3.0.0. It replaces the root-level Trakt block, which is the only backend configuration earlier versions had. See Migrating from 2.x — the app detects the old format at startup and prints the exact block to write.

Trakt free (deprecated) Trakt VIP (deprecated) mdblist free mdblist 1–3 €/month Floppy
Lists 5 100 4 static 20 to 80 unlimited
Items per list 250 5 000 10 000 30 000+ unlimited
Hosting none none none none you provide it
Authentication OAuth device flow OAuth device flow API key API key API key
privacy honoured 4 levels 4 levels public/private only public/private only ignored, set it by hand in the web UI
Update date "Last Updated" in description same native last_updated_at same native latest_update

Trakt (deprecated)

Warning Trakt support is deprecated and will be removed in 4.0.0. Trakt has closed its API to third-party services — MDBList, SIMKL and WeTrakr were all blocked without notice — and has repeatedly broken authentication for integrations like this one. Rather than wait to be cut off, this project is moving away from it.

Use Floppy or mdblist instead. Both are supported today and your existing lists can be rebuilt on either in a single run.

The link and friends privacy levels go away at the same time: no other backend can express them. Replace them with private or public.

{
  "Target": {
    "type": "trakt",
    "saveFile": "./config/.trakt",
    "clientId": "your-trakt-client-id",
    "clientSecret": "your-trakt-client-secret"
  }
}

Floppy

{
  "Target": {
    "type": "floppy",
    "url": "http://localhost:8000",
    "apiKey": "your-floppy-token"
  }
}

mdblist

{
  "Target": {
    "type": "mdblist",
    "apiKey": "your-mdblist-api-key"
  }
}

Privacy levels per backend

privacy on a list entry takes private, link, friends or public. The last two are Trakt concepts with no equivalent elsewhere, so the app refuses to start when link or friends is used with Target.type set to floppy or mdblist — the error names each offending block and index. On Floppy, visibility cannot be set through the API at all: every list is created private and a warning says so once at startup.

link and friends are deprecated and removed in 4.0.0 on every backend — see Trakt (deprecated).

Migrating from 2.x

Up to and including 2.17.0 the only backend was Trakt, configured by a root-level Trakt block. 3.0.0 adds Floppy and mdblist, so the backend is now chosen explicitly: the new Target block carries type and the credentials of that backend, and the root-level Trakt block is no longer read.

There is exactly one change to make — rename the block and add "type": "trakt":

// before (2.17.0)
"Trakt": {
  "saveFile": "./config/.trakt",
  "clientId": "your-trakt-client-id",
  "clientSecret": "your-trakt-client-secret"
}

// after (3.0.0)
"Target": {
  "type": "trakt",
  "saveFile": "./config/.trakt",
  "clientId": "your-trakt-client-id",
  "clientSecret": "your-trakt-client-secret"
}

Nothing else in your configuration changes. Floppy and mdblist are new in 3.0.0 and have nothing to migrate — see Choosing Your Platform for their Target blocks.

The app detects the old format at startup, prints the exact block to write with your own values already filled in, and exits without touching your file — the config directory is frequently a read-only mount, and the file is usually version-controlled, so nothing is rewritten on your behalf.

Three more points:

  • The Target block is mandatory. A configuration with neither Target nor a root-level Trakt block gets the same migration message, defaulting to the Trakt shape.
  • Leaving the old Trakt block behind is harmless. Once Target is valid the app starts normally and only logs a warning naming the obsolete block, so you can delete it whenever you like.
  • If <Cache.savePath>/movies or <Cache.savePath>/tv-shows still exist, they are leftovers from the 2.x cache layout and nothing reads them any more. A warning names them at startup; delete them yourself whenever you like — the app never removes your files.

Configuration

Environment Variables

Name Description Values Default
LOG_LEVEL How verbose the log will be error, warn, info, debug, silly info
DRY_RUN Run without making changes to the target backend true, false false
LIST_NAME_PREFIX String prepended to every list name (useful for dev/testing) Any string, e.g. [TEST] (none)

Dry-Run Mode

Run the tool without modifying any list on the configured backend. Useful for testing your configuration:

# Linux/macOS
DRY_RUN=true ./flixpatrol-top10-linux-x64

# Docker
docker run --rm -e DRY_RUN=true -v "/path/to/config:/app/config" ghcr.io/navino16/flixpatrol-top10-on-trakt:latest

In dry-run mode:

  • FlixPatrol scraping runs normally
  • The backend search that converts titles to identifiers runs normally
  • Authentication runs normally (the OAuth device flow on Trakt, the API key on Floppy and mdblist)
  • List creation, item addition/removal, and updates are logged but not executed
  • Notifications are not suppressed, so you can test that setup too — see Notifications

List Name Prefix

Prepend a fixed string to every list name. Useful when running the tool against your real backend account during development or testing — the prefixed lists stay separate from your real lists and can be deleted in bulk afterwards.

# Linux/macOS — produces lists like "[TEST]netflix-world-top10-without-fallback"
LIST_NAME_PREFIX='[TEST]' ./flixpatrol-top10-linux-x64

# Docker
docker run --rm -e LIST_NAME_PREFIX='[TEST]' -v "/path/to/config:/app/config" ghcr.io/navino16/flixpatrol-top10-on-trakt:latest

The prefix is applied verbatim, after the normalization step (so brackets, spaces, and special characters in the prefix are preserved as-is). When active, a warning is emitted at startup so you don't forget it is set.

Notifications

Send a notification at the start of a run, at the end (with a summary), and on terminal errors. The block is optional — omit it entirely to disable notifications.

{
  "Notifications": {
    "run_start": [
      { "type": "webhook", "url": "https://discord.com/api/webhooks/..." }
    ],
    "run_end": [
      { "type": "webhook", "url": "https://discord.com/api/webhooks/..." },
      { "type": "apprise", "url": "http://apprise:8000", "key": "flixpatrol" }
    ],
    "error": [
      { "type": "gotify", "url": "https://gotify.example.com", "token": "AbCdEf123" },
      { "type": "ntfy", "url": "https://ntfy.sh", "topic": "flixpatrol-alerts" }
    ]
  }
}

Each event takes a list of destinations. A destination has a type and the fields required by that type:

Type Fields Notes
webhook url Sends generic JSON. If url matches discord.com/api/webhooks/... a Discord-shaped payload is sent automatically.
gotify url, token POSTs to {url}/message with the token sent via the X-Gotify-Key header (never in the query string, so it cannot leak into reverse-proxy access logs).
ntfy url, topic POSTs JSON to {url} with the topic in the body. Use https://ntfy.sh for the public service.
apprise url, key POSTs to {url}/notify/{key} against an Apprise API sidecar (see below).

Notifications are best-effort: a failing destination is logged at warn level but never blocks the main sync. Each adapter has a 5-second HTTP timeout and the manager caps total wait at 6 seconds. Failure logs include the adapter name and the destination's host (e.g. WebhookAdapter[discord.com]: HTTP 401) — never the full URL or any path / query secrets, so webhook tokens (Discord bearer tokens in the path, Apprise routing keys, etc.) cannot leak into log files or container stdout.

Every url field is validated as a full URL with scheme at config-load time — typos like discord.com/... (missing https://) are rejected at startup with a clear error rather than silently failing at runtime.

DRY_RUN=true does not suppress notifications (useful for testing the setup). Title and body are both prefixed with [DRY-RUN] so they cannot be mistaken for a real run, and the run_end body says items "would be added" rather than "added".

Apprise sidecar (optional)

The apprise destination talks to an Apprise API instance you host yourself. Once it is running, you configure your downstream services (Discord, Telegram, Email, etc.) inside Apprise — flixpatrol-top10 only needs to know the Apprise URL and a config key.

# docker-compose.yml excerpt
services:
  flixpatrol:
    image: ghcr.io/navino16/flixpatrol-top10-on-trakt:latest
    volumes:
      - ./config:/app/config
    depends_on:
      - apprise

  apprise:
    image: caronc/apprise:latest
    ports:
      - "8000:8000"
    volumes:
      - ./apprise-config:/config

In the Apprise web UI (default http://localhost:8000), create a configuration key (for example flixpatrol) and add your downstream Apprise URLs (discord://..., tgram://..., mailto://..., etc.) under that key. Then reference it from config/default.json:

{ "type": "apprise", "url": "http://apprise:8000", "key": "flixpatrol" }

Configuration File

The configuration file is stored at ./config/default.json (auto-generated on first run).

If there is any configuration error, the tool will exit with information about the error.

FlixPatrolTop10 — Top 10 list configuration
Name Description Mandatory Values Default
platform Which platform to get from Flixpatrol Yes Any Flixpatrol platform (see this)
location Which location to get from Flixpatrol Yes Any Flixpatrol location (see this)
fallback Fallback to another location if no results? Yes False or any Flixpatrol location (see this) false
privacy Privacy of the generated list (backend support varies) Yes private, link, friends, public private
limit How many movie/show to get Yes Number >= 1 10
type Movies, shows or both? Yes movies, shows, both both
name Optional custom list name No Any valid string A generated name based on the top10 config
normalizeName Normalize the list name to kebab-case? No true, false true
kids Get Kids Top 10 (Netflix only, requires specific country) No true, false false

Note on fallback: The fallback is tried only when the configured location exists but has no rankings (empty chart). If the location does not exist for that platform (e.g. Hulu for Russia), the entry is skipped, the list stays unchanged, and the run reports a dead path — fallback cannot fix that, as it would silently fill the list with the wrong location's content.

FlixPatrolPopular — Popular list configuration
Name Description Mandatory Values Default
platform Which popular source to get from Flixpatrol Yes Any Flixpatrol popular platform (see this)
privacy Privacy of the generated list (backend support varies) Yes private, link, friends, public private
limit How many movie/show to get Yes Number between 1 and 100 100
type Movies, shows or both? Yes movies, shows, both both
name Optional custom list name No Any valid string A generated name based on the popular config
normalizeName Normalize the list name to kebab-case? No true, false true
FlixPatrolMostWatched — Netflix Most Watched configuration
Name Description Mandatory Values Default
enabled Enable this most watched list? Yes true, false true
privacy Privacy of the generated list (backend support varies) Yes private, link, friends, public private
type Movies, shows or both? Yes movies, shows, both both
limit How many movie/show to get Yes Number between 1 and 50 50
year Year of the most watched list Yes Number between 2023 and current year current year
name Optional custom list name No Any valid string most-watched
normalizeName Normalize the list name to kebab-case? No true, false true
premiere Filter by premiere year No Year between 1980 and current year All
country Filter by release country No 93 values, see below All
original Netflix originals only? No true, false false
genre Filter by genre. 30 values, see below. Must exist for every requested type. No See below All

genre — common to both types: action, adventure, animation, biography, comedy, crime, documentary, drama, family, fantasy, history, horror, romance, science-fiction, superhero, thriller, war, western. Movies only: concerts, fairy-tale, musical, record, sports. Shows only: broadcast, game-show, music, news, reality-show, sport, talk-show. With type: "both", only the 18 common genres are accepted. Note sports for movies and sport for shows: this is FlixPatrol's own spelling.

country — 93 values, copied from flixpatrolMostWatchedCountry. This is not the same list as FlixPatrolTop10 locations: china, russia and monaco for example are not in it.

argentina, australia, austria, bahamas, bahrain, bangladesh, belgium, bolivia, brazil, bulgaria, canada, chile, colombia, costa-rica, croatia, cyprus, czech-republic, denmark, dominican-republic, ecuador, egypt, estonia, finland, france, germany, greece, guadeloupe, guatemala, honduras, hong-kong, hungary, iceland, india, indonesia, ireland, israel, italy, jamaica, japan, jordan, kenya, kuwait, latvia, lebanon, lithuania, luxembourg, malaysia, maldives, malta, martinique, mauritius, mexico, morocco, netherlands, new-caledonia, new-zealand, nicaragua, nigeria, norway, oman, pakistan, panama, paraguay, peru, philippines, poland, portugal, qatar, reunion, romania, salvador, saudi-arabia, serbia, singapore, slovakia, slovenia, south-africa, south-korea, spain, sri-lanka, sweden, switzerland, taiwan, thailand, trinidad-and-tobago, turkey, ukraine, united-arab-emirates, united-kingdom, united-states, uruguay, venezuela, vietnam.

Shows are always grouped by title, never listed season by season.

FlixPatrolMostHours — Netflix Most Hours configuration
Name Description Mandatory Values Default
enabled Enable this most hours list? Yes true, false true
privacy Privacy of the generated list (backend support varies) Yes private, link, friends, public private
type Movies, shows or both? Yes movies, shows, both both
limit How many movie/show to get Yes Number between 1 and 100 50
period Which ranking period Yes total, first-week, first-month total
language Filter by language (first-week and first-month only) No all, english, non-english all
name Optional custom list name No Any valid string netflix-most-hours-{period}
normalizeName Normalize the list name to kebab-case? No true, false true
FlixPatrolWeekly — weekly /hours/ rankings (Netflix, Amazon Prime)
Name Description Mandatory Values Default
enabled Enable this weekly list? Yes true, false true
privacy Privacy of the generated list (backend support varies) Yes private, link, friends, public private
type Movies, shows or both? Yes movies, shows, both both
limit How many movie/show to get Yes Number between 1 and 20
platform Which platform's weekly chart Yes netflix, amazon-prime
location Worldwide chart, or a per-country chart (Netflix only) No world, or one of the 93 countries listed under FlixPatrolMostWatched above world
language Filter the worldwide chart by language No all, english, non-english all
name Optional custom list name No Any valid string see below
normalizeName Normalize the list name to kebab-case? No true, false true

limit only reaches 20 with language: "all": the worldwide chart then concatenates two sections of 10 (English first). Every other combination — a specific language, or any per-country location — tops out at 10, because that is all FlixPatrol publishes for a single section.

A location other than world is Netflix-only: Amazon Prime publishes no per-country weekly page. That pairing (platform: "amazon-prime" with a country) only warns at config validation — the entry is skipped, never a hard error. Netflix's per-country page carries the platform's own official ranking, which carries no language split: a language set there also only warns, but the entry is not skipped — language is ignored and the list is still produced.

Default list name: {platform}-weekly-{language} worldwide (the -{language} suffix is dropped for all), {platform}-weekly-{location} for a country entry.

Target — Which backend the lists are written to, and its credentials

Target is a discriminated union on type: it carries the backend name and exactly the credentials that backend needs. The block is mandatory.

Name Description Mandatory Values Default
type Which backend receives the generated lists Yes trakt, floppy, mdblist
saveFile Where to save the Trakt session file If type: "trakt" Any valid path None — no shipped template supplies one; ./config/.trakt is the conventional value
clientId Your clientId from Trakt (get one here) If type: "trakt" A valid string
clientSecret Your clientSecret from Trakt (get one here) If type: "trakt" A valid string
url Base URL of your Floppy instance If type: "floppy" Any valid URL
apiKey Floppy API token (Settings → Advanced), or mdblist API key (preferences page) If type is floppy or mdblist A valid string

It replaces the root-level Trakt block of 2.17.0 and earlier — see Migrating from 2.x. See Choosing Your Platform for the trade-offs between the three backends.

Cache — Caching
Name Description Mandatory Values Default
Cache.enabled Enable caching? (recommended) Yes true, false true
Cache.savePath Where to save the cache files Yes Any valid path ./config/.cache
Cache.ttl Cache validity duration in seconds Yes Number > 0 604800 (7 days)

The cache has two levels under savePath: details/ for the scraped FlixPatrol detail pages, and resolution-<backend>/ for the title-to-identifier mapping of each backend. Older movies/ and tv-shows/ directories are leftovers and can be deleted; a startup warning names them if they are still there.

Schedule — Daemon mode / built-in scheduling configuration
Name Description Mandatory Values Default
enabled Enable the built-in scheduler (daemon mode)? No true, false false
crons Cron expression(s) the app runs on, 5-field format (min hour day month weekday) Yes, when enabled: true Array of valid cron strings, e.g. ["0 6 * * *"] []
runOnStart Also run immediately at startup, in addition to the schedule? No true, false false

The whole Schedule block is optional — omit it entirely (or leave enabled: false) to keep the classic one-shot behaviour: the app runs once and exits, exactly like today. See Daemon Mode below for details.

FlareSolverr — Cloudflare challenge bypass (optional)

FlixPatrol is behind a Cloudflare managed challenge that returns HTTP 403 to non-browser clients. FlareSolverr solves that challenge and proxies the request.

This block is entirely optional. If it is absent, or enabled is false, the tool behaves exactly as before and never contacts FlareSolverr.

Name Description Mandatory Values Default
enabled Route FlixPatrol requests through FlareSolverr No true, false false
url FlareSolverr v1 API endpoint If enabled Any valid URL
maxTimeout Challenge solving timeout in milliseconds No Number 60000
disableMedia Skip images, CSS and fonts while scraping No true, false false

When enabled, a browser session is created once at the start of each run and destroyed at the end. The first request solves the challenge (around 12s); later requests reuse the session and take 1-3s each.

disableMedia makes the solver's browser skip images, stylesheets and fonts, which the scraper never looks at — it only reads HTML. Measured over 80 requests, it takes about 15% off those later requests and leaves the initial challenge solve unchanged, since the challenge is what dominates and it still runs its JavaScript normally. Two things temper it: most of the saving lands on cold-cache runs, because detail pages are cached for Cache.ttl and never reach FlareSolverr twice; and a browser that fetches no stylesheet at all is a slightly unusual traffic shape, which Cloudflare could in principle score. Zero solve failures were observed either way, but the default stays false so nothing changes for existing setups. It also lowers the container's memory and CPU use, which the FlareSolverr docs warn about.

Run FlareSolverr alongside the tool:

# docker-compose.yml
services:
  flaresolverr:
    image: ghcr.io/flaresolverr/flaresolverr:latest
    container_name: flaresolverr
    # Published on loopback only: FlareSolverr has no authentication and must not
    # be reachable from the network.
    ports:
      - "127.0.0.1:8191:8191"
    environment:
      - LOG_LEVEL=info
    restart: unless-stopped

If the tool itself runs in Docker on the same Compose network, use the service name instead of localhost: "url": "http://flaresolverr:8191/v1".

Example configuration

Note This example defines 11 lists — more than a free mdblist account (4 static lists) or a free Trakt account (5 lists) allows. It exists to show the available options, not as a ready-to-use file: trim it to your backend's capacity. See Choosing Your Platform.

{
  "FlixPatrolTop10": [
    {
      "platform": "netflix",
      "location": "world",
      "fallback": false,
      "privacy": "private",
      "limit": 10,
      "name": "Netflix Top 10 Movies",
      "type": "movies"
    },
    {
      "platform": "disney",
      "location": "world",
      "fallback": false,
      "privacy": "private",
      "limit": 10,
      "name": "Disney Plus Top 10 Shows",
      "type": "shows"
    },
    {
      "platform": "amazon-prime",
      "location": "world",
      "fallback": false,
      "privacy": "private",
      "limit": 10,
      "name": "Amazon Prime Top 10",
      "type": "both"
    },
    {
      "platform": "netflix",
      "location": "united-states",
      "fallback": false,
      "privacy": "private",
      "limit": 10,
      "name": "Netflix Top 10 Kids",
      "type": "both",
      "kids": true
    }
  ],
  "FlixPatrolPopular": [
    {
      "platform": "wikipedia",
      "privacy": "private",
      "limit": 100,
      "type": "both"
    }
  ],
  "FlixPatrolMostWatched": [
    {
      "enabled": true,
      "privacy": "public",
      "year": 2023,
      "limit": 50,
      "type": "both"
    }
  ],
  "FlixPatrolMostHours": [
    {
      "enabled": true,
      "privacy": "public",
      "limit": 50,
      "type": "both",
      "period": "total"
    },
    {
      "enabled": true,
      "privacy": "public",
      "limit": 50,
      "type": "both",
      "period": "first-week"
    },
    {
      "enabled": true,
      "privacy": "public",
      "limit": 50,
      "type": "movies",
      "period": "first-month",
      "language": "english"
    }
  ],
  "FlixPatrolWeekly": [
    {
      "enabled": true,
      "privacy": "public",
      "limit": 20,
      "type": "both",
      "platform": "netflix"
    },
    {
      "enabled": true,
      "privacy": "public",
      "limit": 10,
      "type": "both",
      "platform": "netflix",
      "location": "france"
    }
  ],
  "Target": {
    "type": "mdblist",
    "apiKey": "your-mdblist-api-key"
  },
  "Cache": {
    "enabled": true,
    "savePath": "./config/.cache",
    "ttl": 604800
  },
  "Notifications": {
    "run_start": [],
    "run_end": [],
    "error": []
  },
  "Schedule": {
    "enabled": false,
    "crons": ["0 6 * * *"],
    "runOnStart": false
  },
  "FlareSolverr": {
    "enabled": false,
    "url": "http://localhost:8191/v1",
    "maxTimeout": 60000,
    "disableMedia": false
  }
}

The Notifications block is fully optional — leave the arrays empty (or omit the block entirely) to disable notifications. See Notifications above for the supported destination types and a worked example.

The Schedule block is fully optional and disabled by default — omit it (or leave enabled: false) to keep the classic one-shot behaviour. See Daemon Mode below.

The FlareSolverr block is fully optional and disabled by default — omit it (or leave enabled: false) to keep the classic behaviour of talking directly to FlixPatrol.

Trakt Setup

Warning Deprecated — removed in 4.0.0. See Choosing Your Platform.

To run this application you need a Trakt account and a Client ID / Client Secret.

Warning Trakt free accounts are limited to 5 lists. If you configure more, the tool will fail. Remove some or upgrade to Trakt VIP.

  1. Create an account or login
  2. Create a new application with:
    • Redirect uri: urn:ietf:wg:oauth:2.0:oob
    • Other fields are optional
  3. Replace the Target block in ./config/default.json with the Trakt shape:
{
  "Target": {
    "type": "trakt",
    "saveFile": "./config/.trakt",
    "clientId": "your-trakt-client-id",
    "clientSecret": "your-trakt-client-secret"
  }
}
  1. Run the app and follow the on-screen instructions

Floppy Setup

Floppy is a self-hosted media tracker. Point the tool at your instance and it will create and refresh lists there instead of on Trakt.

  1. Create a dedicated account on your instance for this tool. Do not reuse your personal one: adding a title Floppy has never seen goes through a catalogue bootstrap that briefly creates a Planning tracking row, which the tool deletes right after. If the process is killed in that narrow window, the stray row stays behind — on a dedicated account it is harmless noise, on your own account it pollutes your watchlist.
  2. Log in as that account and copy its token from Settings → Advanced.
  3. Put it in Target.apiKey, and your instance URL in Target.url.
{
  "Target": {
    "type": "floppy",
    "url": "http://localhost:8000",
    "apiKey": "your-floppy-token"
  }
}

Note The Floppy API exposes no way to set a list's visibility, so the privacy field of your list entries is ignored and every list the tool creates stays private — a warning says so once at startup. If you want to share one, flip it by hand in the web UI. This blocks nothing in practice: Kometa reads private lists with the same token, so a private list is fully usable. Note that link and friends are rejected outright on this backend, as they are Trakt-only concepts.

When wiring the result into Kometa, prefer the floppy_list builder over floppy_list_details: the latter overwrites your Plex collection summary with the list description, and the tool never writes one on this backend — it relies on Floppy's native latest_update field instead of a "Last Updated" description.

mdblist Setup

mdblist is a hosted list service authenticated by a single API key.

  1. Create an account and copy your API key from your preferences page.
  2. Put it in Target.apiKey.
{
  "Target": {
    "type": "mdblist",
    "apiKey": "your-mdblist-api-key"
  }
}

Note mdblist only knows public and private lists, so link and friends are rejected at startup on this backend. Use private or public.

Warning A free mdblist account is capped at 4 static lists. Configure more entries than that and list creation will start failing — trim your configuration or take a paid plan.

Note The mdblist search endpoint rejects titles containing characters beyond Latin-1 with 400 Invalid search query. Accented latin letters are fine (Amélie resolves), but curly quotes, dashes and ellipses are not — the tool folds those back to ASCII, so Let’s Marry Harry is found anyway. A title in a non-latin script cannot be folded and would stay unresolvable here; in practice that does not happen, because FlixPatrol publishes titles in English or romaji even for a platform like Crunchyroll. Should one ever appear, it logs a warning and is skipped, and the rest of the list is written normally.

The API budget is 1 000 requests per day, billed one unit per HTTP call. Two things keep a run cheap:

  • Writes scale per list, not per item. mdblist accepts bulk add and remove, and both media types are written in a single call, so a list of 100 items costs the same as a list of 3 — about four calls, plus one shared index lookup per run.
  • Resolution is cached (see Cache.ttl). Only titles the tool has never resolved before consume a search call, so the first run carries the cost and later runs do not.

In practice a 10-entry configuration costs a few hundred units on its first run and a few dozen afterwards — comfortably inside the free tier, whose real ceiling is the 4-list cap above rather than the request budget. The remaining budget is logged after every list write, and any API response reports it in x-ratelimit-remaining.

Supported Platforms

Top 10 Platforms (74)

9now, abema, amazon, amazon-channels, amazon-prime, amc-plus, antenna-tv, apple-tv, bbc, canal, catchplay, cda, chili, claro-video, coupang-play, crunchyroll, discovery-plus, disney, francetv, friday, globoplay, go3, google, hami-video, hayu, hbo-max, hrti, hulu, hulu-nippon, itunes, jiocinema, jiohotstar, joyn, lemino, m6plus, mgm-plus, myvideo, neon-tv, netflix, now, oneplay, osn, paramount-plus, peacock, player, pluto-tv, raiplay, rakuten-tv, rtl-plus, sbs, shahid, skyshowtime, stan, starz, streamz, telasa, tf1, tod, trueid, tubi, tv-2-norge, u-next, viaplay, videoland, vidio, viki, viu, vix, voyo, vudu, watchit, wavve, wow, zee5

Popular Sources (2)

wikipedia, youtube

Locations (199)

world, united-states, france, united-kingdom, germany, canada, australia, japan, and 191 more countries...

For the complete list, see the source code: Config.types.ts

Scheduling

Linux (cron)

# Run daily at 6 AM
0 6 * * * /path/to/flixpatrol-top10-linux-x64

# Run every 12 hours
0 */12 * * * /path/to/flixpatrol-top10-linux-x64

Docker with cron

# Run daily at 6 AM
0 6 * * * docker run --rm -v "/path/to/config:/app/config" ghcr.io/navino16/flixpatrol-top10-on-trakt:latest

Windows Task Scheduler

  1. Open Task Scheduler
  2. Create a new task
  3. Set the trigger (e.g., daily at 6 AM)
  4. Set the action to run the executable

Daemon Mode (built-in scheduling)

Instead of relying on an external scheduler (cron, Task Scheduler, docker run on a timer), the app can run as a long-lived process with its own built-in scheduler. Add a Schedule block to config/default.json and set enabled: true:

{
  "Schedule": {
    "enabled": true,
    "crons": ["0 6 * * *"],
    "runOnStart": false
  }
}
Field Description
enabled Turns daemon mode on. When false or omitted, the app runs once and exits — the current behaviour is completely unchanged.
crons One or more cron expressions, standard 5-field format (min hour day month weekday), e.g. "0 6 * * *" for daily at 6 AM. Must contain at least one entry when enabled is true.
runOnStart When true, triggers an immediate run at startup, then continues to follow the configured schedule.

Backward compatibility

The Schedule block is entirely optional. If it is absent, or enabled is false, the app behaves exactly as before: it runs once and exits with the appropriate code. Your existing external cron job or docker run on a timer keeps working without any change.

Timezone

The scheduler follows the system clock. There is no timezone field in the config — set the TZ environment variable (e.g. TZ=Europe/Paris) on the host or container so cron expressions are evaluated in the timezone you expect.

Behaviour while running

  • If a scheduled trigger fires while a run is still in progress, it is skipped (logged as a warning) rather than queued or run concurrently.
  • A failed run is logged and sent through the Notifications system (the error event), but it does not stop the daemon — the scheduler keeps waiting for the next trigger.
  • On SIGTERM (e.g. docker stop) or SIGINT (Ctrl-C), the app performs a graceful shutdown: it stops accepting new triggers and waits for the current run to finish its backend write before exiting, so lists are never left half-updated. Because each list is written in a single call, the stop point always falls between two lists — never between the movies and the shows of the same list.

Docker Compose example

Run the container as a long-lived daemon instead of a one-shot job:

# docker-compose.yml
services:
  flixpatrol:
    image: ghcr.io/navino16/flixpatrol-top10-on-trakt:latest
    restart: unless-stopped
    environment:
      - TZ=Europe/Paris
    volumes:
      - ./config:/app/config

With Schedule.enabled: true in config/default.json, this container stays up, runs on the configured cron schedule(s), and survives restarts (restart: unless-stopped). This replaces the external-cron pattern shown above (docker run triggered by a host cron entry) — you no longer need a cron job on the host, since the schedule now lives inside the app itself.

Troubleshooting

Roughly ordered by how often each one comes up.

Startup fails with Configuration format changed in 3.0.0. and prints a Target block. Your configuration still uses the 2.17-and-earlier shape: a root-level Trakt block and no Target. The message contains the exact block to paste, with your own values already filled in — copy it into config/default.json in place of the old block. Your file is never rewritten for you: the config directory is frequently a read-only mount and usually version-controlled. See Migrating from 2.x.

Startup fails saying a Target field still holds a placeholder value. The config/default.json the app generated on first run was never edited. A fresh install ships Target.apiKey for mdblist — replace it with your real key from your preferences page. The same check catches unreplaced Trakt clientId/clientSecret if you configured that backend by hand instead. It runs field by field, so replacing only some of the fields is still caught and the message names the ones left over.

Warning about an obsolete root-level Trakt block. Nothing is broken: the run proceeds normally. Credentials now live inside Target, so the root-level block is no longer read. Delete it whenever you like to silence the warning.

Unable to get FlixPatrol ... page with HTTP 403 (cf-mitigated: challenge). Cloudflare is challenging the request. Enable the optional FlareSolverr block and point it at a FlareSolverr instance.

Warning naming leftover cache directories movies/ and tv-shows/ under your cache path. They are the 2.x cache layout. The cache is now split into details/ and resolution-<backend>/, so nothing reads them any more. Delete them yourself — the app never removes your files.

One list stopped updating, on a market that charts only one media type. This is the correct behaviour, not a regression. When FlixPatrol publishes no chart for a media type, that half of the list is left exactly as it was rather than being filled with the other type's rows — which is what earlier versions could do. The other half still updates normally.

A kids: true entry produced nothing. Kids is the last section of the day FlixPatrol publishes, so an early run finds the tables absent and leaves the list unchanged rather than writing the wrong content. Run later in the day. Also check the entry is on netflix with a specific location: kids charts do not exist for other platforms or for world, and the app skips the entry with a warning.

Titles in the list are wrong or missing. FlixPatrol exposes only a name and a release year, so titles are matched on the backend by those two fields and can occasionally mismatch. When a detail page exposes no usable premiere date the year is reported as unknown and the match falls back to the title alone. If nothing at all resolves, a warning says so and the list is left unchanged instead of being emptied.

No items found for a platform/location combination. Verify the combination actually exists on FlixPatrol — not every platform charts in every country. Set fallback to another location if you want an empty result to fall back rather than produce nothing.

Creating a list fails once you have a few of them. A free Trakt account is capped at 5 personal lists and a free mdblist account at 4 static lists. Beyond that, list creation starts failing. Trim your configuration, or upgrade (Trakt VIP, or a paid mdblist plan). Floppy has no such cap.

On Floppy, every list is created private and privacy is ignored. The Floppy API exposes no way to set visibility, so the setting cannot be honoured; a warning says so once at startup. Flip the ones you want to share by hand in the Floppy web UI. This blocks nothing for Kometa, which reads private lists with the same token. Note that link and friends are rejected outright on Floppy and mdblist — see Privacy levels per backend.

Permission denied on the config folder (Docker). The image runs as a non-root user (flixpatrol, UID 1000). Fix ownership of the mounted directory: sudo chown -R 1000:1000 /path/to/config.

Authentication failed on Trakt. Delete the token file Target.saveFile points at (./config/.trakt by default) and run again to go through the OAuth device flow afresh. On Floppy and mdblist there is no token file — re-check Target.apiKey.

Rate limit exceeded. Increase the time between runs. Keeping Cache.enabled: true reduces both scraping and backend API calls substantially, since resolved titles are not looked up again until the TTL expires.

Development

Local setup, the full command reference (build, lint, unit tests, coverage), the code style and the pull request conventions live in CONTRIBUTING.md.

npm run test:e2e runs the opt-in end-to-end suites against real services — a real Floppy instance, a real Trakt account, a real mdblist account, the live FlixPatrol site. Each suite is enabled by its own environment variables and skips cleanly when they are missing, so running it with no environment at all skips everything and exits green. See CONTRIBUTING.md for the variables, the local infrastructure and what runs in CI.

License

GNU General Public License v3.0

Requirements


        Expects a config file in the Config Path. See documentation: https://github.com/Navino16/flixpatrol-top10-on-trakt#configuration-file
    

Details

Repository
ghcr.io/navino16/flixpatrol-top10-on-trakt:latest
Last Updated2026-10-16
First Seen2025-05-11

Runtime arguments

Network
bridge
Privileged
false

Template configuration

Config PathPath

Path to app data

Target
/app/config
Default
/mnt/user/appdata/flixpatrol_to_trakt/config
Value
/mnt/user/appdata/flixpatrol_to_trakt/config
Log LevelVariable

Log level for the application

Target
LOG_LEVEL
Default
info|err|warn|debug