[167] update changelog and missing documentation to prep for 1.19
65
CHANGELOG
|
@ -1,17 +1,58 @@
|
||||||
# Requirements Update (requirements will need to be reinstalled)
|
# Requirements Update (requirements will need to be reinstalled)
|
||||||
Updated arrapi requirement to 1.4.2
|
Updated pillow requirement to 9.5.0
|
||||||
Updated pillow requirement to 9.4.0
|
Updated plexapi requirement to 4.13.4
|
||||||
Updated requests requirement to 2.28.2
|
New requirement GitPython version 3.1.31
|
||||||
|
|
||||||
# New Features
|
# New Features
|
||||||
Added new collection_order `custom.desc` ([FR](https://features.metamanager.wiki/features/p/reverse-sort-collectionorder-custom))
|
Added `episode_year` as a dynamic collection option.
|
||||||
Added webp Image Support ([FR](https://features.metamanager.wiki/features/p/support-webp-image-extensions))
|
Added `mass_studio_update` [library operation](https://metamanager.wiki/en/latest/config/operations.html#mass-studio-update).
|
||||||
Added Spanish Defaults Translation
|
Changes Environment Variable/Run Argument list separator from `,` to `|`.
|
||||||
Added Delete Webhooks
|
Added `PMM_LOG_REQUESTS`/`--log-requests` Environment Variable/Run Argument which will log every single HTTP request in the log.
|
||||||
Added collection detail `delete_collections_named` to delete any collections listed while running this collection definition.
|
Added EXIF Tags to Overlayed Images to be able to determine if they have an overlay or not.
|
||||||
|
Added `anidb`, `anidb_3_0`, `anidb_2_5`, `anidb_2_0`, `anidb_1_5`, `anidb_1_0`, `anidb_0_5` options to the [`mass_genre_update` Library Operation](https://metamanager.wiki/en/latest/config/operations.html#mass-genre-update).
|
||||||
|
Added `ignore_cache` to [`radarr`](https://metamanager.wiki/en/latest/config/radarr.html) and [`sonarr`](https://metamanager.wiki/en/latest/config/sonarr.html) Settings and `radarr_ignore_cache` and `sonarr_ignore_cache` to [Radarr/Sonarr Definition Settings](https://metamanager.wiki/en/latest/metadata/details/arr.html).
|
||||||
|
Closes #1286 Updates Synology Walkthrough with DSM7 images.
|
||||||
|
Closes #1159 Adds support for official trakt lists.
|
||||||
|
Closes #1251 When resetting Overlays Seasons where theres no poster will use the show poster.
|
||||||
|
Templates can now be used with metadata updates.
|
||||||
|
`allowed_library_types` Definition Setting has been changed to `run_definition` the old attribute will still work in the same way.
|
||||||
|
Added `mapping_id`, `run_definition`, `update_seasons`, and `update_episodes` to Metadata definitions.
|
||||||
|
Added a [Ratings Explained](https://metamanager.wiki/en/latest/home/guides/ratings.html) page to the Wiki to help explain how PMM interacts with the various Ratings.
|
||||||
|
Add more options to the [`mass_imdb_parental_labels` Library Operation](https://metamanager.wiki/en/latest/config/operations.html#mass-imdb-parental-labels).
|
||||||
|
Added `imdb_keyword` as a [Tag Filter](https://metamanager.wiki/en/latest/metadata/filters.html#tag-filters).
|
||||||
|
Added `has_edition` as a [Boolean Filter](https://metamanager.wiki/en/latest/metadata/filters.html#boolean-filters).
|
||||||
|
Added `has_stinger` and `stinger_rating` as [Filters](https://metamanager.wiki/en/latest/metadata/filters.html) based on http://www.mediastinger.com
|
||||||
|
When editing episode metadata the key can now be either episode number, episode title, or episodeoriginally released date.
|
||||||
|
The Collectionless builder now can work with other builders.
|
||||||
|
|
||||||
|
# New Defaults Features
|
||||||
|
Removed Translations from the defaults directory and in to their own [repo](https://github.com/meisnate12/PMM-Translations) which is managed at [translations.metamanager.wiki](https://translations.metamanager.wiki/projects/plex-meta-manager/defaults/).
|
||||||
|
Added `minimum_rating`, `fresh_rating`, and `maximum_rating` as template variable options to the [Ratings Overlays](https://metamanager.wiki/en/latest/defaults/overlays/ratings.html) to control which ratings get displayed.
|
||||||
|
Added the ability to update Overlay Defaults Positioning with just setting the alignment variables.
|
||||||
|
Added [Based On...](https://metamanager.wiki/en/latest/defaults/both/based.html) Collection Default.
|
||||||
|
Added Signature Style, DIIIVOY Style, and DIIVOY Color Style to [`actor`](https://metamanager.wiki/en/latest/defaults/both/actor.html), [`directors`](https://metamanager.wiki/en/latest/defaults/movie/director.html), [`producers`](https://metamanager.wiki/en/latest/defaults/movie/producer.html), and [`writers`](https://metamanager.wiki/en/latest/defaults/movie/writer.html).
|
||||||
|
Added new editions to the [editions Overlay File](https://metamanager.wiki/en/latest/defaults/overlays/resolution.html).
|
||||||
|
Added `delete_playlist` and `delete_playlist_<<key>>` as template variable options to the [Playlist Default](https://metamanager.wiki/en/latest/defaults/playlist.html).
|
||||||
|
Added `region` as a template variable options to the [`streaming` Overlay](https://metamanager.wiki/en/latest/defaults/overlays/streaming.html) and [`streaming` Collection](https://metamanager.wiki/en/latest/defaults/both/streaming.html) to allow these lists to show items in that region.
|
||||||
|
Added AppleTV to te [FlixPatrol Default](https://metamanager.wiki/en/latest/defaults/overlays/flixpatrol.html).
|
||||||
|
Added `radarr_search` and `sonarr_search` as template variable options to all Collection Defaults.
|
||||||
|
Updated `network` and `franchise` defaults
|
||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
Fixes #1187 Franchise Defaults no longer ignore collection_section and sort_title
|
Fixes Bug with `--time` that caused the times not to display correctly.
|
||||||
Fixed Italian Defaults Translation
|
Fixes `mal_search` search bug.
|
||||||
Fixed TMDb Modified Filters
|
Fixes #1277 corrects bug setting TMDb region.
|
||||||
Fixed ValueError from Anime IDs
|
Fixes a Bug where missing items items wouldn't be sent to radarr if no items were found in the library.
|
||||||
|
Fixes a Bug with template conditionals causing them to sometimes use the wrong result.
|
||||||
|
Fixes #1285 Wiki error.
|
||||||
|
Fixes a Bug with the `mass_poster_update` and `mass_background_update` Library Operations where they would sometimes throw a 406 Error.
|
||||||
|
Fixes a Bug with the `mass_poster_update` Library Operation where it would also update backgrounds in addition to posters.
|
||||||
|
Fixes multiple unnecessary items loads from plex.
|
||||||
|
Fixes a Bug with using year filters with no modifier.
|
||||||
|
Fixes a Bug where the `dimensional_asset_rename` Setting would rename title cards and season posters to show posters.
|
||||||
|
Fixes [`trakt_userlist` Builder](https://metamanager.wiki/en/latest/metadata/builders/trakt.html#trakt-userlist) where option `recommended` should have been `recommendations`.
|
||||||
|
Fixes overlay remove/reset operations.
|
||||||
|
Closes #1325 Fixes a Bug where `tmdb_vote_count` would be rejected as a filter.
|
||||||
|
Closes #1189 Fixes a Bug in the Resolution Default where the position would be completely off when changed
|
||||||
|
|
||||||
|
Various other Minor Fixes
|
|
@ -9,7 +9,7 @@
|
||||||
[![Discord](https://img.shields.io/discord/822460010649878528?color=%2300bc8c&label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
|
[![Discord](https://img.shields.io/discord/822460010649878528?color=%2300bc8c&label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
|
||||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/PlexMetaManager?color=%2300bc8c&label=r%2FPlexMetaManager&style=plastic)](https://www.reddit.com/r/PlexMetaManager/)
|
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/PlexMetaManager?color=%2300bc8c&label=r%2FPlexMetaManager&style=plastic)](https://www.reddit.com/r/PlexMetaManager/)
|
||||||
[![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?color=%2300bc8c&style=plastic)](https://metamanager.wiki)
|
[![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?color=%2300bc8c&style=plastic)](https://metamanager.wiki)
|
||||||
[![Translations](https://img.shields.io/weblate/progress/plex-meta-manager?color=00bc8c&server=https%3A%2F%2Ftranslations.metamanager.wiki&style=plastic)](https://translations.metamanager.wiki/engage/plex-meta-manager/)
|
[![Translations](https://img.shields.io/weblate/progress/plex-meta-manager?color=00bc8c&server=https%3A%2F%2Ftranslations.metamanager.wiki&style=plastic)](https://translations.metamanager.wiki/projects/plex-meta-manager/#languages)
|
||||||
[![GitHub Sponsors](https://img.shields.io/github/sponsors/meisnate12?color=%238a2be2&style=plastic)](https://github.com/sponsors/meisnate12)
|
[![GitHub Sponsors](https://img.shields.io/github/sponsors/meisnate12?color=%238a2be2&style=plastic)](https://github.com/sponsors/meisnate12)
|
||||||
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor%2FDonate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
|
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor%2FDonate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
|
||||||
[![Feature Requests](https://img.shields.io/badge/Feature%20Requests-blueviolet?style=plastic)](https://features.metamanager.wiki/)
|
[![Feature Requests](https://img.shields.io/badge/Feature%20Requests-blueviolet?style=plastic)](https://features.metamanager.wiki/)
|
||||||
|
|
2
VERSION
|
@ -1 +1 @@
|
||||||
1.18.3-develop166
|
1.18.3-develop167
|
||||||
|
|
|
@ -20,7 +20,7 @@ collections:
|
||||||
sort: Golden Globes !1
|
sort: Golden Globes !1
|
||||||
allowed_libraries: movie
|
allowed_libraries: movie
|
||||||
image: award/golden/best_picture_winner
|
image: award/golden/best_picture_winner
|
||||||
translation_key: golden_best
|
translation_key: golden_picture
|
||||||
- name: arr
|
- name: arr
|
||||||
- name: custom
|
- name: custom
|
||||||
collection_order: release.desc
|
collection_order: release.desc
|
||||||
|
|
|
@ -11,11 +11,11 @@ external_templates:
|
||||||
collection_section: "085"
|
collection_section: "085"
|
||||||
|
|
||||||
collections:
|
collections:
|
||||||
Based On... Collections:
|
Based on... Collections:
|
||||||
template:
|
template:
|
||||||
- name: separator
|
- name: separator
|
||||||
separator: based
|
separator: based
|
||||||
key_name: Based On...
|
key_name: Based on...
|
||||||
translation_key: separator
|
translation_key: separator
|
||||||
|
|
||||||
dynamic_collections:
|
dynamic_collections:
|
||||||
|
|
|
@ -189,10 +189,10 @@ templates:
|
||||||
conditions:
|
conditions:
|
||||||
- alt: hdr
|
- alt: hdr
|
||||||
value: true
|
value: true
|
||||||
hdr10plus:
|
plus:
|
||||||
conditions:
|
conditions:
|
||||||
- alt: hdr10plus
|
- alt: plus
|
||||||
value: '(?i)\bHDR10(\+|P(lus)?\b)'
|
value: '(?i)\bhdr10(\+|p(lus)?\b)'
|
||||||
optional:
|
optional:
|
||||||
- all
|
- all
|
||||||
- use_<<key>>
|
- use_<<key>>
|
||||||
|
@ -215,12 +215,12 @@ templates:
|
||||||
hdr: <<hdr>>
|
hdr: <<hdr>>
|
||||||
filters:
|
filters:
|
||||||
has_dolby_vision: <<dolby_vision>>
|
has_dolby_vision: <<dolby_vision>>
|
||||||
- filepath.regex: <<hdr10plus>>
|
filepath.regex: <<plus>>
|
||||||
|
|
||||||
overlays:
|
overlays:
|
||||||
|
|
||||||
4K-HDR10PLUS-Dovetail:
|
4K-HDR10PLUS-Dovetail:
|
||||||
variables: {key: 4k, alt: hdr10plus, weight: 160, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 4k, alt: plus, weight: 160, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
4K-DV-Dovetail:
|
4K-DV-Dovetail:
|
||||||
variables: {key: 4k, alt: dv, weight: 150, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 4k, alt: dv, weight: 150, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
|
@ -232,7 +232,7 @@ overlays:
|
||||||
variables: {key: 4k, alt: "", weight: 130, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 4k, alt: "", weight: 130, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
1080P-HDR10PLUS-Dovetail:
|
1080P-HDR10PLUS-Dovetail:
|
||||||
variables: {key: 1080p, alt: hdr10plus, weight: 125, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 1080p, alt: plus, weight: 125, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
1080P-DV-Dovetail:
|
1080P-DV-Dovetail:
|
||||||
variables: {key: 1080p, alt: dv, weight: 120, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 1080p, alt: dv, weight: 120, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
|
@ -244,7 +244,7 @@ overlays:
|
||||||
variables: {key: 1080p, alt: "", weight: 100, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 1080p, alt: "", weight: 100, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
720P-HDR10PLUS-Dovetail:
|
720P-HDR10PLUS-Dovetail:
|
||||||
variables: {key: 720p, alt: hdr10plus, weight: 95, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 720p, alt: plus, weight: 95, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
720P-DV-Dovetail:
|
720P-DV-Dovetail:
|
||||||
variables: {key: 720p, alt: dv, weight: 90, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 720p, alt: dv, weight: 90, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
|
@ -256,7 +256,7 @@ overlays:
|
||||||
variables: {key: 720p, alt: "", weight: 70, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 720p, alt: "", weight: 70, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
576P-HDR10PLUS-Dovetail:
|
576P-HDR10PLUS-Dovetail:
|
||||||
variables: {key: 576p, alt: hdr10plus, weight: 65, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 576p, alt: plus, weight: 65, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
576P-DV-Dovetail:
|
576P-DV-Dovetail:
|
||||||
variables: {key: 576p, alt: dv, weight: 60, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 576p, alt: dv, weight: 60, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
|
@ -268,7 +268,7 @@ overlays:
|
||||||
variables: {key: 576p, alt: "", weight: 40, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 576p, alt: "", weight: 40, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
480P-HDR10PLUS-Dovetail:
|
480P-HDR10PLUS-Dovetail:
|
||||||
variables: {key: 480p, alt: hdr10plus, weight: 35, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 480p, alt: plus, weight: 35, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
480P-DV-Dovetail:
|
480P-DV-Dovetail:
|
||||||
variables: {key: 480p, alt: dv, weight: 30, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 480p, alt: dv, weight: 30, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
|
@ -280,7 +280,7 @@ overlays:
|
||||||
variables: {key: 480p, alt: "", weight: 10, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: 480p, alt: "", weight: 10, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
HDR10PLUS-Dovetail:
|
HDR10PLUS-Dovetail:
|
||||||
variables: {key: "", alt: hdr10plus, weight: 7, type: resolution_dovetail, allowed_libraries: movie}
|
variables: {key: "", alt: plus, weight: 7, type: resolution_dovetail, allowed_libraries: movie}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
DV-Dovetail:
|
DV-Dovetail:
|
||||||
variables: {key: "", alt: dv, weight: 5, type: resolution_dovetail, all: true, allowed_libraries: movie}
|
variables: {key: "", alt: dv, weight: 5, type: resolution_dovetail, all: true, allowed_libraries: movie}
|
||||||
|
@ -360,7 +360,7 @@ overlays:
|
||||||
template: [name: edition, name: standard]
|
template: [name: edition, name: standard]
|
||||||
|
|
||||||
4K-HDR10PLUS:
|
4K-HDR10PLUS:
|
||||||
variables: {key: 4k, alt: hdr10plus, weight: 160, type: resolution}
|
variables: {key: 4k, alt: plus, weight: 160, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
4K-DV:
|
4K-DV:
|
||||||
variables: {key: 4k, alt: dv, weight: 150, type: resolution}
|
variables: {key: 4k, alt: dv, weight: 150, type: resolution}
|
||||||
|
@ -372,7 +372,7 @@ overlays:
|
||||||
variables: {key: 4k, alt: "", weight: 130, type: resolution}
|
variables: {key: 4k, alt: "", weight: 130, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
1080P-HDR10PLUS:
|
1080P-HDR10PLUS:
|
||||||
variables: {key: 1080p, alt: hdr10plus, weight: 125, type: resolution}
|
variables: {key: 1080p, alt: plus, weight: 125, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
1080P-DV:
|
1080P-DV:
|
||||||
variables: {key: 1080p, alt: dv, weight: 120, type: resolution}
|
variables: {key: 1080p, alt: dv, weight: 120, type: resolution}
|
||||||
|
@ -384,7 +384,7 @@ overlays:
|
||||||
variables: {key: 1080p, alt: "", weight: 100, type: resolution}
|
variables: {key: 1080p, alt: "", weight: 100, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
720P-HDR10PLUS:
|
720P-HDR10PLUS:
|
||||||
variables: {key: 720p, alt: hdr10plus, weight: 95, type: resolution}
|
variables: {key: 720p, alt: plus, weight: 95, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
720P-DV:
|
720P-DV:
|
||||||
variables: {key: 720p, alt: dv, weight: 90, type: resolution}
|
variables: {key: 720p, alt: dv, weight: 90, type: resolution}
|
||||||
|
@ -396,7 +396,7 @@ overlays:
|
||||||
variables: {key: 720p, alt: "", weight: 70, type: resolution}
|
variables: {key: 720p, alt: "", weight: 70, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
576P-HDR10PLUS:
|
576P-HDR10PLUS:
|
||||||
variables: {key: 576p, alt: hdr10plus, weight: 65, type: resolution}
|
variables: {key: 576p, alt: plus, weight: 65, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
576P-DV:
|
576P-DV:
|
||||||
variables: {key: 576p, alt: dv, weight: 60, type: resolution}
|
variables: {key: 576p, alt: dv, weight: 60, type: resolution}
|
||||||
|
@ -408,7 +408,7 @@ overlays:
|
||||||
variables: {key: 576p, alt: "", weight: 40, type: resolution}
|
variables: {key: 576p, alt: "", weight: 40, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
480P-HDR10PLUS:
|
480P-HDR10PLUS:
|
||||||
variables: {key: 480p, alt: hdr10plus, weight: 35, type: resolution}
|
variables: {key: 480p, alt: plus, weight: 35, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
480P-DV:
|
480P-DV:
|
||||||
variables: {key: 480p, alt: dv, weight: 30, type: resolution}
|
variables: {key: 480p, alt: dv, weight: 30, type: resolution}
|
||||||
|
@ -420,7 +420,7 @@ overlays:
|
||||||
variables: {key: 480p, alt: "", weight: 10, type: resolution}
|
variables: {key: 480p, alt: "", weight: 10, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
HDR10PLUS:
|
HDR10PLUS:
|
||||||
variables: {key: "", alt: hdr10plus, weight: 7, type: resolution}
|
variables: {key: "", alt: plus, weight: 7, type: resolution}
|
||||||
template: [name: resolution, name: standard]
|
template: [name: resolution, name: standard]
|
||||||
DV:
|
DV:
|
||||||
variables: {key: "", alt: dv, weight: 5, type: resolution, all: true}
|
variables: {key: "", alt: dv, weight: 5, type: resolution, all: true}
|
||||||
|
|
BIN
defaults/posters/backgrounds/bottom_fade.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
defaults/posters/backgrounds/center_fade.png
Normal file
After Width: | Height: | Size: 426 KiB |
BIN
defaults/posters/backgrounds/top_bottom_fade.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
defaults/posters/backgrounds/top_fade.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
defaults/posters/images/plex.png
Normal file
After Width: | Height: | Size: 20 KiB |
|
@ -177,6 +177,7 @@ html_theme_options = {
|
||||||
("Mass Genre Update", "config/operations", "#mass-genre-update"),
|
("Mass Genre Update", "config/operations", "#mass-genre-update"),
|
||||||
("Mass Content Rating Update", "config/operations", "#mass-content-rating-update"),
|
("Mass Content Rating Update", "config/operations", "#mass-content-rating-update"),
|
||||||
("Mass Original Title Update", "config/operations", "#mass-original-title-update"),
|
("Mass Original Title Update", "config/operations", "#mass-original-title-update"),
|
||||||
|
("Mass Studio Update", "config/operations", "#mass-studio-update"),
|
||||||
("Mass Originally Available Update", "config/operations", "#mass-originally-available-update"),
|
("Mass Originally Available Update", "config/operations", "#mass-originally-available-update"),
|
||||||
("Mass * Rating Update", "config/operations", "#mass--rating-update"),
|
("Mass * Rating Update", "config/operations", "#mass--rating-update"),
|
||||||
("Mass Episode * Rating Update", "config/operations", "#mass-episode--rating-update"),
|
("Mass Episode * Rating Update", "config/operations", "#mass-episode--rating-update"),
|
||||||
|
|
|
@ -42,7 +42,7 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
|
|
||||||
| Variable | Description & Values |
|
| Variable | Description & Values |
|
||||||
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diivoy`, or `diivoycolor` |
|
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diiivoy`, or `diiivoycolor` |
|
||||||
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
||||||
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
||||||
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
||||||
|
@ -66,7 +66,7 @@ libraries:
|
||||||
data:
|
data:
|
||||||
depth: 10
|
depth: 10
|
||||||
limit: 20
|
limit: 20
|
||||||
style: diivoy
|
style: diiivoy
|
||||||
sort_by: title.asc
|
sort_by: title.asc
|
||||||
use_separator: false
|
use_separator: false
|
||||||
sep_style: purple
|
sep_style: purple
|
||||||
|
|
|
@ -12,7 +12,7 @@ Supported Library Types: Movie, Show
|
||||||
|
|
||||||
| Collection | Key | Description |
|
| Collection | Key | Description |
|
||||||
|:---------------------------|:--------------|:----------------------------------------------------------------------------|
|
|:---------------------------|:--------------|:----------------------------------------------------------------------------|
|
||||||
| `Based On... Collections` | `separator` | [Separator Collection](../separators) to denote the Section of Collections. |
|
| `Based on... Collections` | `separator` | [Separator Collection](../separators) to denote the Section of Collections. |
|
||||||
| `Based on a Book` | `books` | Collection of Movies/Shows based on or inspired by books |
|
| `Based on a Book` | `books` | Collection of Movies/Shows based on or inspired by books |
|
||||||
| `Based on a Comic` | `comics` | Collection of Movies/Shows based on or inspired by comics |
|
| `Based on a Comic` | `comics` | Collection of Movies/Shows based on or inspired by comics |
|
||||||
| `Based on a True Story` | `true_story` | Collection of Movies/Shows based on or inspired by true stories |
|
| `Based on a True Story` | `true_story` | Collection of Movies/Shows based on or inspired by true stories |
|
||||||
|
@ -51,8 +51,8 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
| `sync_mode` | **Description:** Changes the Sync Mode for all collections in a Defaults file.<br>**Default:** `sync`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
| `sync_mode` | **Description:** Changes the Sync Mode for all collections in a Defaults file.<br>**Default:** `sync`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
||||||
| `sync_mode_<<key>>`<sup>1</sup> | **Description:** Changes the Sync Mode of the specified key's collection.<br>**Default:** `sync_mode`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
| `sync_mode_<<key>>`<sup>1</sup> | **Description:** Changes the Sync Mode of the specified key's collection.<br>**Default:** `sync_mode`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
||||||
| `exclude` | **Description:** Exclude these Media Outlets from creating a Dynamic Collection.<br>**Values:** List of Media Outlet Keys |
|
| `exclude` | **Description:** Exclude these Media Outlets from creating a Dynamic Collection.<br>**Values:** List of Media Outlet Keys |
|
||||||
| `based_name` | **Description:** Changes the title format of the Dynamic Collections.<br>**Default:** `Based on a <<key_name>>`<br>**Values:** Any string with `<<key_name>>` in it. |
|
| `name_format` | **Description:** Changes the title format of the Dynamic Collections.<br>**Default:** `Based on a <<key_name>>`<br>**Values:** Any string with `<<key_name>>` in it. |
|
||||||
| `based_summary` | **Description:** Changes the summary format of the Dynamic Collections.<br>**Default:** `<<library_translationU>>s based on or inspired by <<translated_key_name>>s.`<br>**Values:** Any string. |
|
| `summary_format` | **Description:** Changes the summary format of the Dynamic Collections.<br>**Default:** `<<library_translationU>>s based on or inspired by <<translated_key_name>>s.`<br>**Values:** Any string. |
|
||||||
|
|
||||||
1. Each default collection has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default collection has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
| `sync_mode` | **Description:** Changes the Sync Mode for all collections in a Defaults file.<br>**Default:** `sync`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
| `sync_mode` | **Description:** Changes the Sync Mode for all collections in a Defaults file.<br>**Default:** `sync`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
||||||
| `sync_mode_<<key>>`<sup>1</sup> | **Description:** Changes the Sync Mode of the specified key's collection.<br>**Default:** `sync_mode`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
| `sync_mode_<<key>>`<sup>1</sup> | **Description:** Changes the Sync Mode of the specified key's collection.<br>**Default:** `sync_mode`<br>**Values:**<table class="clearTable"><tr><td>`sync`</td><td>Add and Remove Items based on Builders</td></tr><tr><td>`append`</td><td>Only Add Items based on Builders</td></tr></table> |
|
||||||
| `exclude` | **Description:** Exclude these Streaming Services from creating a Dynamic Collection.<br>**Values:** List of Streaming Service Keys |
|
| `exclude` | **Description:** Exclude these Streaming Services from creating a Dynamic Collection.<br>**Values:** List of Streaming Service Keys |
|
||||||
| `region` | **Description:** Changes some Streaming Service lists to regional variants (see below table for more information.<br>**Default:** `us`<br>**Values:** `us,`uk`,`ca`, `da`, `de`, `es`, `fr`, `it`, `pt-br` |
|
| `region` | **Description:** Changes some Streaming Service lists to regional variants (see below table for more information.<br>**Default:** `us`<br>**Values:** `us`,`uk`,`ca`, `da`, `de`, `es`, `fr`, `it`, `pt-br` |
|
||||||
| `streaming_name` | **Description:** Changes the title format of the Dynamic Collections.<br>**Default:** `<<key_name>> <<library_translationU>>s`<br>**Values:** Any string with `<<key_name>>` in it. |
|
| `streaming_name` | **Description:** Changes the title format of the Dynamic Collections.<br>**Default:** `<<key_name>> <<library_translationU>>s`<br>**Values:** Any string with `<<key_name>>` in it. |
|
||||||
| `streaming_summary` | **Description:** Changes the summary format of the Dynamic Collections.<br>**Default:** `<<library_translationU>>s streaming on <<key_name>>.`<br>**Values:** Any string. |
|
| `streaming_summary` | **Description:** Changes the summary format of the Dynamic Collections.<br>**Default:** `<<library_translationU>>s streaming on <<key_name>>.`<br>**Values:** Any string. |
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ Below are the available variables which can be used to customize the file.
|
||||||
| `radarr_add_missing_<<key>>`<sup>1</sup> | **Description:** Override Radarr `add_missing` attribute of the specified key's collection.<br>**Default:** `radarr_add_missing`<br>**Values:** `true` or `false` |
|
| `radarr_add_missing_<<key>>`<sup>1</sup> | **Description:** Override Radarr `add_missing` attribute of the specified key's collection.<br>**Default:** `radarr_add_missing`<br>**Values:** `true` or `false` |
|
||||||
| `radarr_folder` | **Description:** Override Radarr `root_folder_path` attribute for all collections in a Defaults file.<br>**Values:** Folder Path |
|
| `radarr_folder` | **Description:** Override Radarr `root_folder_path` attribute for all collections in a Defaults file.<br>**Values:** Folder Path |
|
||||||
| `radarr_folder_<<key>>`<sup>1</sup> | **Description:** Override Radarr `root_folder_path` attribute of the specified key's collection.<br>**Default:** `radarr_folder`<br>**Values:** Folder Path |
|
| `radarr_folder_<<key>>`<sup>1</sup> | **Description:** Override Radarr `root_folder_path` attribute of the specified key's collection.<br>**Default:** `radarr_folder`<br>**Values:** Folder Path |
|
||||||
|
| `radarr_search` | **Description:** Override Radarr `search` attribute or all collections in a Defaults file.<br>**Values:** `true` or `false` |
|
||||||
|
| `radarr_search_<<key>>`<sup>1</sup> | **Description:** Override Radarr `search` attribute of the specified key's collection.<br>**Default:** `radarr_search`<br>**Values:** `true` or `false` |
|
||||||
| `radarr_tag` | **Description:** Override Radarr `tag` attribute for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
| `radarr_tag` | **Description:** Override Radarr `tag` attribute for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
||||||
| `radarr_tag_<<key>>`<sup>1</sup> | **Description:** Override Radarr `tag` attribute of the specified key's collection.<br>**Default:** `radarr_tag`<br>**Values:** List or comma-separated string of tags |
|
| `radarr_tag_<<key>>`<sup>1</sup> | **Description:** Override Radarr `tag` attribute of the specified key's collection.<br>**Default:** `radarr_tag`<br>**Values:** List or comma-separated string of tags |
|
||||||
| `item_radarr_tag` | **Description:** Used to append a tag in Radarr for every movie found by the builders that's in Radarr for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
| `item_radarr_tag` | **Description:** Used to append a tag in Radarr for every movie found by the builders that's in Radarr for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
||||||
|
@ -38,6 +40,8 @@ Below are the available variables which can be used to customize the file.
|
||||||
| `sonarr_add_missing_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `add_missing` attribute of the specified key's collection.<br>**Default:** `sonarr_add_missing`<br>**Values:** `true` or `false` |
|
| `sonarr_add_missing_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `add_missing` attribute of the specified key's collection.<br>**Default:** `sonarr_add_missing`<br>**Values:** `true` or `false` |
|
||||||
| `sonarr_folder` | **Description:** Override Sonarr `root_folder_path` attribute for all collections in a Defaults file.<br>**Values:** Folder Path |
|
| `sonarr_folder` | **Description:** Override Sonarr `root_folder_path` attribute for all collections in a Defaults file.<br>**Values:** Folder Path |
|
||||||
| `sonarr_folder_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `root_folder_path` attribute of the specified key's collection.<br>**Default:** `sonarr_folder`<br>**Values:** Folder Path |
|
| `sonarr_folder_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `root_folder_path` attribute of the specified key's collection.<br>**Default:** `sonarr_folder`<br>**Values:** Folder Path |
|
||||||
|
| `sonarr_search` | **Description:** Override Sonarr `search` attribute or all collections in a Defaults file.<br>**Values:** `true` or `false` |
|
||||||
|
| `sonarr_search_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `search` attribute of the specified key's collection.<br>**Default:** `sonarr_search`<br>**Values:** `true` or `false` |
|
||||||
| `sonarr_tag` | **Description:** Override Sonarr `tag` attribute for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
| `sonarr_tag` | **Description:** Override Sonarr `tag` attribute for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
||||||
| `sonarr_tag_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `tag` attribute of the specified key's collection.<br>**Default:** `sonarr_tag`<br>**Values:** List or comma-separated string of tags |
|
| `sonarr_tag_<<key>>`<sup>1</sup> | **Description:** Override Sonarr `tag` attribute of the specified key's collection.<br>**Default:** `sonarr_tag`<br>**Values:** List or comma-separated string of tags |
|
||||||
| `item_sonarr_tag` | **Description:** Used to append a tag in Sonarr for every series found by the builders that's in Sonarr for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
| `item_sonarr_tag` | **Description:** Used to append a tag in Sonarr for every series found by the builders that's in Sonarr for all collections in a Defaults file.<br>**Values:** List or comma-separated string of tags |
|
||||||
|
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |
|
@ -39,7 +39,7 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
|
|
||||||
| Variable | Description & Values |
|
| Variable | Description & Values |
|
||||||
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diivoy`, or `diivoycolor` |
|
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diiivoy`, or `diiivoycolor` |
|
||||||
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
||||||
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
||||||
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
||||||
|
|
|
@ -39,7 +39,7 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
|
|
||||||
| Variable | Description & Values |
|
| Variable | Description & Values |
|
||||||
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diivoy`, or `diivoycolor` |
|
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diiivoy`, or `diiivoycolor` |
|
||||||
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
||||||
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
||||||
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
||||||
|
@ -60,7 +60,7 @@ libraries:
|
||||||
metadata_path:
|
metadata_path:
|
||||||
- pmm: producer
|
- pmm: producer
|
||||||
template_variables:
|
template_variables:
|
||||||
style: diivoycolor
|
style: diiivoycolor
|
||||||
use_separator: false
|
use_separator: false
|
||||||
sep_style: purple
|
sep_style: purple
|
||||||
data:
|
data:
|
||||||
|
|
|
@ -39,7 +39,7 @@ This file contains a [Separator](../separators) so all [Shared Separator Variabl
|
||||||
|
|
||||||
| Variable | Description & Values |
|
| Variable | Description & Values |
|
||||||
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diivoy`, or `diivoycolor` |
|
| `style` | **Description:** Controls the visual theme of the collections created.<br>**Default:** `bw`<br>**Values:** `bw`, `rainier`, `signature`, `diiivoy`, or `diiivoycolor` |
|
||||||
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
| `limit` | **Description:** Changes the Builder Limit for all collections in a Defaults file.<br>**Values:** Number Greater then 0 |
|
||||||
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
| `limit_<<key>>`<sup>1</sup> | **Description:** Changes the Builder Limit of the specified key's collection.<br>**Default:** `limit`<br>**Values:** Number Greater then 0 |
|
||||||
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
| `sort_by` | **Description:** Changes the Smart Filter Sort for all collections in a Defaults file.<br>**Default:** `release.desc`<br>**Values:** [Any `smart_filter` Sort Option](../../metadata/builders/smart.md#sort-options) |
|
||||||
|
|
|
@ -6,27 +6,28 @@ Note that the `template_variables:` section only needs to be used if you do want
|
||||||
|
|
||||||
Below are the available variables which can be used to customize the file.
|
Below are the available variables which can be used to customize the file.
|
||||||
|
|
||||||
**NOTE:** `file`, `url`, `git`, `repo`, and `pmm` can all be used with `_<<key>>`
|
| Variable | Description & Values |
|
||||||
<br>For example, with the audio_codec overlay `file_dtsx: config/overlays/dtsx.png`
|
|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `use_<<key>>`<sup>1</sup> | **Description:** Turns off individual Overlays in a Defaults file.<br>**Values:** `false` to turn off the overlay |
|
||||||
| Variable | Description & Values |
|
| `file` | **Description:** Controls the images associated with all the Overlays to a local file.<br>**Values:** Filepath to Overlay Image |
|
||||||
|:--------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
| `file_<<key>>`<sup>1</sup> | **Description:** Controls the image associated with this key's Overlay to a local file.<br>**Values:** Filepath to Overlay Image |
|
||||||
| `use_<<key>>`<sup>1</sup> | **Description:** Turns off individual Overlays in a Defaults file.<br>**Values:** `false` to turn off the overlay |
|
| `url` | **Description:** Controls the images associated with all the Overlays to a url.<br>**Values:** URL to Overlay Image |
|
||||||
| `file` | **Description:** Controls the image associated with the Overlay to a local file.<br>**Values:** Filepath to Overlay Image |
|
| `url_<<key>>`<sup>1</sup> | **Description:** Controls the image associated with this key's Overlay to a url.<br>**Values:** URL to Overlay Image |
|
||||||
| `url` | **Description:** Controls the image associated with the Overlay to a url.<br>**Values:** URL to Overlay Image |
|
| `git` | **Description:** Controls the images associated with all the Overlays to the git repo.<br>**Values:** Git Path to Overlay Image |
|
||||||
| `git` | **Description:** Controls the image associated with the Overlay to the git repo.<br>**Values:** Git Path to Overlay Image |
|
| `git_<<key>>`<sup>1</sup> | **Description:** Controls the image associated with this key's Overlay to the git repo.<br>**Values:** Git Path to Overlay Image |
|
||||||
| `repo` | **Description:** Controls the image associated with the Overlay to a custom repo.<br>**Values:** Repo Path to Overlay Image |
|
| `repo` | **Description:** Controls the images associated with all the Overlays to a custom repo.<br>**Values:** Repo Path to Overlay Image |
|
||||||
| `horizontal_offset` | **Description:** Controls the Horizontal Offset of this overlay. Can be a %.<br>**Values:** Number 0 or greater or 0%-100% |
|
| `repo_<<key>>`<sup>1</sup> | **Description:** Controls the image associated with this key's Overlay to a custom repo.<br>**Values:** Repo Path to Overlay Image |
|
||||||
| `horizontal_align` | **Description:** Controls the Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, or `right` |
|
| `horizontal_offset` | **Description:** Controls the Horizontal Offset of this overlay. Can be a %.<br>**Values:** Number 0 or greater or 0%-100% |
|
||||||
| `vertical_offset` | **Description:** Controls the Vertical Offset of this overlay. Can be a %.<br>**Values:** Number 0 or greater or 0%-100% |
|
| `horizontal_align` | **Description:** Controls the Horizontal Alignment of the overlay.<br>**Values:** `left`, `center`, or `right` |
|
||||||
| `vertical_align` | **Description:** Controls the Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, or `bottom` |
|
| `vertical_offset` | **Description:** Controls the Vertical Offset of this overlay. Can be a %.<br>**Values:** Number 0 or greater or 0%-100% |
|
||||||
| `back_color` | **Description:** Controls the Backdrop Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
| `vertical_align` | **Description:** Controls the Vertical Alignment of the overlay.<br>**Values:** `top`, `center`, or `bottom` |
|
||||||
| `back_width` | **Description:** Controls the Backdrop Width for the Text Overlay. If `back_width` is not specified the Backdrop Sizes to the text<br>**Values:** Any Number greater then 0 |
|
| `back_color` | **Description:** Controls the Backdrop Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
||||||
| `back_height` | **Description:** Controls the Backdrop Height for the Text Overlay. If `back_height` is not specified the Backdrop Sizes to the text<br>**Values:** Any Number greater then 0 |
|
| `back_width` | **Description:** Controls the Backdrop Width for the Text Overlay. If `back_width` is not specified the Backdrop Sizes to the text<br>**Values:** Any Number greater then 0 |
|
||||||
| `back_align` | **Description:** Controls the Alignment for the Text Overlay inside the backdrop. If `back_align` is not specified the Backdrop Centers the text.<br>**Values:** `left`, `right`, `center`, `top`, or `bottom` |
|
| `back_height` | **Description:** Controls the Backdrop Height for the Text Overlay. If `back_height` is not specified the Backdrop Sizes to the text<br>**Values:** Any Number greater then 0 |
|
||||||
| `back_padding` | **Description:** Controls the Backdrop Padding for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
| `back_align` | **Description:** Controls the Alignment for the Text Overlay inside the backdrop. If `back_align` is not specified the Backdrop Centers the text.<br>**Values:** `left`, `right`, `center`, `top`, or `bottom` |
|
||||||
| `back_radius` | **Description:** Controls the Backdrop Radius for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
| `back_padding` | **Description:** Controls the Backdrop Padding for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
||||||
| `back_line_color` | **Description:** Controls the Backdrop Line Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
| `back_radius` | **Description:** Controls the Backdrop Radius for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
||||||
| `back_line_width` | **Description:** Controls the Backdrop Line Width for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
| `back_line_color` | **Description:** Controls the Backdrop Line Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
||||||
|
| `back_line_width` | **Description:** Controls the Backdrop Line Width for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
||||||
|
|
|
@ -83,7 +83,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
| `regex_<<key>>`<sup>1</sup> | **Description:** Controls the regex of the Overlay Search.<br>**Values:** Any Proper Regex |
|
| `regex_<<key>>`<sup>1</sup> | **Description:** Controls the regex of the Overlay Search.<br>**Values:** Any Proper Regex |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ All [Shared Overlay Variables](../overlay_variables) except `horizontal_offset`,
|
||||||
| `addon_offset` | **Description:** Text Addon Image Offset from the text.<br>**Default:** `30`<br>**Values:** Any Number greater then 0 |
|
| `addon_offset` | **Description:** Text Addon Image Offset from the text.<br>**Default:** `30`<br>**Values:** Any Number greater then 0 |
|
||||||
| `addon_position` | **Description:** Text Addon Image Alignment in relation to the text.<br>**Default:** `top`<br>**Values:** `left`, `right`, `top`, `bottom` |
|
| `addon_position` | **Description:** Text Addon Image Alignment in relation to the text.<br>**Default:** `top`<br>**Values:** `left`, `right`, `top`, `bottom` |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `overlay_level` | **Description:** Choose the Overlay Level.<br>**Values:** `season` or `episode` |
|
| `overlay_level` | **Description:** Choose the Overlay Level.<br>**Values:** `season` or `episode` |
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `country_<<key>>`<sup>1</sup> | **Description:** Controls the country image for the Overlay.<br>**Default:** Listed in the [Table](#supported-audiosubtitle-language-flags) above<br>**Values:** [ISO 3166-1 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) for the flag desired |
|
| `country_<<key>>`<sup>1</sup> | **Description:** Controls the country image for the Overlay.<br>**Default:** Listed in the [Table](#supported-audiosubtitle-language-flags) above<br>**Values:** [ISO 3166-1 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) for the flag desired |
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,31 @@ Recommendations: Editions overlay is designed to use the Editions field within P
|
||||||
|
|
||||||
## Supported Resolutions
|
## Supported Resolutions
|
||||||
|
|
||||||
| Resolution | Key |
|
| Resolution | Key | Weight |
|
||||||
|:---------------|:----------|
|
|:-------------|:-------------|:-------|
|
||||||
| 4K | `4k` |
|
| 4K HDR10+ | `4k_plus` | `160` |
|
||||||
| 1080P | `1080p` |
|
| 4K DV | `4k_dv` | `150` |
|
||||||
| 720P | `720p` |
|
| 4K HDR | `4k_hdr` | `140` |
|
||||||
| 576P | `576p` |
|
| 4K | `4k` | `130` |
|
||||||
| 480P | `480p` |
|
| 1080P HDR10+ | `1080p_plus` | `125` |
|
||||||
| DV | `dv` |
|
| 1080P DV | `1080p_dv` | `120` |
|
||||||
| HDR | `hdr` |
|
| 1080P HDR | `1080p_hdr` | `110` |
|
||||||
|
| 1080P | `1080p` | `100` |
|
||||||
|
| 720P HDR10+ | `720p_plus` | `95` |
|
||||||
|
| 720P DV | `720p_dv` | `90` |
|
||||||
|
| 720P HDR | `720p_hdr` | `80` |
|
||||||
|
| 720P | `720p` | `70` |
|
||||||
|
| 576P HDR10+ | `576p_plus` | `65` |
|
||||||
|
| 576P DV | `576p_dv` | `60` |
|
||||||
|
| 576P HDR | `576p_hdr` | `50` |
|
||||||
|
| 576P | `576p` | `40` |
|
||||||
|
| 480P HDR10+ | `480p_plus` | `35` |
|
||||||
|
| 480P DV | `480p_dv` | `30` |
|
||||||
|
| 480P HDR | `480p_hdr` | `20` |
|
||||||
|
| 480P | `480p` | `10` |
|
||||||
|
| HDR10+ | `plus` | `7` |
|
||||||
|
| DV | `dv` | `5` |
|
||||||
|
| HDR | `hdr` | `1` |
|
||||||
|
|
||||||
## Supported Editions
|
## Supported Editions
|
||||||
|
|
||||||
|
@ -96,7 +112,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `overlay_level` | **Description:** Choose the Overlay Level.<br>**Values:** `season` or `episode` |
|
| `overlay_level` | **Description:** Choose the Overlay Level.<br>**Values:** `season` or `episode` |
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority. **Only works with Edition keys.**<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority. **Only works with Edition keys.**<br>**Values:** Any Number |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
|:-------------------------------|:-------------------------------------------------------------------------------------------------------------|
|
|:-------------------------------|:-------------------------------------------------------------------------------------------------------------|
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `stroke_width` | **Description:** Font Stroke Width for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
| `stroke_width` | **Description:** Font Stroke Width for the Text Overlay.<br>**Values:** Any Number greater then 0 |
|
||||||
| `stroke_color` | **Description:** Font Stroke Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
| `stroke_color` | **Description:** Font Stroke Color for the Text Overlay.<br>**Values:** Color Hex Code in format `#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA` |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -70,18 +70,18 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
|:-----------------------------|:-------------------------------------------------------------------------------------------------------------|
|
|:-----------------------------|:-------------------------------------------------------------------------------------------------------------|
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
## Regional Variants
|
## Regional Variants
|
||||||
|
|
||||||
Some logic is applied to allow for regional streaming service lists to be available to users depending on where they are, as detailed below:
|
Some logic is applied to allow for regional streaming service lists to be available to users depending on where they are, as detailed below:
|
||||||
|
|
||||||
| Region | Key | Description |
|
| Region | Key | Description |
|
||||||
|:-----------------|:---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
|
|:-----------------|:---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| any besides `us` | `amazon`, `disney`, `netflix` | These collections will use regional variant lists to ensure the lists populate with what is available in the region specified |
|
| any besides `us` | `amazon`, `disney`, `netflix` | These overlays will use regional variant lists to ensure the overlays are applied to what is available in the region specified |
|
||||||
| any besides `uk` | `all4`, `britbox`, `hayu`, `now` | These collections will not be created if the region is not `uk` as these streaming services are UK-focused |
|
| any besides `uk` | `all4`, `britbox`, `hayu`, `now` | These overlays will not be used if the region is not `uk` as these streaming services are UK-focused |
|
||||||
| any besides `ca` | `crave` | These collections will not be created if the region is not `ca` as these streaming services are Canada-focused |
|
| any besides `ca` | `crave` | These overlays will not be used if the region is not `ca` as these streaming services are Canada-focused |
|
||||||
| `ca` | `hbomax`, `showtime` | These collections will not be created if the region is `ca` as these streaming services are part of the Crave streaming service in Canada |
|
| `ca` | `hbomax`, `showtime` | These overlays will not be used if the region is `ca` as these streaming services are part of the Crave streaming service in Canada |
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ All [Shared Overlay Variables](../overlay_variables) are available with the defa
|
||||||
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
| `weight_<<key>>`<sup>1</sup> | **Description:** Controls the weight of the Overlay. Higher numbers have priority.<br>**Values:** Any Number |
|
||||||
| `regex_<<key>>`<sup>1</sup> | **Description:** Controls the regex of the Overlay Search.<br>**Values:** Any Proper Regex |
|
| `regex_<<key>>`<sup>1</sup> | **Description:** Controls the regex of the Overlay Search.<br>**Values:** Any Proper Regex |
|
||||||
|
|
||||||
1. Each default overlay has a `key` that when calling to effect a specific collection you must replace `<<key>>` with when calling.
|
1. Each default overlay has a `key` that when calling to effect a specific overlay you must replace `<<key>>` with when calling.
|
||||||
|
|
||||||
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
The below is an example config.yml extract with some Template Variables added in to change how the file works.
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,10 @@ This Default can use the `style` template variable to easily change the posters
|
||||||
|
|
||||||
![](../images/person_signature.png)
|
![](../images/person_signature.png)
|
||||||
|
|
||||||
### Diivoy Style
|
### Diiivoy Style
|
||||||
|
|
||||||
![](../images/person_diivoy.png)
|
![](../images/person_diiivoy.png)
|
||||||
|
|
||||||
### Diivoy Color Style
|
### Diiivoy Color Style
|
||||||
|
|
||||||
![](../images/person_diivoycolor.png)
|
![](../images/person_diiivoycolor.png)
|
||||||
|
|
|
@ -48,6 +48,8 @@ Note that the `template_variables:` section only needs to be used if you do want
|
||||||
| `exclude_user` | **Description:** Sets the users to exclude from sync for all playlists.<br>**Default:** `playlist_sync_to_user` Global Setting Value<br>**Values:** Comma-separated string or list of user names. |
|
| `exclude_user` | **Description:** Sets the users to exclude from sync for all playlists.<br>**Default:** `playlist_sync_to_user` Global Setting Value<br>**Values:** Comma-separated string or list of user names. |
|
||||||
| `exclude_user_<<key>>`<sup>1</sup> | **Description:** Sets the users to exclude from sync the specified key's playlist.<br>**Default:** `sync_to_user` Value<br>**Values:** Comma-separated string or list of user names. |
|
| `exclude_user_<<key>>`<sup>1</sup> | **Description:** Sets the users to exclude from sync the specified key's playlist.<br>**Default:** `sync_to_user` Value<br>**Values:** Comma-separated string or list of user names. |
|
||||||
| `trakt_list_<<key>>`<sup>1</sup> | **Description:** Adds the Movies in the Trakt List to the specified key's playlist. Overrides the [default trakt_list](#default-trakt_list) for that playlist if used.<br>**Values:** List of Trakt List URLs | | | |
|
| `trakt_list_<<key>>`<sup>1</sup> | **Description:** Adds the Movies in the Trakt List to the specified key's playlist. Overrides the [default trakt_list](#default-trakt_list) for that playlist if used.<br>**Values:** List of Trakt List URLs | | | |
|
||||||
|
| `delete_playlist` | **Description:** Will delete all playlists for the users defined by sync_to_users.<br>**Values:** `true` or `false` |
|
||||||
|
| `delete_playlist_<<key>>`<sup>1</sup> | **Description:** Will delete the specified key's playlists for the users defined by sync_to_users.<br>**Values:** `true` or `false` |
|
||||||
| `ignore_ids` | **Description:** Set a list or comma-separated string of TMDb/TVDb IDs to ignore in all playlists.<br>**Values:** List or comma-separated string of TMDb/TVDb IDs |
|
| `ignore_ids` | **Description:** Set a list or comma-separated string of TMDb/TVDb IDs to ignore in all playlists.<br>**Values:** List or comma-separated string of TMDb/TVDb IDs |
|
||||||
| `ignore_imdb_ids` | **Description:** Set a list or comma-separated string of IMDb IDs to ignore in all playlists.<br>**Values:** List or comma-separated string of IMDb IDs |
|
| `ignore_imdb_ids` | **Description:** Set a list or comma-separated string of IMDb IDs to ignore in all playlists.<br>**Values:** List or comma-separated string of IMDb IDs |
|
||||||
| `url_poster_<<key>>`<sup>1</sup> | **Description:** Changes the poster url of the specified key's playlist.<br>**Values:** URL directly to the Image |
|
| `url_poster_<<key>>`<sup>1</sup> | **Description:** Changes the poster url of the specified key's playlist.<br>**Values:** URL directly to the Image |
|
||||||
|
|
|
@ -280,7 +280,7 @@ Run with every network request printed to the Logs. **This can potentially have
|
||||||
<tr>
|
<tr>
|
||||||
<th>Example</th>
|
<th>Example</th>
|
||||||
<td><code>--log-requests</code></td>
|
<td><code>--log-requests</code></td>
|
||||||
<td><code>PMM_NETWORK=true</code></td>
|
<td><code>PMM_LOG_REQUESTS=true</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
[![Discord](https://img.shields.io/discord/822460010649878528?color=%2300bc8c&label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
|
[![Discord](https://img.shields.io/discord/822460010649878528?color=%2300bc8c&label=Discord&style=plastic)](https://discord.gg/NfH6mGFuAB)
|
||||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/PlexMetaManager?color=%2300bc8c&label=r%2FPlexMetaManager&style=plastic)](https://www.reddit.com/r/PlexMetaManager/)
|
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/PlexMetaManager?color=%2300bc8c&label=r%2FPlexMetaManager&style=plastic)](https://www.reddit.com/r/PlexMetaManager/)
|
||||||
[![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?color=%2300bc8c&style=plastic)](https://metamanager.wiki)
|
[![Wiki](https://img.shields.io/readthedocs/plex-meta-manager?color=%2300bc8c&style=plastic)](https://metamanager.wiki)
|
||||||
[![Translations](https://img.shields.io/weblate/progress/plex-meta-manager?color=00bc8c&server=https%3A%2F%2Ftranslations.metamanager.wiki&style=plastic)](https://translations.metamanager.wiki/engage/plex-meta-manager/)
|
[![Translations](https://img.shields.io/weblate/progress/plex-meta-manager?color=00bc8c&server=https%3A%2F%2Ftranslations.metamanager.wiki&style=plastic)](https://translations.metamanager.wiki/projects/plex-meta-manager/#languages)
|
||||||
[![GitHub Sponsors](https://img.shields.io/github/sponsors/meisnate12?color=%238a2be2&style=plastic)](https://github.com/sponsors/meisnate12)
|
[![GitHub Sponsors](https://img.shields.io/github/sponsors/meisnate12?color=%238a2be2&style=plastic)](https://github.com/sponsors/meisnate12)
|
||||||
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor%2FDonate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
|
[![Sponsor or Donate](https://img.shields.io/badge/-Sponsor%2FDonate-blueviolet?style=plastic)](https://github.com/sponsors/meisnate12)
|
||||||
[![Feature Requests](https://img.shields.io/badge/Feature%20Requests-blueviolet?style=plastic)](https://features.metamanager.wiki/)
|
[![Feature Requests](https://img.shields.io/badge/Feature%20Requests-blueviolet?style=plastic)](https://features.metamanager.wiki/)
|
||||||
|
|
|
@ -103,16 +103,17 @@ The available attributes for editing movies are as follows
|
||||||
|
|
||||||
### Special Attributes
|
### Special Attributes
|
||||||
|
|
||||||
| Attribute | Allowed Values |
|
| Attribute | Allowed Values |
|
||||||
|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `title` | Title if different from the mapping value useful when you have multiple movies with the same name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `title` | Title if different from the mapping value useful when you have multiple movies with the same name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `alt_title` | Alternative title to look for and then change to the mapping name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `alt_title` | Alternative title to look for and then change to the mapping name. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `year` | Year of movie for better identification. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `year` | Year of movie for better identification. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `blank_edition`<sup>1</sup> | Movie has no Edition. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `blank_edition`<sup>1</sup> | Movie has no Edition. See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `edition_filter`<sup>1</sup> | Edition of movie for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `edition_filter`<sup>1</sup> | Edition of movie for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `edition_contains`<sup>1</sup> | Edition of movie must contain the given string for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
| `edition_contains`<sup>1</sup> | Edition of movie must contain the given string for better identification. Can be a list (only one needs to match). See the [Metadata Page](../metadata.md#metadata-attributes) for how searching for files works. |
|
||||||
| `tmdb_show` | TMDb Show ID to use for metadata useful for miniseries that have been compiled into a movie. **This is not used to say this show is the given ID.** |
|
| `tmdb_show` | TMDb Show ID to use for metadata useful for miniseries that have been compiled into a movie. **This is not used to say this show is the given ID.** |
|
||||||
| `tmdb_movie` | TMDb Movie ID to use for metadata useful for movies that have been split into segments **This is not used to say this show is the given ID.** |
|
| `tmdb_movie` | TMDb Movie ID to use for metadata useful for movies that have been split into segments **This is not used to say this show is the given ID.** |
|
||||||
|
| `run_definition` | Used to specify if this definition runs.<br>Multiple can be used for one definition as a list or comma separated string. One `false` or unmatched library type will cause it to fail.<br>**Values:** `movie`, `show`, `artist`, `true`, `false` |
|
||||||
|
|
||||||
1. If the server does not have a Plex Pass then the Edition Field is not accessible. In this case PMM will check the movies filepath for `{edition-MOVIES EDITION}` to determine what the edition is.
|
1. If the server does not have a Plex Pass then the Edition Field is not accessible. In this case PMM will check the movies filepath for `{edition-MOVIES EDITION}` to determine what the edition is.
|
||||||
|
|
||||||
|
|
|
@ -67,18 +67,19 @@ The available attributes for editing artists, albums, and tracks are as follows
|
||||||
``
|
``
|
||||||
### General Attributes
|
### General Attributes
|
||||||
|
|
||||||
| Attribute | Values | Artists | Album | Tracks |
|
| Attribute | Values | Artists | Album | Tracks |
|
||||||
|:-----------------------|:--------------------------------------------------------------|:--------:|:--------:|:--------:|
|
|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:--------:|:--------:|
|
||||||
| `title` | Text to change Title | ❌ | ❌ | ✅ |
|
| `title` | Text to change Title | ❌ | ❌ | ✅ |
|
||||||
| `sort_title` | Text to change Sort Title | ✅ | ✅ | ✅ |
|
| `sort_title` | Text to change Sort Title | ✅ | ✅ | ✅ |
|
||||||
| `user_rating` | Number to change User Rating | ✅ | ✅ | ✅ |
|
| `user_rating` | Number to change User Rating | ✅ | ✅ | ✅ |
|
||||||
| `critic_rating` | Number to change Critic Rating | ❌ | ✅ | ❌ |
|
| `critic_rating` | Number to change Critic Rating | ❌ | ✅ | ❌ |
|
||||||
| `originally_available` | Date to change Originally Available<br>**Format:** YYYY-MM-DD | ❌ | ✅ | ❌ |
|
| `originally_available` | Date to change Originally Available<br>**Format:** YYYY-MM-DD | ❌ | ✅ | ❌ |
|
||||||
| `record_label` | Text to change Record Label | ❌ | ✅ | ❌ |
|
| `record_label` | Text to change Record Label | ❌ | ✅ | ❌ |
|
||||||
| `summary` | Text to change Summary | ✅ | ✅ | ✅ |
|
| `summary` | Text to change Summary | ✅ | ✅ | ✅ |
|
||||||
| `track` | Text to change Track | ❌ | ❌ | ✅ |
|
| `track` | Text to change Track | ❌ | ❌ | ✅ |
|
||||||
| `disc` | Text to change Disc | ❌ | ❌ | ✅ |
|
| `disc` | Text to change Disc | ❌ | ❌ | ✅ |
|
||||||
| `original_artist` | Text to change Original Artist | ❌ | ❌ | ✅ |
|
| `original_artist` | Text to change Original Artist | ❌ | ❌ | ✅ |
|
||||||
|
| `run_definition` | Used to specify if this definition runs.<br>Multiple can be used for one definition as a list or comma separated string. One `false` or unmatched library type will cause it to fail.<br>**Values:** `movie`, `show`, `artist`, `true`, `false` | ✅ | ❌ | ❌ |
|
||||||
|
|
||||||
### Tag Attributes
|
### Tag Attributes
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ The mapping name is the season number (use 0 for specials) or the season name.
|
||||||
|
|
||||||
To edit the metadata of a particular Episode in a Season use the `episodes` attribute on its season.
|
To edit the metadata of a particular Episode in a Season use the `episodes` attribute on its season.
|
||||||
|
|
||||||
The mapping name is the episode number in that season or the title of the episode.
|
The mapping name is the episode number in that season, the title of the episode, or the Originally Available date in the format `MM/DD`.
|
||||||
|
|
||||||
## Metadata Edits
|
## Metadata Edits
|
||||||
|
|
||||||
|
|
|
@ -200,24 +200,24 @@ You can use the item's metadata to determine the text by adding Special Text Var
|
||||||
|
|
||||||
There are multiple Special Text Variables that can be used when formatting the text. The variables are defined like so `<<name>>` and some can have modifiers like so `<<name$>>` where `$` is the modifier. The available options are:
|
There are multiple Special Text Variables that can be used when formatting the text. The variables are defined like so `<<name>>` and some can have modifiers like so `<<name$>>` where `$` is the modifier. The available options are:
|
||||||
|
|
||||||
| Special Text Variables & Mods | Movies | Shows | Seasons | Episodes |
|
| Special Text Variables & Mods | Movies | Shows | Seasons | Episodes |
|
||||||
|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:--------:|:--------:|:--------:|
|
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:--------:|:--------:|:--------:|
|
||||||
| `<<audience_rating>>`: audience rating (`8.7`, `9.0`)<br>`<<audience_rating%>>`: audience rating out of 100 (`87`, `90`)<br>`<<audience_rating#>>`: audience rating removing `.0` as needed (`8.7`, `9`)<br>`<<audience_rating/>>`: audience rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ❌ | ✅ |
|
| `<<audience_rating>>`: audience rating (`8.7`, `9.0`)<br>`<<audience_rating%>>`: audience rating out of 100 (`87`, `90`)<br>`<<audience_rating#>>`: audience rating removing `.0` as needed (`8.7`, `9`)<br>`<<audience_rating/>>`: audience rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ❌ | ✅ |
|
||||||
| `<<critic_rating>>`: critic rating (`8.7`, `9.0`)<br>`<<critic_rating%>>`: critic rating out of 100 (`87`, `90`)<br>`<<critic_rating#>>`: critic rating removing `.0` as needed (`8.7`, `9`)<br>`<<critic_rating/>>`: critic rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ❌ | ✅ |
|
| `<<critic_rating>>`: critic rating (`8.7`, `9.0`)<br>`<<critic_rating%>>`: critic rating out of 100 (`87`, `90`)<br>`<<critic_rating#>>`: critic rating removing `.0` as needed (`8.7`, `9`)<br>`<<critic_rating/>>`: critic rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ❌ | ✅ |
|
||||||
| `<<user_rating>>`: user rating (`8.7`, `9.0`)<br>`<<user_rating%>>`: user rating out of 100 (`87`, `90`)<br>`<<user_rating#>>`: user rating removing `.0` as needed (`8.7`, `9`)<br>`<<user_rating/>>`: user rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ✅ | ✅ |
|
| `<<user_rating>>`: user rating (`8.7`, `9.0`)<br>`<<user_rating%>>`: user rating out of 100 (`87`, `90`)<br>`<<user_rating#>>`: user rating removing `.0` as needed (`8.7`, `9`)<br>`<<user_rating/>>`: user rating on a 5 point scale (`8.6` shows as `4.3`) | ✅ | ✅ | ✅ | ✅ |
|
||||||
| `<<title>>`: Title of the Item<br>`<<titleU>>`: Uppercase Title of the Item<br>`<<titleL>>`Lowercase Title of the Item<br>`<<titleP>>`Proper Title of the Item | ✅ | ✅ | ✅ | ✅ |
|
| `<<title>>`: Title of the Item<br>`<<titleU>>`: Uppercase Title of the Item<br>`<<titleL>>`Lowercase Title of the Item<br>`<<titleP>>`Proper Title of the Item | ✅ | ✅ | ✅ | ✅ |
|
||||||
| `<<show_title>>`: Title of the Item's Show<br>`<<show_itleU>>`: Uppercase Title of the Item's Show<br>`<<show_titleL>>`Lowercase Title of the Item's Show<br>`<<show_titleP>>`Proper Title of the Item's Show | ❌ | ❌ | ✅ | ✅ |
|
| `<<show_title>>`: Title of the Item's Show<br>`<<show_itleU>>`: Uppercase Title of the Item's Show<br>`<<show_titleL>>`Lowercase Title of the Item's Show<br>`<<show_titleP>>`Proper Title of the Item's Show | ❌ | ❌ | ✅ | ✅ |
|
||||||
| `<<season_title>>`: Title of the Item's Season<br>`<<season_titleU>>`: Uppercase Title of the Item's Season<br>`<<season_titleL>>`Lowercase title of the Item's Season<br>`<<season_titleP>>`Proper title of the Item's Season | ❌ | ❌ | ❌ | ✅ |
|
| `<<season_title>>`: Title of the Item's Season<br>`<<season_titleU>>`: Uppercase Title of the Item's Season<br>`<<season_titleL>>`Lowercase title of the Item's Season<br>`<<season_titleP>>`Proper title of the Item's Season | ❌ | ❌ | ❌ | ✅ |
|
||||||
| `<<original_title>>`: Original Title of the Item<br>`<<original_titleU>>`: Original Title of the Item<br>`<<original_titleL>>`Lowercase Original Title of the Item<br>`<<original_titleP>>`Proper Original Title of the Item | ✅ | ✅ | ❌ | ❌ |
|
| `<<original_title>>`: Original Title of the Item<br>`<<original_titleU>>`: Original Title of the Item<br>`<<original_titleL>>`Lowercase Original Title of the Item<br>`<<original_titleP>>`Proper Original Title of the Item | ✅ | ✅ | ❌ | ❌ |
|
||||||
| `<<edition>>`: Edition of the Item<br>`<<editionU>>`: Uppercase Edition of the Item<br>`<<editionL>>`Lowercase Edition of the Item<br>`<<editionP>>`Proper Edition of the Item | ✅ | ❌ | ❌ | ❌ |
|
| `<<edition>>`: Edition of the Item<br>`<<editionU>>`: Uppercase Edition of the Item<br>`<<editionL>>`Lowercase Edition of the Item<br>`<<editionP>>`Proper Edition of the Item | ✅ | ❌ | ❌ | ❌ |
|
||||||
| `<<content_rating>>`: Content Rating of the Item<br>`<<content_ratingU>>`: Uppercase Content Rating of the Item<br>`<<content_ratingL>>`Lowercase Content Rating of the Item<br>`<<content_ratingP>>`Proper Content Rating of the Item | ✅ | ✅ | ❌ | ✅ |
|
| `<<content_rating>>`: Content Rating of the Item<br>`<<content_ratingU>>`: Uppercase Content Rating of the Item<br>`<<content_ratingL>>`Lowercase Content Rating of the Item<br>`<<content_ratingP>>`Proper Content Rating of the Item | ✅ | ✅ | ❌ | ✅ |
|
||||||
| `<<episode_count>>`: Number of Episodes (`1`)<br>`<<episode_countW>>`: Number of Episodes As Words (`One`)<br>`<<episode_count0>>`: Number of Episodes With 10s Padding (`01`)<br>`<<episode_count00>>`: Number of Episodes With 100s Padding (`001`) | ❌ | ✅ | ✅ | ❌ |
|
| `<<episode_count>>`: Number of Episodes (`1`)<br>`<<episode_countW>>`: Number of Episodes As Words (`One`)<br>`<<episode_countWU>>`: Number of Episodes As Uppercase Words (`ONE`)<br>`<<episode_countWL>>`: Number of Episodes As Lowercase Words (`one`)<br>`<<episode_count0>>`: Number of Episodes With 10s Padding (`01`)<br>`<<episode_count00>>`: Number of Episodes With 100s Padding (`001`) | ❌ | ✅ | ✅ | ❌ |
|
||||||
| `<<season_number>>`: Season Number (`1`)<br>`<<season_numberW>>`: Season Number As Words (`One`)<br>`<<season_number0>>`: Season Number With 10s Padding (`01`)<br>`<<season_number00>>`: Season Number With 100s Padding (`001`) | ❌ | ❌ | ✅ | ✅ |
|
| `<<season_number>>`: Season Number (`1`)<br>`<<season_numberW>>`: Season Number As Words (`One`)<br>`<<season_numberWU>>`: Season Number As Uppercase Words (`ONE`)<br>`<<season_numberWL>>`: Season Number As Lowercase Words (`one`)<br>`<<season_number0>>`: Season Number With 10s Padding (`01`)<br>`<<season_number00>>`: Season Number With 100s Padding (`001`) | ❌ | ❌ | ✅ | ✅ |
|
||||||
| `<<episode_number>>`: Episode Number (`1`)<br>`<<episode_numberW>>`: Episode Number As Words (`One`)<br>`<<episode_number0>>`: Episode Number With 10s Padding (`01`)<br>`<<episode_number00>>`: Episode Number With 100s Padding (`001`) | ❌ | ❌ | ❌ | ✅ |
|
| `<<episode_number>>`: Episode Number (`1`)<br>`<<episode_numberW>>`: Episode Number As Words (`One`)<br>`<<episode_numberWU>>`: Episode Number As Uppercase Words (`One`)<br>`<<episode_numberWL>>`: Episode Number As Lowercase Words (`one`)<br>`<<episode_number0>>`: Episode Number With 10s Padding (`01`)<br>`<<episode_number00>>`: Episode Number With 100s Padding (`001`) | ❌ | ❌ | ❌ | ✅ |
|
||||||
| `<<versions>>`: Number of Versions of the Item (`1`)<br>`<<versionsW>>`: Number of Versions of the Item As Words (`One`)<br>`<<versions0>>`: Number of Versions of the Item With 10s Padding (`01`)<br>`<<versions00>>`: Number of Versions of the Item With 100s Padding (`001`) | ✅ | ❌ | ❌ | ✅ |
|
| `<<versions>>`: Number of Versions of the Item (`1`)<br>`<<versionsW>>`: Number of Versions of the Item As Words (`One`)<br>`<<versionsWO>>`: Number of Versions of the Item As Uppercase Words (`ONE`)<br>`<<versionsWL>>`: Number of Versions of the Item As Words (`one`)<br>`<<versions0>>`: Number of Versions of the Item With 10s Padding (`01`)<br>`<<versions00>>`: Number of Versions of the Item With 100s Padding (`001`) | ✅ | ❌ | ❌ | ✅ |
|
||||||
| `<<runtime>>`: Complete Runtime of the Item in minutes (`150`)<br>`<<runtimeH>>`: Hours in runtime of the Item (`2`)<br>`<<runtimeM>>`: Minutes remaining in the hour in the runtime of the Item (`30`) | ✅ | ❌ | ❌ | ✅ |
|
| `<<runtime>>`: Complete Runtime of the Item in minutes (`150`)<br>`<<runtimeH>>`: Hours in runtime of the Item (`2`)<br>`<<runtimeM>>`: Minutes remaining in the hour in the runtime of the Item (`30`) | ✅ | ❌ | ❌ | ✅ |
|
||||||
| `<<bitrate>>`: Bitrate of the first media file for an item.<br>`<<bitrateH>>`: Bitrate of the media file with the highest bitrate<br>`<<bitrateL>>`: Bitrate of the media file with the lowest bitrate | ✅ | ❌ | ❌ | ✅ |
|
| `<<bitrate>>`: Bitrate of the first media file for an item.<br>`<<bitrateH>>`: Bitrate of the media file with the highest bitrate<br>`<<bitrateL>>`: Bitrate of the media file with the lowest bitrate | ✅ | ❌ | ❌ | ✅ |
|
||||||
| `<<originally_available>>`: Original Available Date of the Item<br>`<<originally_available[FORMAT]>>`: Original Available Date of the Item in the given format. [Format Options](https://strftime.org/) | ✅ | ✅ | ❌ | ✅ |
|
| `<<originally_available>>`: Original Available Date of the Item<br>`<<originally_available[FORMAT]>>`: Original Available Date of the Item in the given format. [Format Options](https://strftime.org/) | ✅ | ✅ | ❌ | ✅ |
|
||||||
|
|
||||||
Note: You can use the `mass_audience_rating_update` or `mass_critic_rating_update` [Library Operation](../config/operations) to update your plex ratings to various services like `tmdb`, `imdb`, `mdb`, `metacritic`, `letterboxd` and many more.
|
Note: You can use the `mass_audience_rating_update` or `mass_critic_rating_update` [Library Operation](../config/operations) to update your plex ratings to various services like `tmdb`, `imdb`, `mdb`, `metacritic`, `letterboxd` and many more.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Templates
|
# Templates
|
||||||
|
|
||||||
Collection and Overlay Definitions often share a lot of common or generalizable configuration details. Templates allow you to define these details so they can be used across multiple definitions.
|
Collection, Playlist, Metadata, and Overlay Definitions often share a lot of common or generalizable configuration details. Templates allow you to define these details so they can be used across multiple definitions.
|
||||||
|
|
||||||
For example, an actor collection might look like this:
|
For example, an actor collection might look like this:
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from datetime import datetime
|
||||||
from modules import anidb, anilist, flixpatrol, icheckmovies, imdb, letterboxd, mal, plex, radarr, reciperr, sonarr, tautulli, tmdb, trakt, tvdb, mdblist, util
|
from modules import anidb, anilist, flixpatrol, icheckmovies, imdb, letterboxd, mal, plex, radarr, reciperr, sonarr, tautulli, tmdb, trakt, tvdb, mdblist, util
|
||||||
from modules.util import Failed, FilterFailed, NonExisting, NotScheduled, NotScheduledRange, Deleted
|
from modules.util import Failed, FilterFailed, NonExisting, NotScheduled, NotScheduledRange, Deleted
|
||||||
from modules.overlay import Overlay
|
from modules.overlay import Overlay
|
||||||
|
from modules.poster import PMMImage
|
||||||
from plexapi.audio import Artist, Album, Track
|
from plexapi.audio import Artist, Album, Track
|
||||||
from plexapi.exceptions import NotFound
|
from plexapi.exceptions import NotFound
|
||||||
from plexapi.video import Movie, Show, Season, Episode
|
from plexapi.video import Movie, Show, Season, Episode
|
||||||
|
@ -467,6 +468,19 @@ class CollectionBuilder:
|
||||||
raise Failed(f"{self.Type} Error: {self.data[methods['builder_level']]} builder_level invalid{options}")
|
raise Failed(f"{self.Type} Error: {self.data[methods['builder_level']]} builder_level invalid{options}")
|
||||||
self.parts_collection = self.builder_level in plex.builder_level_options
|
self.parts_collection = self.builder_level in plex.builder_level_options
|
||||||
|
|
||||||
|
self.posters = {}
|
||||||
|
self.backgrounds = {}
|
||||||
|
if "pmm_poster" in methods:
|
||||||
|
logger.debug("")
|
||||||
|
logger.debug("Validating Method: pmm_poster")
|
||||||
|
if self.data[methods["pmm_poster"]] is None:
|
||||||
|
logger.error(f"{self.Type} Error: pmm_poster attribute is blank")
|
||||||
|
logger.debug(f"Value: {data[methods['pmm_poster']]}")
|
||||||
|
try:
|
||||||
|
self.posters["pmm_poster"] = PMMImage(self.config, self.data[methods["pmm_poster"]], "pmm_poster", playlist=self.playlist)
|
||||||
|
except Failed as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
if self.overlay:
|
if self.overlay:
|
||||||
if "overlay" in methods:
|
if "overlay" in methods:
|
||||||
overlay_data = data[methods["overlay"]]
|
overlay_data = data[methods["overlay"]]
|
||||||
|
@ -578,8 +592,6 @@ class CollectionBuilder:
|
||||||
self.notification_removals = []
|
self.notification_removals = []
|
||||||
self.items = []
|
self.items = []
|
||||||
self.remove_item_map = {}
|
self.remove_item_map = {}
|
||||||
self.posters = {}
|
|
||||||
self.backgrounds = {}
|
|
||||||
self.schedule = ""
|
self.schedule = ""
|
||||||
self.beginning_count = 0
|
self.beginning_count = 0
|
||||||
self.default_percent = 50
|
self.default_percent = 50
|
||||||
|
@ -3010,11 +3022,24 @@ class CollectionBuilder:
|
||||||
self.collection_poster = util.pick_image(self.obj.title, self.posters, self.library.prioritize_assets, self.library.download_url_assets, asset_location)
|
self.collection_poster = util.pick_image(self.obj.title, self.posters, self.library.prioritize_assets, self.library.download_url_assets, asset_location)
|
||||||
self.collection_background = util.pick_image(self.obj.title, self.backgrounds, self.library.prioritize_assets, self.library.download_url_assets, asset_location, is_poster=False)
|
self.collection_background = util.pick_image(self.obj.title, self.backgrounds, self.library.prioritize_assets, self.library.download_url_assets, asset_location, is_poster=False)
|
||||||
|
|
||||||
|
clean_temp = False
|
||||||
|
if isinstance(self.collection_poster, PMMImage):
|
||||||
|
clean_temp = True
|
||||||
|
item_vars = {"title": self.name, "titleU": self.name.upper(), "titleL": self.name.lower()}
|
||||||
|
self.collection_poster = self.collection_poster.save(item_vars)
|
||||||
|
|
||||||
if self.collection_poster or self.collection_background:
|
if self.collection_poster or self.collection_background:
|
||||||
pu, bu = self.library.upload_images(self.obj, poster=self.collection_poster, background=self.collection_background)
|
pu, bu = self.library.upload_images(self.obj, poster=self.collection_poster, background=self.collection_background)
|
||||||
if pu or bu:
|
if pu or bu:
|
||||||
updated_details.append("Image")
|
updated_details.append("Image")
|
||||||
|
|
||||||
|
if clean_temp:
|
||||||
|
code_base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
posters_dir = os.path.join(code_base, "defaults", "posters")
|
||||||
|
for filename in os.listdir(posters_dir):
|
||||||
|
if "temp" in filename:
|
||||||
|
os.remove(os.path.join(posters_dir, filename))
|
||||||
|
|
||||||
if self.url_theme: # TODO: cache theme path to not constantly upload
|
if self.url_theme: # TODO: cache theme path to not constantly upload
|
||||||
self.library.upload_theme(self.obj, url=self.url_theme)
|
self.library.upload_theme(self.obj, url=self.url_theme)
|
||||||
elif self.file_theme:
|
elif self.file_theme:
|
||||||
|
|
|
@ -490,7 +490,7 @@ class Plex(Library):
|
||||||
def get_all_collections(self, label=None):
|
def get_all_collections(self, label=None):
|
||||||
args = "?type=18"
|
args = "?type=18"
|
||||||
if label:
|
if label:
|
||||||
label_id = next((c.key for c in self.get_tags("label") if c.title == label), None)
|
label_id = next((c.key for c in self.get_tags("label") if c.title == label), None) # noqa
|
||||||
if label_id:
|
if label_id:
|
||||||
args = f"{args}&label={label_id}"
|
args = f"{args}&label={label_id}"
|
||||||
else:
|
else:
|
||||||
|
@ -677,12 +677,11 @@ class Plex(Library):
|
||||||
item, is_full = self.cached_items[item.ratingKey]
|
item, is_full = self.cached_items[item.ratingKey]
|
||||||
try:
|
try:
|
||||||
if not is_full or force:
|
if not is_full or force:
|
||||||
item.reload(checkFiles=False, includeAllConcerts=False, includeBandwidths=False,
|
item.reload(checkFiles=False, includeAllConcerts=False, includeBandwidths=False, includeChapters=False,
|
||||||
includeChapters=False, includeChildren=False, includeConcerts=False,
|
includeChildren=False, includeConcerts=False, includeExternalMedia=False, includeExtras=False,
|
||||||
includeExternalMedia=False, includeExtras=False, includeFields=False,
|
includeFields=False, includeGeolocation=False, includeLoudnessRamps=False, includeMarkers=False,
|
||||||
includeGeolocation=False, includeLoudnessRamps=False, includeMarkers=False,
|
includeOnDeck=False, includePopularLeaves=False, includeRelated=False, includeRelatedCount=0,
|
||||||
includeOnDeck=False, includePopularLeaves=False, includeRelated=False,
|
includeReviews=False, includeStations=False)
|
||||||
includeRelatedCount=0, includeReviews=False, includeStations=False)
|
|
||||||
item._autoReload = False
|
item._autoReload = False
|
||||||
self.cached_items[item.ratingKey] = (item, True)
|
self.cached_items[item.ratingKey] = (item, True)
|
||||||
except (BadRequest, NotFound) as e:
|
except (BadRequest, NotFound) as e:
|
||||||
|
@ -843,7 +842,7 @@ class Plex(Library):
|
||||||
self._query(key, put=True)
|
self._query(key, put=True)
|
||||||
|
|
||||||
def smart_label_check(self, label):
|
def smart_label_check(self, label):
|
||||||
labels = [la.title for la in self.get_tags("label")]
|
labels = [la.title for la in self.get_tags("label")] # noqa
|
||||||
if label in labels:
|
if label in labels:
|
||||||
return True
|
return True
|
||||||
logger.trace(f"Label not found in Plex. Options: {labels}")
|
logger.trace(f"Label not found in Plex. Options: {labels}")
|
||||||
|
@ -878,7 +877,7 @@ class Plex(Library):
|
||||||
self._query(f"/library/collections{utils.joinArgs(args)}", post=True)
|
self._query(f"/library/collections{utils.joinArgs(args)}", post=True)
|
||||||
|
|
||||||
def get_smart_filter_from_uri(self, uri):
|
def get_smart_filter_from_uri(self, uri):
|
||||||
smart_filter = parse.parse_qs(parse.urlparse(uri.replace("/#!/", "/")).query)["key"][0]
|
smart_filter = parse.parse_qs(parse.urlparse(uri.replace("/#!/", "/")).query)["key"][0] # noqa
|
||||||
args = smart_filter[smart_filter.index("?"):]
|
args = smart_filter[smart_filter.index("?"):]
|
||||||
return self.build_smart_filter(args), int(args[args.index("type=") + 5:args.index("type=") + 6])
|
return self.build_smart_filter(args), int(args[args.index("type=") + 5:args.index("type=") + 6])
|
||||||
|
|
||||||
|
|
385
modules/poster.py
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
import os, time
|
||||||
|
from modules import util
|
||||||
|
from modules.util import Failed, ImageData
|
||||||
|
from PIL import Image, ImageFont, ImageDraw, ImageColor
|
||||||
|
|
||||||
|
logger = util.logger
|
||||||
|
|
||||||
|
class ImageBase:
|
||||||
|
def __init__(self, config, data):
|
||||||
|
self.config = config
|
||||||
|
self.data = data
|
||||||
|
self.methods = {str(m).lower(): m for m in self.data}
|
||||||
|
self.code_base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
self.posters_dir = os.path.join(self.code_base, "defaults", "posters")
|
||||||
|
|
||||||
|
def check_data(self, attr):
|
||||||
|
if attr not in self.methods or not self.data[self.methods[attr]]:
|
||||||
|
return None
|
||||||
|
return self.data[self.methods[attr]]
|
||||||
|
|
||||||
|
def check_file(self, attr, pmm_items, local=False, required=False):
|
||||||
|
if attr not in self.methods or not self.data[self.methods[attr]]:
|
||||||
|
if required:
|
||||||
|
raise Failed(f"Posters Error: {attr} not found or is blank")
|
||||||
|
return None
|
||||||
|
file_data = self.data[self.methods[attr]]
|
||||||
|
if isinstance(file_data, list):
|
||||||
|
file_data = file_data[0]
|
||||||
|
if not isinstance(file_data, dict):
|
||||||
|
file_data = {"pmm": str(file_data)}
|
||||||
|
if "pmm" in file_data and file_data["pmm"]:
|
||||||
|
file_path = pmm_items[file_data["pmm"]] if file_data["pmm"] in pmm_items else file_data["pmm"]
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
return file_path, os.path.getsize(file_path)
|
||||||
|
raise Failed(f"Poster Error: {attr} pmm invalid. Options: {', '.join(pmm_items.keys())}")
|
||||||
|
elif "file" in file_data and file_data["file"]:
|
||||||
|
if os.path.exists(file_data["file"]):
|
||||||
|
return file_data["file"], os.path.getsize(file_data["file"])
|
||||||
|
raise Failed(f"Poster Error: {attr} file not found: {os.path.abspath(file_data['file'])}")
|
||||||
|
elif local:
|
||||||
|
return None, None
|
||||||
|
elif "git" in file_data and file_data["git"]:
|
||||||
|
url = f"{self.config.GitHub.configs_url}{file_data['git']}"
|
||||||
|
elif "repo" in file_data and file_data["repo"]:
|
||||||
|
url = f"{self.config.custom_repo}{file_data['repo']}"
|
||||||
|
elif "url" in file_data and file_data["url"]:
|
||||||
|
url = file_data["url"]
|
||||||
|
else:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
response = self.config.get(url)
|
||||||
|
if response.status_code >= 400:
|
||||||
|
raise Failed(f"Poster Error: {attr} not found at: {url}")
|
||||||
|
if "Content-Type" not in response.headers or response.headers["Content-Type"] not in util.image_content_types:
|
||||||
|
raise Failed(f"Poster Error: {attr} not a png, jpg, or webp: {url}")
|
||||||
|
if response.headers["Content-Type"] == "image/jpeg":
|
||||||
|
ext = "jpg"
|
||||||
|
elif response.headers["Content-Type"] == "image/webp":
|
||||||
|
ext = "webp"
|
||||||
|
else:
|
||||||
|
ext = "png"
|
||||||
|
num = ""
|
||||||
|
image_path = os.path.join(self.posters_dir, f"temp{num}.{ext}")
|
||||||
|
while os.path.exists(image_path):
|
||||||
|
if not num:
|
||||||
|
num = 1
|
||||||
|
else:
|
||||||
|
num += 1
|
||||||
|
image_path = os.path.join(self.posters_dir, f"temp{num}.{ext}")
|
||||||
|
with open(image_path, "wb") as handler:
|
||||||
|
handler.write(response.content)
|
||||||
|
while util.is_locked(image_path):
|
||||||
|
time.sleep(1)
|
||||||
|
return image_path, url
|
||||||
|
|
||||||
|
def check_color(self, attr):
|
||||||
|
if attr not in self.methods or not self.data[self.methods[attr]]:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return ImageColor.getcolor(self.data[self.methods[attr]], "RGBA")
|
||||||
|
except ValueError:
|
||||||
|
raise Failed(f"Poster Error: {attr}: {self.data[self.methods[attr]]} invalid")
|
||||||
|
|
||||||
|
class Component(ImageBase):
|
||||||
|
def __init__(self, config, data):
|
||||||
|
super().__init__(config, data)
|
||||||
|
self.draw = ImageDraw.Draw(Image.new("RGBA", (0, 0)))
|
||||||
|
self.back_color = self.check_color("back_color")
|
||||||
|
self.back_radius = util.parse("Posters", "back_radius", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "back_radius" in self.methods else 0
|
||||||
|
self.back_line_width = util.parse("Posters", "back_line_width", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "back_line_width" in self.methods else 0
|
||||||
|
self.back_line_color = self.check_color("back_line_color")
|
||||||
|
self.back_padding = util.parse("Posters", "back_padding", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "back_padding" in self.methods else 0
|
||||||
|
self.back_align = util.parse("Posters", "back_align", self.data, methods=self.methods, default="center", options=["left", "right", "center", "top", "bottom"]) if "back_align" in self.methods else "center"
|
||||||
|
|
||||||
|
self.back_width = 0
|
||||||
|
if "back_width" in self.methods:
|
||||||
|
if str(self.methods["back_width"]).lower() == "max":
|
||||||
|
self.back_width = "max"
|
||||||
|
else:
|
||||||
|
self.back_width = util.parse("Posters", "back_width", self.data, methods=self.methods, datatype="int", minimum=0)
|
||||||
|
self.back_height = 0
|
||||||
|
if "back_height" in self.methods:
|
||||||
|
if str(self.methods["back_height"]).lower() == "max":
|
||||||
|
self.back_height = "max"
|
||||||
|
else:
|
||||||
|
self.back_height = util.parse("Posters", "back_height", self.data, methods=self.methods, datatype="int", minimum=0)
|
||||||
|
self.has_back = True if self.back_color or self.back_line_color else False
|
||||||
|
self.horizontal_offset, self.horizontal_align, self.vertical_offset, self.vertical_align = util.parse_cords(self.data, "component", err_type="Posters", default=(0, "center", 0, "center"))
|
||||||
|
|
||||||
|
self.images_dir = os.path.join(self.posters_dir, "images")
|
||||||
|
self.pmm_images = {k[:-4]: os.path.join(self.images_dir, k) for k in os.listdir(self.images_dir)}
|
||||||
|
self.image, self.image_compare = self.check_file("image", self.pmm_images)
|
||||||
|
self.image_width = util.parse("Posters", "image_width", self.data, datatype="int", methods=self.methods, default=0, minimum=0, maximum=2000) if "image_width" in self.methods else 0
|
||||||
|
self.image_color = self.check_color("image_color")
|
||||||
|
|
||||||
|
self.text = None
|
||||||
|
self.font_name = None
|
||||||
|
self.font = None
|
||||||
|
self.font_style = None
|
||||||
|
self.addon_position = None
|
||||||
|
self.text_align = util.parse("Posters", "text_align", self.data, methods=self.methods, default="center", options=["left", "right", "center"]) if "text_align" in self.methods else "center"
|
||||||
|
self.font_size = util.parse("Posters", "font_size", self.data, datatype="int", methods=self.methods, default=163, minimum=1) if "font_size" in self.methods else 163
|
||||||
|
self.font_color = self.check_color("font_color")
|
||||||
|
self.stroke_color = self.check_color("stroke_color")
|
||||||
|
self.stroke_width = util.parse("Posters", "stroke_width", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "stroke_width" in self.methods else 0
|
||||||
|
self.addon_offset = util.parse("Posters", "addon_offset", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "stroke_width" in self.methods else 0
|
||||||
|
if "text" in self.methods:
|
||||||
|
font_base = os.path.join(self.code_base, "fonts")
|
||||||
|
pmm_fonts = os.listdir(font_base)
|
||||||
|
all_fonts = {s: s for s in util.get_system_fonts()}
|
||||||
|
for font_name in pmm_fonts:
|
||||||
|
all_fonts[font_name] = os.path.join(font_base, font_name)
|
||||||
|
self.text = util.parse("Posters", "text", self.data, methods=self.methods, default="<<title>>")
|
||||||
|
self.font_name, self.font_compare = self.check_file("font", all_fonts, local=True)
|
||||||
|
if not self.font_name:
|
||||||
|
self.font_name = all_fonts["Roboto-Medium.ttf"]
|
||||||
|
self.font = ImageFont.truetype(self.font_name, self.font_size)
|
||||||
|
if "font_style" in self.methods and self.data[self.methods["font_style"]]:
|
||||||
|
try:
|
||||||
|
variation_names = [n.decode("utf-8") for n in self.font.get_variation_names()]
|
||||||
|
if self.data[self.methods["font_style"]] in variation_names:
|
||||||
|
self.font.set_variation_by_name(self.data[self.methods["font_style"]])
|
||||||
|
self.font_style = self.data[self.methods["font_style"]]
|
||||||
|
else:
|
||||||
|
raise Failed(f"Posters Error: Font Style {self.data[self.methods['font_style']]} not found. Options: {','.join(variation_names)}")
|
||||||
|
except OSError:
|
||||||
|
raise Failed(f"Posters Warning: font: {self.font} does not have variations")
|
||||||
|
self.addon_position = util.parse("Posters", "addon_position", self.data, methods=self.methods, options=["left", "right", "top", "bottom"]) if "addon_position" in self.methods else "left"
|
||||||
|
|
||||||
|
if not self.image and not self.text:
|
||||||
|
raise Failed("Posters Error: An image or text is required for each component")
|
||||||
|
|
||||||
|
def apply_vars(self, item_vars):
|
||||||
|
for var_key, var_data in item_vars.items():
|
||||||
|
self.text = self.text.replace(f"<<{var_key}>>", str(var_data))
|
||||||
|
|
||||||
|
def adjust_text_width(self, max_width):
|
||||||
|
lines = []
|
||||||
|
for line in self.text.split("\n"):
|
||||||
|
for word in line.split(" "):
|
||||||
|
word_length = self.draw.textlength(word, font=self.font)
|
||||||
|
while word_length > max_width:
|
||||||
|
self.font_size -= 1
|
||||||
|
self.font = ImageFont.truetype(self.font_name, self.font_size)
|
||||||
|
word_length = self.draw.textlength(word, font=self.font)
|
||||||
|
for line in self.text.split("\n"):
|
||||||
|
line_length = self.draw.textlength(line, font=self.font)
|
||||||
|
if line_length <= max_width:
|
||||||
|
lines.append(line)
|
||||||
|
continue
|
||||||
|
current_line = ""
|
||||||
|
line_length = 0
|
||||||
|
for word in line.split(" "):
|
||||||
|
if current_line:
|
||||||
|
word = f" {word}"
|
||||||
|
word_length = self.draw.textlength(word, font=self.font)
|
||||||
|
if line_length + word_length <= max_width:
|
||||||
|
current_line += word
|
||||||
|
line_length += word_length
|
||||||
|
else:
|
||||||
|
if current_line:
|
||||||
|
lines.append(current_line)
|
||||||
|
word = word.strip()
|
||||||
|
word_length = self.draw.textlength(word, font=self.font)
|
||||||
|
current_line = word
|
||||||
|
line_length = word_length
|
||||||
|
if current_line:
|
||||||
|
lines.append(current_line)
|
||||||
|
self.text = "\n".join(lines)
|
||||||
|
|
||||||
|
def get_compare_string(self):
|
||||||
|
output = ""
|
||||||
|
if self.text:
|
||||||
|
output += f"{self.text} {self.text_align} {self.font_compare}"
|
||||||
|
output += str(self.font_size)
|
||||||
|
for value in [self.font_color, self.font_style, self.stroke_color, self.stroke_width]:
|
||||||
|
if value:
|
||||||
|
output += f"{value}"
|
||||||
|
if self.image:
|
||||||
|
output += f"{self.addon_position} {self.addon_offset}"
|
||||||
|
|
||||||
|
if self.image:
|
||||||
|
output += str(self.image_compare)
|
||||||
|
for value in [self.image_width, self.image_color]:
|
||||||
|
if value:
|
||||||
|
output += str(value)
|
||||||
|
|
||||||
|
output += f"({self.horizontal_offset},{self.horizontal_align},{self.vertical_offset},{self.vertical_align})"
|
||||||
|
if self.has_back:
|
||||||
|
for value in [self.back_color, self.back_radius, self.back_padding, self.back_align,
|
||||||
|
self.back_width, self.back_height, self.back_line_color, self.back_line_width]:
|
||||||
|
if value is not None:
|
||||||
|
output += f"{value}"
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_text_size(self, text):
|
||||||
|
return self.draw.multiline_textbbox((0, 0), text, font=self.font)
|
||||||
|
|
||||||
|
def get_coordinates(self, canvas_box, box, new_cords=None):
|
||||||
|
canvas_width, canvas_height = canvas_box
|
||||||
|
box_width, box_height = box
|
||||||
|
|
||||||
|
def get_cord(value, image_value, over_value, align):
|
||||||
|
value = int(image_value * 0.01 * int(value[:-1])) if str(value).endswith("%") else int(value)
|
||||||
|
if align in ["right", "bottom"]:
|
||||||
|
return image_value - over_value - value
|
||||||
|
elif align == "center":
|
||||||
|
return int(image_value / 2) - int(over_value / 2) + value
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
if new_cords:
|
||||||
|
ho, ha, vo, va = new_cords
|
||||||
|
else:
|
||||||
|
ho, ha, vo, va = self.horizontal_offset, self.horizontal_align, self.vertical_offset, self.vertical_align
|
||||||
|
|
||||||
|
return get_cord(ho, canvas_width, box_width, ha), get_cord(vo, canvas_height, box_height, va)
|
||||||
|
|
||||||
|
def get_generated_layer(self, canvas_box, new_cords=None):
|
||||||
|
canvas_width, canvas_height = canvas_box
|
||||||
|
generated_layer = None
|
||||||
|
text_width, text_height = None, None
|
||||||
|
if self.image:
|
||||||
|
image = Image.open(self.image)
|
||||||
|
image_width, image_height = image.size
|
||||||
|
if self.image_width:
|
||||||
|
image_height = int(float(image_height) * float(self.image_width / float(image_width)))
|
||||||
|
image_width = self.image_width
|
||||||
|
image = image.resize((image_width, image_height), Image.Resampling.LANCZOS) # noqa
|
||||||
|
if self.image_color:
|
||||||
|
r, g, b = self.image_color
|
||||||
|
pixels = image.load()
|
||||||
|
for x in range(image_width):
|
||||||
|
for y in range(image_height):
|
||||||
|
if pixels[x, y][3] > 0: # noqa
|
||||||
|
pixels[x, y] = (r, g, b, pixels[x, y][3]) # noqa
|
||||||
|
else:
|
||||||
|
image, image_width, image_height = None, 0, 0
|
||||||
|
if self.text is not None:
|
||||||
|
_, _, text_width, text_height = self.get_text_size(self.text)
|
||||||
|
if image_width and self.addon_position in ["left", "right"]:
|
||||||
|
box = (text_width + image_width + self.addon_offset, text_height if text_height > image_height else image_height)
|
||||||
|
elif image_width:
|
||||||
|
box = (text_width if text_width > image_width else image_width, text_height + image_height + self.addon_offset)
|
||||||
|
else:
|
||||||
|
box = (text_width, text_height)
|
||||||
|
else:
|
||||||
|
box = (image_width, image_height)
|
||||||
|
box_width, box_height = box
|
||||||
|
back_width = canvas_width if self.back_width == "max" else self.back_width if self.back_width else box_width
|
||||||
|
back_height = canvas_height if self.back_height == "max" else self.back_height if self.back_height else box_height
|
||||||
|
main_point = self.get_coordinates(canvas_box, (back_width, back_height), new_cords=new_cords)
|
||||||
|
start_x, start_y = main_point
|
||||||
|
|
||||||
|
if self.text is not None or self.has_back:
|
||||||
|
generated_layer = Image.new("RGBA", canvas_box, (255, 255, 255, 0))
|
||||||
|
drawing = ImageDraw.Draw(generated_layer)
|
||||||
|
if self.has_back:
|
||||||
|
cords = (
|
||||||
|
start_x - self.back_padding,
|
||||||
|
start_y - self.back_padding,
|
||||||
|
start_x + back_width + self.back_padding,
|
||||||
|
start_y + back_height + self.back_padding
|
||||||
|
)
|
||||||
|
if self.back_radius:
|
||||||
|
drawing.rounded_rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width, radius=self.back_radius)
|
||||||
|
else:
|
||||||
|
drawing.rectangle(cords, fill=self.back_color, outline=self.back_line_color, width=self.back_line_width)
|
||||||
|
|
||||||
|
main_x, main_y = main_point
|
||||||
|
if self.back_height and self.back_align in ["left", "right", "center", "bottom"]:
|
||||||
|
main_y = start_y + (back_height - box_height) // (1 if self.back_align == "bottom" else 2)
|
||||||
|
if self.back_width and self.back_align in ["top", "bottom", "center", "right"]:
|
||||||
|
main_x = start_x + (back_width - box_width) // (1 if self.back_align == "right" else 2)
|
||||||
|
|
||||||
|
addon_x = None
|
||||||
|
addon_y = None
|
||||||
|
if self.text is not None and self.image:
|
||||||
|
addon_x = main_x
|
||||||
|
addon_y = main_y
|
||||||
|
if self.addon_position == "left":
|
||||||
|
main_x = main_x + image_width + self.addon_offset
|
||||||
|
elif self.addon_position == "right":
|
||||||
|
addon_x = main_x + text_width + self.addon_offset
|
||||||
|
elif text_width < image_width:
|
||||||
|
main_x = main_x + ((image_width - text_width) / 2)
|
||||||
|
elif text_width > image_width:
|
||||||
|
addon_x = main_x + ((text_width - image_width) / 2)
|
||||||
|
|
||||||
|
if self.addon_position == "top":
|
||||||
|
main_y = main_y + image_height + self.addon_offset
|
||||||
|
elif self.addon_position == "bottom":
|
||||||
|
addon_y = main_y + text_height + self.addon_offset
|
||||||
|
elif text_height < image_height:
|
||||||
|
main_y = main_y + ((image_height - text_height) / 2)
|
||||||
|
elif text_height > image_height:
|
||||||
|
addon_y = main_y + ((text_height - image_height) / 2)
|
||||||
|
main_point = (int(main_x), int(main_y))
|
||||||
|
|
||||||
|
if self.text is not None:
|
||||||
|
drawing.multiline_text(main_point, self.text, font=self.font, fill=self.font_color, align=self.text_align,
|
||||||
|
stroke_fill=self.stroke_color, stroke_width=self.stroke_width)
|
||||||
|
if addon_x is not None:
|
||||||
|
main_point = (addon_x, addon_y)
|
||||||
|
|
||||||
|
return generated_layer, main_point, image
|
||||||
|
|
||||||
|
class PMMImage(ImageBase):
|
||||||
|
def __init__(self, config, data, image_attr, playlist=False):
|
||||||
|
super().__init__(config, data)
|
||||||
|
self.image_attr = image_attr
|
||||||
|
self.backgrounds_dir = os.path.join(self.posters_dir, "backgrounds")
|
||||||
|
self.playlist = playlist
|
||||||
|
self.pmm_backgrounds = {k[:-4]: os.path.join(self.backgrounds_dir, k) for k in os.listdir(self.backgrounds_dir)}
|
||||||
|
|
||||||
|
self.background_image, self.background_compare = self.check_file("background_image", self.pmm_backgrounds)
|
||||||
|
self.background_color = self.check_color("background_color")
|
||||||
|
self.border_width = util.parse("Posters", "border_width", self.data, datatype="int", methods=self.methods, default=0, minimum=0) if "border_width" in self.methods else 0
|
||||||
|
self.border_color = self.check_color("border_color")
|
||||||
|
if "components" not in self.methods or not self.data[self.methods["components"]]:
|
||||||
|
raise Failed("Posters Error: components attribute is required")
|
||||||
|
self.components = [Component(self.config, d) for d in util.parse("Posters", "components", self.data, datatype="listdict", methods=self.methods)]
|
||||||
|
|
||||||
|
def get_compare_string(self):
|
||||||
|
output = ""
|
||||||
|
for value in [self.background_compare, self.background_color, self.border_width, self.border_color]:
|
||||||
|
if value:
|
||||||
|
output += f"{value}"
|
||||||
|
for component in self.components:
|
||||||
|
output += component.get_compare_string()
|
||||||
|
return output
|
||||||
|
|
||||||
|
def save(self, item_vars):
|
||||||
|
image_path = os.path.join(self.posters_dir, "temp_poster.png")
|
||||||
|
if os.path.exists(image_path):
|
||||||
|
os.remove(image_path)
|
||||||
|
canvas_width = 1000
|
||||||
|
canvas_height = 1000 if self.playlist else 1500
|
||||||
|
canvas_box = (canvas_width, canvas_height)
|
||||||
|
|
||||||
|
pmm_image = Image.new(mode="RGB", size=canvas_box, color=self.background_color)
|
||||||
|
if self.background_image:
|
||||||
|
bkg_image = Image.open(self.background_image)
|
||||||
|
bkg_image = bkg_image.resize(canvas_box, Image.Resampling.LANCZOS) # noqa
|
||||||
|
pmm_image.paste(bkg_image, (0, 0), bkg_image)
|
||||||
|
|
||||||
|
if self.border_width:
|
||||||
|
draw = ImageDraw.Draw(pmm_image)
|
||||||
|
draw.rectangle(((0, 0), canvas_box), outline=self.border_color, width=self.border_width)
|
||||||
|
|
||||||
|
max_border_width = canvas_width - self.border_width - 100
|
||||||
|
|
||||||
|
for component in self.components:
|
||||||
|
if component.text:
|
||||||
|
component.apply_vars(item_vars)
|
||||||
|
component.adjust_text_width(component.back_width if component.back_width and component.back_width != "max" else max_border_width)
|
||||||
|
generated_layer, image_point, image = component.get_generated_layer(canvas_box)
|
||||||
|
if generated_layer:
|
||||||
|
pmm_image.paste(generated_layer, (0, 0), generated_layer)
|
||||||
|
if image:
|
||||||
|
pmm_image.paste(image, image_point, image)
|
||||||
|
|
||||||
|
pmm_image.save(image_path)
|
||||||
|
|
||||||
|
return ImageData(self.image_attr, image_path, is_url=False, compare=self.get_compare_string())
|
||||||
|
|
|
@ -44,13 +44,13 @@ class NotScheduledRange(NotScheduled):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ImageData:
|
class ImageData:
|
||||||
def __init__(self, attribute, location, prefix="", is_poster=True, is_url=True):
|
def __init__(self, attribute, location, prefix="", is_poster=True, is_url=True, compare=None):
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
self.location = location
|
self.location = location
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.is_poster = is_poster
|
self.is_poster = is_poster
|
||||||
self.is_url = is_url
|
self.is_url = is_url
|
||||||
self.compare = location if is_url else os.stat(location).st_size
|
self.compare = compare if compare else location if is_url else os.stat(location).st_size
|
||||||
self.message = f"{prefix}{'poster' if is_poster else 'background'} to [{'URL' if is_url else 'File'}] {location}"
|
self.message = f"{prefix}{'poster' if is_poster else 'background'} to [{'URL' if is_url else 'File'}] {location}"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -211,8 +211,8 @@ def pick_image(title, images, prioritize_assets, download_url_assets, item_dir,
|
||||||
if prioritize_assets and "asset_directory" in images:
|
if prioritize_assets and "asset_directory" in images:
|
||||||
return images["asset_directory"]
|
return images["asset_directory"]
|
||||||
for attr in ["style_data", f"url_{image_type}", f"file_{image_type}", f"tmdb_{image_type}", "tmdb_profile",
|
for attr in ["style_data", f"url_{image_type}", f"file_{image_type}", f"tmdb_{image_type}", "tmdb_profile",
|
||||||
"tmdb_list_poster", "tvdb_list_poster", f"tvdb_{image_type}", "asset_directory", "tmdb_person",
|
"tmdb_list_poster", "tvdb_list_poster", f"tvdb_{image_type}", "asset_directory", f"pmm_{image_type}",
|
||||||
"tmdb_collection_details", "tmdb_actor_details", "tmdb_crew_details", "tmdb_director_details",
|
"tmdb_person", "tmdb_collection_details", "tmdb_actor_details", "tmdb_crew_details", "tmdb_director_details",
|
||||||
"tmdb_producer_details", "tmdb_writer_details", "tmdb_movie_details", "tmdb_list_details",
|
"tmdb_producer_details", "tmdb_writer_details", "tmdb_movie_details", "tmdb_list_details",
|
||||||
"tvdb_list_details", "tvdb_movie_details", "tvdb_show_details", "tmdb_show_details"]:
|
"tvdb_list_details", "tvdb_movie_details", "tvdb_show_details", "tmdb_show_details"]:
|
||||||
if attr in images:
|
if attr in images:
|
||||||
|
@ -224,7 +224,7 @@ def pick_image(title, images, prioritize_assets, download_url_assets, item_dir,
|
||||||
return download_image(title, images[attr], item_dir, image_name)
|
return download_image(title, images[attr], item_dir, image_name)
|
||||||
except Failed as e:
|
except Failed as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
if attr == "asset_directory":
|
if attr in ["asset_directory", f"pmm_{image_type}"]:
|
||||||
return images[attr]
|
return images[attr]
|
||||||
return ImageData(attr, images[attr], is_poster=is_poster, is_url=attr != f"file_{image_type}")
|
return ImageData(attr, images[attr], is_poster=is_poster, is_url=attr != f"file_{image_type}")
|
||||||
|
|
||||||
|
@ -738,7 +738,7 @@ def parse(error, attribute, data, datatype=None, methods=None, parent=None, defa
|
||||||
return []
|
return []
|
||||||
elif datatype == "listdict":
|
elif datatype == "listdict":
|
||||||
final_list = []
|
final_list = []
|
||||||
for dict_data in get_list(value):
|
for dict_data in get_list(value, split=False):
|
||||||
if isinstance(dict_data, dict):
|
if isinstance(dict_data, dict):
|
||||||
final_list.append(dict_data)
|
final_list.append(dict_data)
|
||||||
else:
|
else:
|
||||||
|
@ -816,16 +816,21 @@ def parse(error, attribute, data, datatype=None, methods=None, parent=None, defa
|
||||||
logger.warning(f"{error} Warning: {message} using {default} as default")
|
logger.warning(f"{error} Warning: {message} using {default} as default")
|
||||||
return translation[default] if translation is not None else default
|
return translation[default] if translation is not None else default
|
||||||
|
|
||||||
def parse_cords(data, parent, required=False):
|
def parse_cords(data, parent, required=False, err_type="Overlay", default=None):
|
||||||
horizontal_align = parse("Overlay", "horizontal_align", data["horizontal_align"], parent=parent,
|
dho, dha, dvo, dva = default if default else (None, None, None, None)
|
||||||
|
horizontal_align = parse(err_type, "horizontal_align", data["horizontal_align"], parent=parent,
|
||||||
options=["left", "center", "right"]) if "horizontal_align" in data else None
|
options=["left", "center", "right"]) if "horizontal_align" in data else None
|
||||||
if required and horizontal_align is None:
|
if horizontal_align is None:
|
||||||
raise Failed(f"Overlay Error: {parent} horizontal_align is required")
|
if required:
|
||||||
|
raise Failed(f"{err_type} Error: {parent} horizontal_align is required")
|
||||||
|
horizontal_align = dha
|
||||||
|
|
||||||
vertical_align = parse("Overlay", "vertical_align", data["vertical_align"], parent=parent,
|
vertical_align = parse(err_type, "vertical_align", data["vertical_align"], parent=parent,
|
||||||
options=["top", "center", "bottom"]) if "vertical_align" in data else None
|
options=["top", "center", "bottom"]) if "vertical_align" in data else None
|
||||||
if required and vertical_align is None:
|
if vertical_align is None:
|
||||||
raise Failed(f"Overlay Error: {parent} vertical_align is required")
|
if required:
|
||||||
|
raise Failed(f"{err_type} Error: {parent} vertical_align is required")
|
||||||
|
vertical_align = dva
|
||||||
|
|
||||||
horizontal_offset = None
|
horizontal_offset = None
|
||||||
if "horizontal_offset" in data and data["horizontal_offset"] is not None:
|
if "horizontal_offset" in data and data["horizontal_offset"] is not None:
|
||||||
|
@ -835,7 +840,7 @@ def parse_cords(data, parent, required=False):
|
||||||
x_off = x_off[:-1]
|
x_off = x_off[:-1]
|
||||||
per = True
|
per = True
|
||||||
x_off = check_num(x_off)
|
x_off = check_num(x_off)
|
||||||
error = f"Overlay Error: {parent} horizontal_offset: {data['horizontal_offset']} must be a number"
|
error = f"{err_type} Error: {parent} horizontal_offset: {data['horizontal_offset']} must be a number"
|
||||||
if x_off is None:
|
if x_off is None:
|
||||||
raise Failed(error)
|
raise Failed(error)
|
||||||
if horizontal_align != "center" and not per and x_off < 0:
|
if horizontal_align != "center" and not per and x_off < 0:
|
||||||
|
@ -845,8 +850,10 @@ def parse_cords(data, parent, required=False):
|
||||||
elif horizontal_align == "center" and per and (x_off > 50 or x_off < -50):
|
elif horizontal_align == "center" and per and (x_off > 50 or x_off < -50):
|
||||||
raise Failed(f"{error} between -50% and 50%")
|
raise Failed(f"{error} between -50% and 50%")
|
||||||
horizontal_offset = f"{x_off}%" if per else x_off
|
horizontal_offset = f"{x_off}%" if per else x_off
|
||||||
if required and horizontal_offset is None:
|
if horizontal_offset is None:
|
||||||
raise Failed(f"Overlay Error: {parent} horizontal_offset is required")
|
if required:
|
||||||
|
raise Failed(f"{err_type} Error: {parent} horizontal_offset is required")
|
||||||
|
horizontal_offset = dho
|
||||||
|
|
||||||
vertical_offset = None
|
vertical_offset = None
|
||||||
if "vertical_offset" in data and data["vertical_offset"] is not None:
|
if "vertical_offset" in data and data["vertical_offset"] is not None:
|
||||||
|
@ -856,7 +863,7 @@ def parse_cords(data, parent, required=False):
|
||||||
y_off = y_off[:-1]
|
y_off = y_off[:-1]
|
||||||
per = True
|
per = True
|
||||||
y_off = check_num(y_off)
|
y_off = check_num(y_off)
|
||||||
error = f"Overlay Error: {parent} vertical_offset: {data['vertical_offset']} must be a number"
|
error = f"{err_type} Error: {parent} vertical_offset: {data['vertical_offset']} must be a number"
|
||||||
if y_off is None:
|
if y_off is None:
|
||||||
raise Failed(error)
|
raise Failed(error)
|
||||||
if vertical_align != "center" and not per and y_off < 0:
|
if vertical_align != "center" and not per and y_off < 0:
|
||||||
|
@ -866,8 +873,10 @@ def parse_cords(data, parent, required=False):
|
||||||
elif vertical_align == "center" and per and (y_off > 50 or y_off < -50):
|
elif vertical_align == "center" and per and (y_off > 50 or y_off < -50):
|
||||||
raise Failed(f"{error} between -50% and 50%")
|
raise Failed(f"{error} between -50% and 50%")
|
||||||
vertical_offset = f"{y_off}%" if per else y_off
|
vertical_offset = f"{y_off}%" if per else y_off
|
||||||
if required and vertical_offset is None:
|
if vertical_offset is None:
|
||||||
raise Failed(f"Overlay Error: {parent} vertical_offset is required")
|
if required:
|
||||||
|
raise Failed(f"{err_type} Error: {parent} vertical_offset is required")
|
||||||
|
vertical_offset = dvo
|
||||||
|
|
||||||
return horizontal_offset, horizontal_align, vertical_offset, vertical_align
|
return horizontal_offset, horizontal_align, vertical_offset, vertical_align
|
||||||
|
|
||||||
|
|