[178] add delete labels

This commit is contained in:
meisnate12 2022-11-01 14:20:00 -04:00
parent 36ef657938
commit 2bf45597b3
14 changed files with 419 additions and 220 deletions

View file

@ -1 +1 @@
1.17.3-develop177
1.17.3-develop178

View file

@ -14,8 +14,8 @@ templates:
exclude_prefix:
- "!"
- "~"
summary: <<collectionless_summary>>
name: <<collectionless_name>>
summary_collectionless: <<collectionless_summary>>
name_collectionless: <<collectionless_name>>
sort_title: ~_Collectionless
collection_order: alpha
url_poster: https://raw.githubusercontent.com/meisnate12/Plex-Meta-Manager-Images/master/collectionless.jpg
@ -25,8 +25,8 @@ templates:
url_poster: <<url_poster>>
collection_order: <<collection_order>>
sort_title: <<sort_title>>
summary: <<summary>>>
name: <<name>>
summary: <<summary_collectionless>>
name: <<name_collectionless>>
plex_collectionless:
exclude_prefix: <<exclude_prefix>>
exclude: <<exclude>>

View file

@ -40,15 +40,15 @@ Note that the `templates_variables:` section only needs to be used if you do wan
**[Shared Collection Variables](../variables) are NOT available to this default file.**
| Variable | Description & Values |
|:-------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | **Description:** Changes the name of the collection.<br>**Values:** New Collection Name |
| `summary` | **Description:** Changes the summary of the collection.<br>**Values:** New Collection Summary |
| `sort_title` | **Description:** Sets the sort title for the collection.<br>**Default:** `~_Collectionless`<br>**Values:** Any String |
| `collection_order` | **Description:** Changes the Collection Order for all collections in this file.<br>**Default:** `alpha`<br>**Values:**<table class="clearTable"><tr><td>`release`</td><td>Order Collection by Release Dates</td></tr><tr><td>`alpha`</td><td>Order Collection Alphabetically</td></tr><tr><td>`custom`</td><td>Order Collection Via the Builder Order</td></tr><tr><td>[Any `plex_search` Sort Option](../../metadata/builders/plex.md#sort-options)</td><td>Order Collection by any `plex_search` Sort Option</td></tr></table> |
| `url_poster` | **Description:** Changes the poster url of thecollection.<br>**Values:** URL directly to the Image |
| `exclude` | **Description:** Exclude these Collections from being considered for collectionless.<br>**Values:** List of Collections |
| `exclude_prefix` | **Description:** Overrides the [default exclude_prefix list](#default-exclude_prefix). Exclude Collections with one of these prefixes from being considered for collectionless.<br>**Default:** [default exclude_prefix list](#default-exclude_prefix)<br>**Values:** List of Prefixes | |
| Variable | Description & Values |
|:-------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name_collectionless` | **Description:** Changes the name of the collection.<br>**Values:** New Collection Name |
| `summary_collectionless` | **Description:** Changes the summary of the collection.<br>**Values:** New Collection Summary |
| `sort_title` | **Description:** Sets the sort title for the collection.<br>**Default:** `~_Collectionless`<br>**Values:** Any String |
| `collection_order` | **Description:** Changes the Collection Order for all collections in this file.<br>**Default:** `alpha`<br>**Values:**<table class="clearTable"><tr><td>`release`</td><td>Order Collection by Release Dates</td></tr><tr><td>`alpha`</td><td>Order Collection Alphabetically</td></tr><tr><td>`custom`</td><td>Order Collection Via the Builder Order</td></tr><tr><td>[Any `plex_search` Sort Option](../../metadata/builders/plex.md#sort-options)</td><td>Order Collection by any `plex_search` Sort Option</td></tr></table> |
| `url_poster` | **Description:** Changes the poster url of thecollection.<br>**Values:** URL directly to the Image |
| `exclude` | **Description:** Exclude these Collections from being considered for collectionless.<br>**Values:** List of Collections |
| `exclude_prefix` | **Description:** Overrides the [default exclude_prefix list](#default-exclude_prefix). Exclude Collections with one of these prefixes from being considered for collectionless.<br>**Default:** [default exclude_prefix list](#default-exclude_prefix)<br>**Values:** List of Prefixes | |
The below is an example config.yml extract with some Template Variables added in to change how the file works.

View file

@ -5,174 +5,304 @@
```yaml
libraries:
Movies:
Movies: # Must match a library name in your Plex
report_path: config/missing/Movies_report.yml
template_variables:
sep_style: gray # use the gray separators globally for this library
collection_mode: hide # hide the collections
language: fr # could be default, de, fr, pt-br or another language code that we have tranlsated
metadata_path:
- pmm: bafta
- pmm: cannes
- pmm: choice
- pmm: golden
- pmm: oscars
- pmm: other_award
- pmm: spirit
- pmm: sundance
- pmm: anilist
- pmm: basic
- pmm: imdb
- pmm: myanimelist
- pmm: other_chart
- pmm: tautulli
- pmm: tmdb
- pmm: trakt
- pmm: actor
- pmm: audio_language
- pmm: content_rating_us # Choose content_rating_uk or content_rating_us
- pmm: genre
- pmm: resolution_standards # Choose resolution_standards or resolution
- pmm: streaming
- pmm: studio
- pmm: subtitle_language
- pmm: year
- pmm: country
- pmm: decade
- pmm: director
- pmm: franchise
- pmm: universe
- pmm: producer
- pmm: seasonal
- pmm: writer
overlay_path:
- remove_overlays: false
- pmm: audio_codec
- pmm: audio_language
- pmm: commonsense
- pmm: direct_play
- pmm: edition
- pmm: episode_info
- pmm: flixpatrol
- pmm: mediastinger
- pmm: ratings
- pmm: separator_award # An "index card"
- pmm: bafta # BAFTA Awards
template_variables: # based on when the award show started
data:
starting: 2014
ending: current_year
- pmm: cannes # Cannes Film Fstical Awards
template_variables: # based on when the award show started
data:
starting: 2016
ending: current_year
- pmm: choice # Critic's Choice Awards
template_variables: # based on when the award show started
data:
starting: 2014
ending: current_year
- pmm: golden # Golden Globes Awards
template_variables: # based on when the award show started
data:
starting: 1943
ending: current_year
- pmm: oscars # The Oscars
template_variables: # based on when the award show started
data:
starting: 1927
ending: current_year
- pmm: other_award # Other award collections
- pmm: spirit # Independent Spirit Awards
template_variables: # based on when the award show started
data:
starting: 2014
ending: current_year
- pmm: sundance # Sundance Film Festival Awards
template_variables: # based on when the award show started
data:
starting: 2010
ending: current_year
- pmm: separator_chart # An "index card"
- pmm: anilist # AniDB Charts (Popular, Trending, etc.)
- pmm: imdb # IMDb Charts (Popular, Trending, etc.)
- pmm: myanimelist # MAL Charts (Popular, Trending, etc.)
- pmm: other_chart # Other Charts (Popular, Trending, etc.)
- pmm: tautulli # Tautulli Charts (Popular, Trending, etc.)
- pmm: tmdb # TMDb Charts (Popular, Trending, etc.)
- pmm: trakt # Trakt Charts (Popular, Trending, etc.)
- pmm: flixpatrol # Flixpatrol Charts (Popular, Trending, etc.)
- pmm: basic # Keep this as the last chart item so that collection_mode: hide works properly on library tab for CHART COLLECTION
- pmm: collectionless # Collectionless movies/shows (Keep this as the last chart item so that collection_mode: hide works properly on library tab for CHART COLLECTION)
- pmm: actor # Actors
template_variables: # bw, rainier, or orig style is used. depth and limit is set low but sometimes I boost to 10, 150
style: bw
data:
depth: 1
limit: 15
- pmm: director # Directors
template_variables: # bw, rainier, or orig style is used. depth and limit is set low but sometimes I boost to 10, 150
style: bw
data:
depth: 1
limit: 15
- pmm: producer # Producers
template_variables: # bw, rainier, or orig style is used. depth and limit is set low but sometimes I boost to 10, 150
exclude: # ever have some random person... you can exclude them if you want
- Jeremy Kleiner
- Thomas Hayslip
style: bw
data:
depth: 1
limit: 15
- pmm: writer # Writers
template_variables: # bw, rainier, or orig style is used. depth and limit is set low but sometimes I boost to 10, 150
style: bw
data:
depth: 1
limit: 15
- pmm: audio_language # English, French, Arabic, German, etc. audio language
- pmm: content_rating_cs # Choose content_rating_uk, content_rating_us, or content_rating_cs
- pmm: genre # Action, Comedy, Drama, etc.
- pmm: resolution # 4K HDR, 1080P FHD, etc. with the standards style
template_variables:
rating1: critic
style: standards
- pmm: studio # DreamWorks Studios, Lucasfilm Ltd, etc.
- pmm: studio_anime # Anime Studios etc.
- pmm: subtitle_language # English, French, Arabic, German, etc. subtitles
- pmm: year # Year the media item was released starting from 1880 to current_year
template_variables:
data:
starting: 1880
ending: current_year
- pmm: country # Country associated to the media item
- pmm: decade # Decade the media item was released
- pmm: seasonal # Christmas, Halloween, etc.
template_variables: # Canadian Thankgsgiving is a different date range. Otherwise, I want to ALWAYS see the seasonal
schedule_independence: daily
schedule_easter: daily
schedule_valentine: daily
schedule_patrick: daily
schedule_thanksgiving: range(10/01-10/31)
schedule_halloween: daily
schedule_christmas: daily
schedule_years: daily
schedule_mother: daily
schedule_memorial: daily
schedule_father: daily
schedule_labor: daily
- pmm: streaming # Streaming on Disney+, Netflix, etc.
- pmm: universe # Marvel Cinematic Universe, Wizarding World, etc.
overlay_path:
- remove_overlays: false # Set to true if you want to remove overlays
- reapply_overlay: false # If you are doing a lot of testing and changes like me, keep this to true to always reapply overlays
- pmm: audio_codec # FLAC, DTS-X, TrueHD, etc.
- pmm: language_count # blank means 1 audio language track, dual means 2, multi means > 2
- pmm: commonsense # Age 2+, Age 14+, etc.
- pmm: flixpatrol # Top 10 flixpatrol for 'this_year', positioned on the left
template_variables:
position: left
time_window: this_year
- pmm: mediastinger # Mediastinger overlay when the media item contains a stinger at the end of the movie/show or during the credits
- pmm: ratings # Ratings with custom fonts matched to the style of the rating, font_size, and on the right in 'square' format
template_variables:
rating1: user
rating1_image: rt_tomato
- pmm: resolution
- pmm: ribbon
- pmm: runtimes
- pmm: special_release
- pmm: streaming
- pmm: versions
- pmm: video_format
TV Shows:
rating2: critic
rating2_image: imdb
rating3: audience
rating3_image: tmdb
horizontal_position: right
- pmm: resolution # 4K HDR, 1080P FHD, etc.
- pmm: ribbon # Used for ribbon in bottom right
- pmm: streaming # Streaming on Disney+, Netflix, etc.
- pmm: versions # Will show duplicates for that media item in top right area
- pmm: video_format # Remux, DVD, Blu-Ray, etc. in bottom left
settings:
asset_directory:
- config/assets
operations:
split_duplicates: false
assets_for_all: false
delete_unmanaged_collections: true # Any manually added collection outside of PMM will be deleted
mass_user_rating_update: mdb_tomatoes # Update user ratings with mdb_tomatoes
mass_critic_rating_update: imdb # Update critic ratings with imdb
mass_audience_rating_update: tmdb # Update audience ratings with tmdb
mass_genre_update: tmdb # Update all genres from tmdb
mass_content_rating_update: mdb_commonsense # Changes Content Rating to "1", "2" etc. to specify appropriate age
mass_originally_available_update: tmdb # Update all original available date from tmdb
mass_imdb_parental_labels: without_none
TV Shows: # Must match a library name in your Plex
report_path: config/missing/TV_missing.yml
template_variables:
sep_style: gray # use the gray separators globally for this library
collection_mode: hide # hide the collections
language: fr # could be default, de, fr, pt-br or another lang code that we have tranlsated
metadata_path:
- pmm: choice
- pmm: golden
- pmm: emmy
- pmm: anilist
- pmm: basic
- pmm: imdb
- pmm: myanimelist
- pmm: other_chart
- pmm: tautulli
- pmm: tmdb
- pmm: trakt
- pmm: actor
- pmm: audio_language
- pmm: content_rating_us # Choose content_rating_uk or content_rating_us
- pmm: genre
- pmm: resolution_standards # Choose resolution_standards or resolution
- pmm: streaming
- pmm: studio
- pmm: subtitle_language
- pmm: year
- pmm: country
- pmm: decade
- pmm: network
- pmm: separator_award # An "index card"
- pmm: choice # Critic's Choice Awards
template_variables: # based on when the award show started
data:
starting: 2014
ending: current_year
- pmm: golden # Golden Globes Awards
template_variables: # based on when the award show started
data:
starting: 1943
ending: current_year
- pmm: emmy # Emmy Awards
template_variables: # based on when the award show started
data:
starting: 1947
ending: current_year
- pmm: separator_chart # An "index card"
- pmm: anilist # AniDB Charts (Popular, Trending, etc.)
- pmm: imdb # IMDb Charts (Popular, Trending, etc.)
- pmm: myanimelist # MAL Charts (Popular, Trending, etc.)
- pmm: other_chart # Other Charts (Popular, Trending, etc.)
- pmm: tautulli # Tautulli Charts (Popular, Trending, etc.)
- pmm: tmdb # TMDb Charts (Popular, Trending, etc.)
- pmm: trakt # Trakt Charts (Popular, Trending, etc.)
- pmm: flixpatrol # Flixpatrol Charts (Popular, Trending, etc.)
- pmm: basic # Keep this as the last chart item so that collection_mode: hide works properly on library tab for CHART COLLECTION
- pmm: collectionless # Collectionless movies/shows (Keep this as the last chart item so that collection_mode: hide works properly on library tab for CHART COLLECTION)
- pmm: actor # Actors
template_variables: # bw, rainier, or orig style is used. depth and limit is set low but sometimes I boost to 10, 150
exclude: # ever have some random person... you can exclude them if you want
- Macy Nyman
style: bw
data:
depth: 1
limit: 15
- pmm: audio_language # English, French, Arabic, German, etc. audio language
- pmm: content_rating_cs # Choose content_rating_uk, content_rating_us, or content_rating_cs
- pmm: genre # Action, Comedy, Drama, etc.
- pmm: resolution # 4K HDR, 1080P FHD, etc. with the standards style
template_variables:
style: standards
- pmm: studio # DreamWorks Studios, Lucasfilm Ltd, etc.
- pmm: studio_anime # Anime Studios etc.
- pmm: subtitle_language # English, French, Arabic, German, etc. subtitles
- pmm: year # Year the media item was released starting from 1880 to current_year
template_variables:
data:
starting: 1880
ending: current_year
- pmm: country # Country associated to the media item
- pmm: decade # Decade the media item was released
- pmm: network # ABC, CBC, NBC, FOX, etc.
- pmm: streaming # Streaming on Disney+, Netflix, etc.
overlay_path:
- remove_overlays: false
- pmm: audio_codec
- remove_overlays: false # Set to true if you want to remove overlays
- reapply_overlay: false # If you are doing a lot of testing and changes like me, keep this to true to always reapply overlays
- pmm: audio_codec # FLAC, DTS-X, TrueHD, etc. and works with overlay_level show, episode, and season
- pmm: audio_codec
template_variables:
overlay_level: episode
- pmm: audio_codec
template_variables:
overlay_level: season
- pmm: audio_language
- pmm: audio_language
- pmm: language_count # blank means 1 audio language track, dual means 2, multi means > 2 and works with overlay_level show, episode, and season
- pmm: language_count
template_variables:
overlay_level: episode
- pmm: audio_language
- pmm: language_count
template_variables:
overlay_level: season
- pmm: commonsense
- pmm: commonsense # Age 2+, Age 14+, etc. and works with overlay_level show, episode, and season
- pmm: commonsense
template_variables:
overlay_level: episode
- pmm: commonsense
template_variables:
overlay_level: season
- pmm: direct_play
- pmm: direct_play
- pmm: episode_info # SE##E## information in bottom right and works with overlay_level episode
template_variables:
overlay_level: episode
- pmm: direct_play
- pmm: flixpatrol # Top 10 flixpatrol for 'this_year', positioned on the left and works with overlay_level show
template_variables:
overlay_level: season
- pmm: edition
- pmm: edition
position: left
time_window: this_year
- pmm: mediastinger # Mediastinger overlay when the media item contains a stinger at the end of the movie/show or during the credits and works with overlay_level show
- pmm: ratings # Ratings with custom fonts matched to the style of the rating, font_size, and on the right in 'square' format. overlay_level: show has 3 ratings max
template_variables:
overlay_level: episode
- pmm: episode_info
template_variables:
overlay_level: episode
- pmm: flixpatrol
- pmm: flixpatrol
template_variables:
overlay_level: episode
- pmm: flixpatrol
template_variables:
overlay_level: season
- pmm: mediastinger
- pmm: mediastinger
template_variables:
overlay_level: episode
- pmm: mediastinger
template_variables:
overlay_level: season
- pmm: ratings
template_variables:
rating2: audience
rating1: user
rating1_image: rt_tomato
rating2: critic
rating2_image: imdb
- pmm: ratings
rating3: audience
rating3_image: tmdb
horizontal_position: right
- pmm: ratings # Ratings with custom fonts matched to the style of the rating, font_size, and on the right in 'square' format. overlay_level: episode has 2 ratings max
template_variables:
rating1: critic
rating1_image: imdb
rating2: audience
rating2_image: imdb
rating2_image: tmdb
horizontal_position: right
overlay_level: episode
- pmm: resolution
- pmm: resolution # 4K HDR, 1080P FHD, etc. and works with overlay_level show, episode, and season
- pmm: resolution
template_variables:
overlay_level: episode
- pmm: resolution
template_variables:
overlay_level: season
- pmm: ribbon
- pmm: ribbon
template_variables:
overlay_level: episode
- pmm: ribbon # Used for ribbon in bottom right and works with overlay_level show and season
- pmm: ribbon
template_variables:
overlay_level: season
- pmm: runtimes
- pmm: episode_info # Runtime information in bottom right and works with overlay_level episode
template_variables:
overlay_level: episode
- pmm: special_release
- pmm: special_release
template_variables:
overlay_level: episode
- pmm: special_release
template_variables:
overlay_level: season
- pmm: status # Airing, Returning, Ended, Canceled and works with overlay_level show
- pmm: streaming # Streaming on Disney+, Netflix, etc. and works with overlay_level show, episode, and season
- pmm: streaming
- pmm: versions
- pmm: versions
template_variables:
overlay_level: episode
- pmm: streaming
template_variables:
overlay_level: season
- pmm: versions # Will show duplicates for that media item in top right area and works with overlay_level show, episode, and season
template_variables:
overlay_level: episode
- pmm: versions
@ -181,14 +311,33 @@ libraries:
- pmm: versions
template_variables:
overlay_level: show
- pmm: video_format
- pmm: video_format # Remux, DVD, Blu-Ray, etc. in bottom left and works with overlay_level show, episode, and season
- pmm: video_format
template_variables:
overlay_level: episode
- pmm: video_format
template_variables:
overlay_level: season
settings:
asset_directory:
- config/assets
operations:
split_duplicates: false
assets_for_all: false
delete_unmanaged_collections: true # Any manually added collection outside of PMM will be deleted
mass_user_rating_update: mdb_tomatoes # Update user ratings with mdb_tomatoes
mass_critic_rating_update: imdb # Update critic ratings with imdb
mass_audience_rating_update: tmdb # Update audience ratings with tmdb
mass_genre_update: tmdb # Update all genres from tmdb
mass_content_rating_update: mdb_commonsense # Changes Content Rating to "1", "2" etc. to specify appropriate age
mass_originally_available_update: tmdb # Update all original available date from tmdb
mass_episode_critic_rating_update: imdb # Update critic ratings with imdb for episodes
mass_episode_audience_rating_update: tmdb # Update audience ratings with tmdb for episodes
mass_imdb_parental_labels: without_none
playlist_files:
- pmm: playlist
template_variables:
libraries: Movies, TV Shows
```
</details>

View file

@ -130,9 +130,7 @@ All [Shared Overlay Variables](variables) are available with the default values
| Variable | Description & Values |
|:------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `position` | **Description:** Changes the position of all Overlay Queues in this File.<br>**Default:** `left`<br>**Values:** `left`, `right`, `half`, or List of Coordinates |
| `position_audio_flags` | **Description:** Changes the position of the audio flags Overlays.<br>**Default:** `left`<br>**Values:** `left`, `right`, `half`, or List of Coordinates |
| `position_subtitle_flags` | **Description:** Changes the position of the subtitle flags Overlays.<br>**Default:** `left`<br>**Values:** `left`, `right`, `half`, or List of Coordinates |
| `position` | **Description:** Use the Custom Given Queue instead of the the provided Queues.<br>**Values:** List of Coordinates |
| `horizontal_position` | **Description:** Choose the horizontal position for the flag group.<br>**Default:** `left`<br>**Values:** `left`, `right`, or `center` |
| `vertical_position` | **Description:** Choose the vertical position for the flag group.<br>**Default:** `top`<br>**Values:** `top`, `bottom`, or `center` |
| `flag_alignment` | **Description:** Choose the display alignment for the flag group.<br>**Default:** `vertical`<br>**Values:** `horizontal`, or `vertical` |

View file

@ -1,6 +1,6 @@
# Ratings Overlays
The `ratings` Default Overlay File is used to create an overlay based on if there's an after credit scene on each item within your library.
The `ratings` Default Overlay File is used to create an overlay based on the Critic Rating, Audience Rating, and User Rating in Plex for each item within your library.
This file only updates the overlays based on the data in Plex. Use the [Mass * Rating Update Library Operation](../../config/operations.md#mass--rating-update) and the [Mass Episode * Rating Update Library Operation](../../config/operations.md#mass-episode--rating-update) to update Plex to the Ratings you want on the Overlay.

View file

@ -9,10 +9,10 @@ Below are the available variables which can be used to customize the file.
| Variable | Description & Values |
|:--------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `use_<<key>>`<sup>1</sup> | **Description:** Turns off individual Overlays in a Defaults file.<br>**Values:** `false` to turn off the overlay |
| `file` | **Description:** Controls the image associated with the Overlay to a local file. Use `pmm: null` with this to no use the default image.<br>**Values:** Filepath to Overlay Image |
| `url` | **Description:** Controls the image associated with the Overlay to a url. Use `pmm: null` with this to no use the default image.<br>**Values:** URL to Overlay Image |
| `git` | **Description:** Controls the image associated with the Overlay to the git repo. Use `pmm: null` with this to no use the default image.<br>**Values:** Git Path to Overlay Image |
| `repo` | **Description:** Controls the image associated with the Overlay to a custom repo. Use `pmm: null` with this to no use the default image.<br>**Values:** Repo Path to Overlay Image |
| `file` | **Description:** Controls the image associated with the Overlay to a local file. Use `pmm: null` with this to not use the default image.<br>**Values:** Filepath to Overlay Image |
| `url` | **Description:** Controls the image associated with the Overlay to a url. Use `pmm: null` with this to not use the default image.<br>**Values:** URL to Overlay Image |
| `git` | **Description:** Controls the image associated with the Overlay to the git repo. Use `pmm: null` with this to not use the default image.<br>**Values:** Git Path to Overlay Image |
| `repo` | **Description:** Controls the image associated with the Overlay to a custom repo. Use `pmm: null` with this to not use the default image.<br>**Values:** Repo Path to Overlay Image |
| `pmm` | **Description:** Controls the image associated with the Overlay to a pmm file.<br>**Values:** PMM Overlay Image |
| `horizontal_offset` | **Description:** Controls the Horizontal 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` |

View file

@ -27,6 +27,7 @@ These docs are assuming you have a basic understanding of Docker concepts. One
| [Ignore Ghost](#ignore-ghost) | `-ig` or `--ignore-ghost` | `PMM_IGNORE_GHOST` |
| [Cache Libraries](#cache-libraries) | `-ca` or `--cache-libraries` | `PMM_CACHE_LIBRARIES` |
| [Delete Collections](#delete-collections) | `-dc` or `--delete-collections` | `PMM_DELETE_COLLECTIONS` |
| [Delete Labels](#delete-labels) | `-dl` or `--delete-labels` | `PMM_DELETE_LABELS` |
| [Resume Run](#resume-run) | `-re` or `--resume` | `PMM_RESUME` |
| [No Countdown](#no-countdown) | `-nc` or `--no-countdown` | `PMM_NO_COUNTDOWN` |
| [No Missing](#no-missing) | `-nm` or `--no-missing` | `PMM_NO_MISSING` |
@ -815,6 +816,45 @@ docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex
</details>
### Delete Labels
Delete all labels on every item in a Library prior to running collections/operations.
<table class="dualTable colwidths-auto align-default table">
<tr>
<th style="background-color: #222;"></th>
<th>Shell</th>
<th>Environment</th>
</tr>
<tr>
<th>Flags</th>
<td><code>-dl</code> or <code>--delete-labels</code></td>
<td><code>PMM_DELETE_LABELS</code></td>
</tr>
<tr>
<th>Example</th>
<td><code>--delete-labels</code></td>
<td><code>PMM_DELETE_LABELS=true</code></td>
</tr>
</table>
<details>
<summary>Local Environment</summary>
```shell
python plex_meta_manager.py --delete-labels
```
</details>
<details>
<summary>Docker Environment</summary>
```shell
docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex-meta-manager --delete-labels
```
</details>
### Resume Run
Perform a resume run immediately resuming from the first instance of the specified collection, bypassing the time to run flag.

View file

@ -415,6 +415,30 @@ class CollectionBuilder:
else:
server_check = pl_library.PlexServer.machineIdentifier
self.smart_filter_details = ""
self.smart_label = {"sort_by": "random", "all": {"label": [self.name]}}
self.smart_label_collection = False
if "smart_label" in methods and not self.playlist and not self.overlay and not self.library.is_music:
logger.debug("")
logger.debug("Validating Method: smart_label")
self.smart_label_collection = True
if not self.data[methods["smart_label"]]:
logger.warning(f"{self.Type} Error: smart_label attribute is blank defaulting to random")
else:
logger.debug(f"Value: {self.data[methods['smart_label']]}")
if isinstance(self.data[methods["smart_label"]], dict):
_data, replaced = util.replace_label(self.name, self.data[methods["smart_label"]])
if not replaced:
raise Failed("Config Error: <<smart_label>> not found in the smart_label attribute data")
self.smart_label = _data
elif (self.library.is_movie and str(self.data[methods["smart_label"]]).lower() in plex.movie_sorts) \
or (self.library.is_show and str(self.data[methods["smart_label"]]).lower() in plex.show_sorts):
self.smart_label["sort_by"] = str(self.data[methods["smart_label"]]).lower()
else:
logger.warning(f"{self.Type} Error: smart_label attribute: {self.data[methods['smart_label']]} is invalid defaulting to random")
if self.smart_label_collection and self.library.smart_label_check(self.name):
_, self.smart_filter_details, _ = self.build_filter("smart_label", self.smart_label, default_sort="random")
if "delete_not_scheduled" in methods and not self.overlay:
logger.debug("")
logger.debug("Validating Method: delete_not_scheduled")
@ -535,30 +559,6 @@ class CollectionBuilder:
else:
raise Failed(f"{self.Type} Error: No valid TMDb Person IDs in {self.data[methods['tmdb_person']]}")
self.smart_filter_details = ""
self.smart_label = {"sort_by": "random", "all": {"label": [self.name]}}
self.smart_label_collection = False
if "smart_label" in methods and not self.playlist and not self.overlay and not self.library.is_music:
logger.debug("")
logger.debug("Validating Method: smart_label")
self.smart_label_collection = True
if not self.data[methods["smart_label"]]:
logger.warning(f"{self.Type} Error: smart_label attribute is blank defaulting to random")
else:
logger.debug(f"Value: {self.data[methods['smart_label']]}")
if isinstance(self.data[methods["smart_label"]], dict):
_data, replaced = util.replace_label(self.name, self.data[methods["smart_label"]])
if not replaced:
raise Failed("Config Error: <<smart_label>> not found in the smart_label attribute data")
self.smart_label = _data
elif (self.library.is_movie and str(self.data[methods["smart_label"]]).lower() in plex.movie_sorts) \
or (self.library.is_show and str(self.data[methods["smart_label"]]).lower() in plex.show_sorts):
self.smart_label["sort_by"] = str(self.data[methods["smart_label"]]).lower()
else:
logger.warning(f"{self.Type} Error: smart_label attribute: {self.data[methods['smart_label']]} is invalid defaulting to random")
if self.smart_label_collection and self.library.smart_label_check(self.name):
_, self.smart_filter_details, _ = self.build_filter("smart_label", self.smart_label, default_sort="random")
self.smart_url = None
self.smart_type_key = None
if "smart_url" in methods and not self.playlist and not self.overlay:
@ -767,7 +767,7 @@ class CollectionBuilder:
self.sonarr_details["add_missing"] = self.library.Sonarr.add_missing if self.library.Sonarr else False
if "add_existing" not in self.sonarr_details:
self.sonarr_details["add_existing"] = self.library.Sonarr.add_existing if self.library.Sonarr else False
if self.smart_url or self.collectionless or self.library.is_music:
self.radarr_details["add_missing"] = False
self.radarr_details["add_existing"] = False
@ -2725,6 +2725,9 @@ class CollectionBuilder:
output += f"\n{self.Type} {'deleted' if self.obj else 'not found'} on {self.library.account.username}"
elif self.obj:
output = f"{self.Type} {self.obj.title} deleted"
if self.smart_label_collection:
for item in self.library.search(label=self.name, libtype=self.builder_level):
self.library.edit_tags("label", item, remove_tags=self.name)
else:
output = ""
if self.obj:

View file

@ -103,17 +103,12 @@ class ConfigFile:
self.version = attrs["version"] if "version" in attrs else None
self.no_missing = attrs["no_missing"] if "no_missing" in attrs else None
self.no_report = attrs["no_report"] if "no_report" in attrs else None
self.test_mode = attrs["test"] if "test" in attrs else False
self.trace_mode = attrs["trace"] if "trace" in attrs else False
self.delete_collections = attrs["delete"] if "delete" in attrs else False
self.ignore_schedules = attrs["ignore_schedules"] if "ignore_schedules" in attrs else False
self.library_first = attrs["library_first"] if "library_first" in attrs else False
self.start_time = attrs["time_obj"]
self.run_hour = datetime.strptime(attrs["time"], "%H:%M").hour
self.requested_collections = util.get_list(attrs["collections"]) if "collections" in attrs else None
self.requested_libraries = util.get_list(attrs["libraries"]) if "libraries" in attrs else None
self.requested_metadata_files = [mf[:-4] if str(mf).endswith(".yml") else mf for mf in util.get_list(attrs["metadata_files"])] if "metadata_files" in attrs and attrs["metadata_files"] else None
self.resume_from = attrs["resume"] if "resume" in attrs else None
self.collection_only = attrs["collection_only"] if "collection_only" in attrs else False
self.operations_only = attrs["operations_only"] if "operations_only" in attrs else False
self.overlays_only = attrs["overlays_only"] if "overlays_only" in attrs else False

View file

@ -18,8 +18,7 @@ class Library(ABC):
self.Notifiarr = None
self.collections = []
self.metadatas = []
self.queues = {}
self.queue_current = 0
self.queue_names = []
self.metadata_files = []
self.overlay_files = []
self.overlay_names = []
@ -150,11 +149,9 @@ class Library(ABC):
if not operations_only and not collection_only:
for file_type, overlay_file, temp_vars, asset_directory in self.overlay_path:
try:
overlay_obj = OverlayFile(self.config, self, file_type, overlay_file, temp_vars, asset_directory, self.queue_current)
overlay_obj = OverlayFile(self.config, self, file_type, overlay_file, temp_vars, asset_directory)
self.overlay_files.append(overlay_obj)
for qk, qv in overlay_obj.queues.items():
self.queues[self.queue_current] = qv
self.queue_current += 1
self.queue_names.extend([q for q in overlay_obj.queues])
except Failed as e:
logger.error(e)
logger.info(f"Overlay File Failed To Load")

View file

@ -184,13 +184,14 @@ class Mdblist:
logger.info(f"Limit: {data['limit']} items")
params["limit"] = data["limit"]
parsed_url = urlparse(data["url"])
url_base = parsed_url._replace(query=None).geturl()
url_base = str(parsed_url._replace(query=None).geturl())
url_base = url_base if url_base.endswith("/") else f"{url_base}/"
url_base = url_base if url_base.endswith("json/") else f"{url_base}json/"
try:
response = self.config.get_json(url_base, headers=headers, params=params)
if "error" in response:
if response["error"] == "empty":
if (isinstance(response, dict) and "error" in response) or (isinstance(response, list) and response and "error" in response[0]):
err = response["error"] if isinstance(response, dict) else response[0]["error"]
if "empty" in err:
raise Failed(f"Mdblist Error: No Items Returned. Lists can take 24 hours to update so try again later.")
raise Failed(f"Mdblist Error: Invalid Response {response}")
results = []

View file

@ -1479,7 +1479,6 @@ class Plex(Library):
if failures > failure_threshold:
return False
elif filter_attr in builder.number_filters or modifier in [".gt", ".gte", ".lt", ".lte", ".count_gt", ".count_gte", ".count_lt", ".count_lte"]:
divider = 60000 if filter_attr == "duration" else 1
test_number = []
if filter_attr in ["channels", "height", "width", "aspect"]:
test_number = 0
@ -1497,12 +1496,14 @@ class Plex(Library):
for media in item.media:
for part in media.parts:
test_number.extend([s.language for s in part.subtitleStreams()])
elif filter_attr == "duration":
test_number = getattr(item, filter_actual) / 60000
else:
test_number = getattr(item, filter_actual)
if modifier in [".count_gt", ".count_gte", ".count_lt", ".count_lte"]:
test_number = len(test_number) if test_number else 0
modifier = f".{modifier[7:]}"
if test_number is None or util.is_number_filter(test_number / divider, modifier, filter_data):
if test_number is None or util.is_number_filter(test_number, modifier, filter_data):
return False
else:
attrs = []

View file

@ -37,7 +37,8 @@ parser.add_argument("-rc", "-cl", "--collection", "--collections", "--run-collec
parser.add_argument("-rl", "-l", "--library", "--libraries", "--run-library", "--run-libraries", dest="libraries", help="Process only specified libraries (comma-separated list)", type=str)
parser.add_argument("-rm", "-m", "--metadata", "--metadata-files", "--run-metadata-files", dest="metadata", help="Process only specified Metadata files (comma-separated list)", type=str)
parser.add_argument("-ca", "--cache-library", "--cache-libraries", dest="cache_libraries", help="Cache Library load for 1 day", action="store_true", default=False)
parser.add_argument("-dc", "--delete", "--delete-collections", dest="delete", help="Deletes all Collections in the Plex Library before running", action="store_true", default=False)
parser.add_argument("-dc", "--delete", "--delete-collections", dest="delete_collections", help="Deletes all Collections in the Plex Library before running", action="store_true", default=False)
parser.add_argument("-dl", "--delete-label", "--delete-labels", dest="delete_labels", help="Deletes all Labels in the Plex Library before running", action="store_true", default=False)
parser.add_argument("-nc", "--no-countdown", dest="no_countdown", help="Run without displaying the countdown", action="store_true", default=False)
parser.add_argument("-nm", "--no-missing", dest="no_missing", help="Run without running the missing section", action="store_true", default=False)
parser.add_argument("-nr", "--no-report", dest="no_report", help="Run without saving a report", action="store_true", default=False)
@ -86,7 +87,8 @@ collections = get_arg("PMM_COLLECTIONS", args.collections)
libraries = get_arg("PMM_LIBRARIES", args.libraries)
metadata_files = get_arg("PMM_METADATA_FILES", args.metadata)
cache_libraries = get_arg("PMM_CACHE_LIBRARIES", args.cache_libraries, arg_bool=True)
delete = get_arg("PMM_DELETE_COLLECTIONS", args.delete, arg_bool=True)
delete_collections = get_arg("PMM_DELETE_COLLECTIONS", args.delete_collections, arg_bool=True)
delete_labels = get_arg("PMM_DELETE_LABELS", args.delete_labels, arg_bool=True)
resume = get_arg("PMM_RESUME", args.resume)
no_countdown = get_arg("PMM_NO_COUNTDOWN", args.no_countdown, arg_bool=True)
no_missing = get_arg("PMM_NO_MISSING", args.no_missing, arg_bool=True)
@ -218,7 +220,8 @@ def start(attrs):
logger.debug(f"--ignore-schedules (PMM_IGNORE_SCHEDULES): {ignore_schedules}")
logger.debug(f"--ignore-ghost (PMM_IGNORE_GHOST): {ignore_ghost}")
logger.debug(f"--cache-libraries (PMM_CACHE_LIBRARIES): {cache_libraries}")
logger.debug(f"--delete-collections (PMM_DELETE_COLLECTIONS): {delete}")
logger.debug(f"--delete-collections (PMM_DELETE_COLLECTIONS): {delete_collections}")
logger.debug(f"--delete-labels (PMM_DELETE_LABELS): {delete_labels}")
logger.debug(f"--resume (PMM_RESUME): {resume}")
logger.debug(f"--no-countdown (PMM_NO_COUNTDOWN): {no_countdown}")
logger.debug(f"--no-missing (PMM_NO_MISSING): {no_missing}")
@ -446,7 +449,7 @@ def run_libraries(config):
logger.debug(f"Optimize: {library.optimize}")
logger.debug(f"Timeout: {library.timeout}")
if config.delete_collections and not playlist_only:
if delete_collections and not playlist_only:
time_start = datetime.now()
logger.info("")
logger.separator(f"Deleting all Collections from the {library.name} Library", space=False, border=False)
@ -459,6 +462,25 @@ def run_libraries(config):
logger.error(f"Collection {collection.title} Failed to Delete")
library_status[library.name]["All Collections Deleted"] = str(datetime.now() - time_start).split('.')[0]
if delete_labels and not playlist_only:
time_start = datetime.now()
logger.info("")
logger.separator(f"Deleting all Labels from All items in the {library.name} Library", space=False, border=False)
logger.info("")
if library.is_show:
library_types = ["show", "season", "episode"]
elif library.is_music:
library_types = ["artist", "album", "track"]
else:
library_types = ["movie"]
for library_type in library_types:
for item in library.get_all(builder_level=library_type):
try:
library.edit_tags("label", item, sync_tags=[])
except NotFound:
logger.error(f"{item.title[:25]:<25} | Labels Failed to be Removed")
library_status[library.name]["All Labels Deleted"] = str(datetime.now() - time_start).split('.')[0]
time_start = datetime.now()
temp_items = None
list_key = None
@ -484,13 +506,13 @@ def run_libraries(config):
library_status[library.name]["Library Loading and Mapping"] = str(datetime.now() - time_start).split('.')[0]
def run_operations_and_overlays():
if not config.test_mode and not collection_only and not playlist_only and not config.requested_metadata_files:
if not test and not collection_only and not playlist_only and not config.requested_metadata_files:
if not overlays_only and library.library_operation:
library_status[library.name]["Library Operations"] = library.Operations.run_operations()
if not operations_only and (library.overlay_files or library.remove_overlays):
library_status[library.name]["Library Overlays"] = library.Overlays.run_overlays()
if config.library_first:
if library_first:
run_operations_and_overlays()
if not operations_only and not overlays_only and not playlist_only:
@ -503,26 +525,26 @@ def run_libraries(config):
continue
logger.info("")
logger.separator(f"Running {metadata_name} Metadata File\n{metadata.path}")
if not config.test_mode and not config.resume_from and not collection_only:
if not test and not resume and not collection_only:
try:
metadata.update_metadata()
except Failed as e:
library.notify(e)
logger.error(e)
collections_to_run = metadata.get_collections(config.requested_collections)
if config.resume_from and config.resume_from not in collections_to_run:
if resume and resume not in collections_to_run:
logger.info("")
logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
logger.warning(f"Collection: {resume} not in Metadata File: {metadata.path}")
continue
if collections_to_run:
logger.info("")
logger.separator(f"{'Test ' if config.test_mode else ''}Collections")
logger.separator(f"{'Test ' if test else ''}Collections")
logger.remove_library_handler(library.mapping_name)
run_collection(config, library, metadata, collections_to_run)
logger.re_add_library_handler(library.mapping_name)
library_status[library.name]["Library Metadata Files"] = str(datetime.now() - time_start).split('.')[0]
if not config.library_first:
if not library_first:
run_operations_and_overlays()
logger.remove_library_handler(library.mapping_name)
@ -536,7 +558,7 @@ def run_collection(config, library, metadata, requested_collections):
logger.info("")
for mapping_name, collection_attrs in requested_collections.items():
collection_start = datetime.now()
if config.test_mode and ("test" not in collection_attrs or collection_attrs["test"] is not True):
if test and ("test" not in collection_attrs or collection_attrs["test"] is not True):
no_template_test = True
if "template" in collection_attrs and collection_attrs["template"]:
for data_template in util.get_list(collection_attrs["template"], split=False):
@ -551,10 +573,10 @@ def run_collection(config, library, metadata, requested_collections):
if no_template_test:
continue
if config.resume_from and config.resume_from != mapping_name:
if resume and resume != mapping_name:
continue
elif config.resume_from == mapping_name:
config.resume_from = None
elif resume == mapping_name:
resume = None
logger.info("")
logger.separator(f"Resuming Collections")
@ -723,7 +745,7 @@ def run_playlists(config):
for playlist_file in config.playlist_files:
for mapping_name, playlist_attrs in playlist_file.playlists.items():
playlist_start = datetime.now()
if config.test_mode and ("test" not in playlist_attrs or playlist_attrs["test"] is not True):
if test and ("test" not in playlist_attrs or playlist_attrs["test"] is not True):
no_template_test = True
if "template" in playlist_attrs and playlist_attrs["template"]:
for data_template in util.get_list(playlist_attrs["template"], split=False):
@ -896,19 +918,12 @@ def run_playlists(config):
if __name__ == "__main__":
try:
params = {"config_file": config_file, "ignore_schedules": ignore_schedules}
if run or test or collections or libraries or metadata_files or resume:
process({
"config_file": config_file,
"test": test,
"delete": delete,
"ignore_schedules": ignore_schedules,
"collections": collections,
"libraries": libraries,
"metadata_files": metadata_files,
"library_first": library_first,
"resume": resume,
"trace": trace
})
params["collections"] = collections
params["libraries"] = libraries
params["metadata_files"] = metadata_files
process(params)
else:
times_to_run = util.get_list(times)
valid_times = []
@ -921,7 +936,7 @@ if __name__ == "__main__":
else:
raise Failed(f"Argument Error: blank time argument")
for time_to_run in valid_times:
schedule.every().day.at(time_to_run).do(process, {"config_file": config_file, "time": time_to_run, "delete": delete, "library_first": library_first, "trace": trace})
schedule.every().day.at(time_to_run).do(process, params)
while True:
schedule.run_pending()
if not no_countdown: