Change the default audio and subtitle streams for items in Plex per user based on codec, language, keywords and more. Customizable with filters and groups. Can run on a schedule or for newly added items using a Tautulli webhook.
services:
defaulterr:
image: varthe/defaulterr:latest
container_name: defaulterr
hostname: defaulterr
ports:
- 3184:3184
volumes:
- /path/to/config:/config
- /path/to/logs:/logs
environment:
- LOG_LEVEL=info
Click here to download the Unraid template.
Your configuration is defined in config.yaml
. Below is a breakdown of the required settings and optional configurations.
See config.yaml for an example of an implementation.
- plex_server_url: Your Plex server URL.
- plex_owner_name: Used to identify the owner, allowing them to be included in groups.
- plex_owner_token: The server owner's token.
- plex_client_identifier: Find this value using the instructions below.
- Go to
https://plex.tv/api/resources?X-Plex-Token={your_admin_token}
(replace{your_admin_token}
with your token). - Search for your server and find the
clientIdentifier
value. This HAS TO be the server's identifier, not the owner's.
- dry_run: Set to
True
to test filters. This mode won't update users and is recommended to verify that your filters work correctly. It overwrites other run settings. - partial_run_on_start: Set to
True
to do a partial run on application start.- WARNING: The first run may take a LONG time to complete as it will update all existing media. Subsequent runs will only update any new items added since the last run.
- partial_run_cron_expression: Specify a cron expression (e.g.,
0 3 * * *
for daily at 3:00 AM) to do a partial run on a schedule. You can create and check cron expressions at https://crontab.guru/. - clean_run_on_start: Set to
True
to update all existing media on application start. Should only be used if you want to re-apply a new set of filters on your libraries.
Groups define collections of users with shared filters:
- Usernames must match exactly as they appear in Plex, including capitalization and special characters.
- Managed accounts require additional setup. Read below.
- Optionally, use
$ALL
in place of a username to include all users from your server.
Example:
groups:
serialTranscoders: # Can be named anything
- varthe
- UserWithCapitalLetters # EXACTLY like in Plex
- $ALL # Grabs all users from the server
subtitleEnjoyers: # Can be named anything
- varthe
deafPeople: # Can be named anything
- varthe
weebs: # Can be named anything
- varthe
To include managed accounts in groups you will need to supply their tokens manually. See this comment by Blind_Watchman on how to obtain their tokens. You have to do it like this because the regular tokens won't work.
Include them in the config file like below. Use the key (e.g user1
) in groups.
managed_users:
user1: token
user2: token
Filters define how audio and subtitle streams are updated based on specified criteria. The structure in config.yaml
is as follows:
- Library Name: The filter applies to a specific Plex library.
- Group Name: Defines which group the filter targets.
- Stream Type: Can be
audio
orsubtitles
.- include: Fields that MUST appear in the stream AND include the specified value
- exclude: Fields that MUST NOT appear in the stream OR not be the specified value
- on_match: Specifies filters for the other stream type if a match is found. For example, disable subtitles if a Spanish audio track is matched. Otherwise find Spanish subtitles.
- Stream Type: Can be
- Group Name: Defines which group the filter targets.
Note: Any field (e.g.,
language
,codec
,extendedDisplayTitle
) can either be a single value or an array of values. This allows flexibility in filtering criteria by matching multiple options when needed.
Multiple groups and filters can be defined per library, with the first matching filter being applied. If no filters match, the item remains unchanged in Plex. Filters can utilize any property in the stream object returned by Plex. See example.json for examples.
filters:
Movies: # Library name
serialTranscoders: # Group name
audio:
# Audio Filter 1 - First English audio track that's not TRUEHD/DTS and not a commentary
- include:
language: English # Needs to be in the original language, e.g Español for Spanish
# languageCode: eng # Alternative to the above, e.g. jpn for Japanese
exclude:
codec:
- truehd
- dts
extendedDisplayTitle: commentary
# Audio Filter 2 - Any English track (fallback if the above filter doesn't match)
- include:
language: English
subtitleEnjoyers:
subtitles:
# Subtitle Filter 1 - First English track that's not forced
- include:
language: English
exclude:
extendedDisplayTitle: forced
deafPeople:
subtitles:
# Subtitle Filter 1 - First English SDH track
- include:
language: English
hearingImpaired: true # SDH
Anime: # Library name
weebs: # Group name
audio:
# Audio Filter 1 - First English track with disabled subtitles
- include:
language: English
on_match:
subtitles: disabled # Set subtitles to "off" in Plex
# Audio Filter 2 - Japenese track with English subtitles
- include:
languageCode: jpn # Japenese
on_match:
subtitles:
# Full subtitles -> Dialogue subtitles -> Anything without the word "signs"
- include:
language: English
extendedDisplayTitle: full
- include:
language: English
extendedDisplayTitle: dialogue
- include:
language: English
exclude:
extendedDisplayTitle: signs
To automate filter applications for newly added items:
- Go to Settings -> Notifications & Newsletters in Tautulli.
- Set Recently Added Notification Delay to 60 (increase if notifications are firing too early).
- Navigate to Settings -> Notification Agents.
- Add a new notification agent and select Webhook.
- Use the Defaulterr URL:
http://defaulterr:3184/webhook
. - Choose POST for the Webhook Method.
- Enable the Recently Added trigger.
- Paste the following JSON data into JSON Data:
<movie>
{
"type": "movie",
"libraryId": "{section_id}",
"mediaId": "{rating_key}"
}
</movie>
<show>
{
"type": "show",
"libraryId": "{section_id}",
"mediaId": "{rating_key}"
}
</show>
<season>
{
"type": "season",
"libraryId": "{section_id}",
"mediaId": "{rating_key}"
}
</season>
<episode>
{
"type": "episode",
"libraryId": "{section_id}",
"mediaId": "{rating_key}"
}
</episode>