All apps · 0 apps
Flixpatrol-Top-10-on-Trakt
Docker app from grtgbln's Repository
Overview
Readme
View on GitHub
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.
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
- Download the latest release for your platform
- Make the binary executable and run it:
chmod +x flixpatrol-top10-linux-x64 ./flixpatrol-top10-linux-x64
Windows
- Download the latest release for Windows
- Run the binary from the command line (double-clicking will close the window automatically)
Next steps (all platforms)
- Choose the backend your lists are written to and fill in its
Targetblock in./config/default.json.Targetis 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. - Edit the list blocks (
FlixPatrolTop10,FlixPatrolPopular, …) to the lists you actually want — see Configuration. - 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
Targetblock is new in 3.0.0. It replaces the root-levelTraktblock, 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
linkandfriendsprivacy levels go away at the same time: no other backend can express them. Replace them withprivateorpublic.
{
"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
Targetblock is mandatory. A configuration with neitherTargetnor a root-levelTraktblock gets the same migration message, defaulting to the Trakt shape. - Leaving the old
Traktblock behind is harmless. OnceTargetis 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>/moviesor<Cache.savePath>/tv-showsstill 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.
- Create an account or login
- Create a new application with:
- Redirect uri:
urn:ietf:wg:oauth:2.0:oob - Other fields are optional
- Redirect uri:
- Replace the
Targetblock in./config/default.jsonwith the Trakt shape:
{
"Target": {
"type": "trakt",
"saveFile": "./config/.trakt",
"clientId": "your-trakt-client-id",
"clientSecret": "your-trakt-client-secret"
}
}
- 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.
- 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
Planningtracking 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. - Log in as that account and copy its token from Settings → Advanced.
- Put it in
Target.apiKey, and your instance URL inTarget.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
privacyfield 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 thatlinkandfriendsare 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.
- Create an account and copy your API key from your preferences page.
- Put it in
Target.apiKey.
{
"Target": {
"type": "mdblist",
"apiKey": "your-mdblist-api-key"
}
}
Note mdblist only knows public and private lists, so
linkandfriendsare rejected at startup on this backend. Useprivateorpublic.
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élieresolves), but curly quotes, dashes and ellipses are not — the tool folds those back to ASCII, soLet’s Marry Harryis 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
- Open Task Scheduler
- Create a new task
- Set the trigger (e.g., daily at 6 AM)
- 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
errorevent), but it does not stop the daemon — the scheduler keeps waiting for the next trigger. - On
SIGTERM(e.g.docker stop) orSIGINT(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
Requirements
Expects a config file in the Config Path. See documentation: https://github.com/Navino16/flixpatrol-top10-on-trakt#configuration-file
Related apps
Explore more like this
Explore allDetails
ghcr.io/navino16/flixpatrol-top10-on-trakt:latestRuntime arguments
- Network
bridge- Privileged
- false
Template configuration
Path to app data
- Target
- /app/config
- Default
- /mnt/user/appdata/flixpatrol_to_trakt/config
- Value
- /mnt/user/appdata/flixpatrol_to_trakt/config
Log level for the application
- Target
- LOG_LEVEL
- Default
- info|err|warn|debug