mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
[15] add director, producer, writer dynamic collections
This commit is contained in:
parent
248ab974a5
commit
92fa57e060
8 changed files with 269 additions and 44 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.16.0-develop14
|
||||
1.16.0-develop15
|
||||
|
|
|
@ -36,7 +36,7 @@ The available setting attributes which can be set at each level are outlined bel
|
|||
| [`default_collection_order`](#default-collection-order) | ✅ | ✅ | ❌ |
|
||||
| [`minimum_items`](#minimum-items) | ✅ | ✅ | ✅ |
|
||||
| [`delete_below_minimum`](#delete-below-minimum) | ✅ | ✅ | ✅ |
|
||||
| [`delete_not_scheduled`d](#delete-not-scheduled) | ✅ | ✅ | ✅ |
|
||||
| [`delete_not_scheduled`](#delete-not-scheduled) | ✅ | ✅ | ✅ |
|
||||
| [`run_again_delay`](#run-again-delay) | ✅ | ❌ | ❌ |
|
||||
| [`missing_only_released`](#missing-only-released) | ✅ | ✅ | ✅ |
|
||||
| [`show_unmanaged`](#show-unmanaged-collections) | ✅ | ✅ | ❌ |
|
||||
|
|
|
@ -104,6 +104,7 @@ This will display a full path:
|
|||
<details>
|
||||
<summary>OS X</summary>
|
||||
<br />
|
||||
|
||||
```
|
||||
/Users/YOURUSERNAME/plex-meta-manager
|
||||
```
|
||||
|
@ -112,6 +113,7 @@ This will display a full path:
|
|||
<details>
|
||||
<summary>Linux</summary>
|
||||
<br />
|
||||
|
||||
```
|
||||
/home/YOURUSERNAME/plex-meta-manager
|
||||
```
|
||||
|
@ -120,6 +122,7 @@ This will display a full path:
|
|||
<details>
|
||||
<summary>Windows</summary>
|
||||
<br />
|
||||
|
||||
```
|
||||
C:\Users\YOURUSERNAME\plex-meta-manager
|
||||
```
|
||||
|
|
|
@ -67,12 +67,12 @@ dynamic_collections:
|
|||
dynamic_collections:
|
||||
networks:
|
||||
type: network
|
||||
addons:
|
||||
MTV Worldwide:
|
||||
- MTV
|
||||
- MTV2
|
||||
- MTV3
|
||||
- MTV (UK)
|
||||
addons:
|
||||
MTV Worldwide:
|
||||
- MTV
|
||||
- MTV2
|
||||
- MTV3
|
||||
- MTV (UK)
|
||||
```
|
||||
|
||||
## Attributes
|
||||
|
@ -110,6 +110,9 @@ Depending on the `type` of dynamic collection, `data` is used to specify the opt
|
|||
| [`trakt_liked_lists`](#trakt-liked-lists) | Create a collection for each list the authenticated trakt user likes | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
| [`trakt_people_list`](#trakt-people-lists) | Create a collection for each actor found in the trakt list | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| [`actor`](#actor) | Create a collection for each actor found in the library | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| [`director`](#director) | Create a collection for each director found in the library | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| [`writer`](#writer) | Create a collection for each writer found in the library | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| [`producer`](#producer) | Create a collection for each producer found in the library | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| [`genre`](#genre) | Create a collection for each genre found in the library | ❌ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [`year`](#year) | Create a collection for each year found in the library | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
| [`decade`](#decade) | Create a collection for each decade found in the library | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
|
@ -434,17 +437,17 @@ Create a collection for each actor found in the library.
|
|||
<th>Description & Values</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>actor_depth</code></td>
|
||||
<td><code>depth</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>actor_minimum</code></td>
|
||||
<td><code>minimum</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>number_of_actors</code></td>
|
||||
<td><code>limit</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> None</td>
|
||||
</tr>
|
||||
|
@ -479,9 +482,210 @@ default_template:
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
* `actor_depth` determines how many top billed actor per item they are in. (i.e. if they play a cameo role, this is unlikely to be counted)
|
||||
* `actor_minimum` determines the minimum number of times the actor must appear within `actor_depth` for the collection to be created.
|
||||
* `number_of_actors` determines the number of actor collection to max out at. (i.e. if to make collections for the top 25 actors)
|
||||
* `depth` determines how many top billed actor per item they are in. (i.e. if they play a cameo role, this is unlikely to be counted)
|
||||
* `minimum` determines the minimum number of times the actor must appear within `depth` for the collection to be created.
|
||||
* `limit` determines the number of actor collection to max out at. (i.e. if to make collections for the top 25 actors)
|
||||
|
||||
### Director
|
||||
|
||||
Create a collection for each director found in the library.
|
||||
|
||||
<table class="dualTable colwidths-auto align-default table">
|
||||
<tr>
|
||||
<th><code>type</code> Option</th>
|
||||
<td><code>director</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>data</code> Values</th>
|
||||
<td>
|
||||
<table class="clearTable">
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Description & Values</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>depth</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>minimum</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> None</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Keys</th>
|
||||
<td>TMDb Person ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Titles</th>
|
||||
<td>TMDb Person Name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default <code>title_format</code></th>
|
||||
<td><code><<title>></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default Template</th>
|
||||
<td>
|
||||
|
||||
```yaml
|
||||
default_template:
|
||||
tmdb_person: <<director>>
|
||||
plex_search:
|
||||
all:
|
||||
director: tmdb
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
* `depth` determines how many directors are looked at per item.
|
||||
* `minimum` determines the minimum number of times the director must appear within `depth` for the collection to be created.
|
||||
* `limit` determines the number of director collection to max out at. (i.e. if to make collections for the top 25 directors)
|
||||
|
||||
### Writer
|
||||
|
||||
Create a collection for each writer found in the library.
|
||||
|
||||
<table class="dualTable colwidths-auto align-default table">
|
||||
<tr>
|
||||
<th><code>type</code> Option</th>
|
||||
<td><code>writer</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>data</code> Values</th>
|
||||
<td>
|
||||
<table class="clearTable">
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Description & Values</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>depth</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>minimum</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> None</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Keys</th>
|
||||
<td>TMDb Person ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Titles</th>
|
||||
<td>TMDb Person Name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default <code>title_format</code></th>
|
||||
<td><code><<title>></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default Template</th>
|
||||
<td>
|
||||
|
||||
```yaml
|
||||
default_template:
|
||||
tmdb_person: <<writer>>
|
||||
plex_search:
|
||||
all:
|
||||
writer: tmdb
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
* `depth` determines how many writers are looked at per item.
|
||||
* `minimum` determines the minimum number of times the writer must appear within `depth` for the collection to be created.
|
||||
* `limit` determines the number of writer collection to max out at. (i.e. if to make collections for the top 25 writers)
|
||||
|
||||
### Producer
|
||||
|
||||
Create a collection for each producer found in the library.
|
||||
|
||||
<table class="dualTable colwidths-auto align-default table">
|
||||
<tr>
|
||||
<th><code>type</code> Option</th>
|
||||
<td><code>producer</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>data</code> Values</th>
|
||||
<td>
|
||||
<table class="clearTable">
|
||||
<tr>
|
||||
<th>Attribute</th>
|
||||
<th>Description & Values</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>depth</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>minimum</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>limit</code></td>
|
||||
<td><strong>Values:</strong> Number greater then 0</td>
|
||||
<td><strong>Default:</strong> None</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Keys</th>
|
||||
<td>TMDb Person ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Titles</th>
|
||||
<td>TMDb Person Name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default <code>title_format</code></th>
|
||||
<td><code><<title>></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Default Template</th>
|
||||
<td>
|
||||
|
||||
```yaml
|
||||
default_template:
|
||||
tmdb_person: <<producer>>
|
||||
plex_search:
|
||||
all:
|
||||
producer: tmdb
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
* `depth` determines how many producers are looked at per item.
|
||||
* `minimum` determines the minimum number of times the producer must appear within `depth` for the collection to be created.
|
||||
* `limit` determines the number of producer collection to max out at. (i.e. if to make collections for the top 25 producers)
|
||||
|
||||
#### Example:
|
||||
|
||||
|
@ -492,8 +696,8 @@ dynamic_collections:
|
|||
Top Actors: # mapping name does not matter just needs to be unique
|
||||
type: actor
|
||||
data:
|
||||
actor_depth: 5
|
||||
number_of_actors: 25
|
||||
depth: 5
|
||||
limit: 25
|
||||
```
|
||||
|
||||
#### Example:
|
||||
|
@ -506,8 +710,8 @@ dynamic_collections:
|
|||
Actors: # mapping name does not matter just needs to be unique
|
||||
type: actor
|
||||
data:
|
||||
actor_depth: 5
|
||||
actor_minimum: 20
|
||||
depth: 5
|
||||
minimum: 20
|
||||
```
|
||||
|
||||
### Genre
|
||||
|
@ -571,7 +775,7 @@ dynamic_collections:
|
|||
Genres: # mapping name does not matter just needs to be unique
|
||||
type: genre
|
||||
exclude:
|
||||
- Talk Show
|
||||
- Talk Show
|
||||
title_format: Top <<title>> <<library_type>>s
|
||||
template: genre collection
|
||||
```
|
||||
|
@ -963,12 +1167,12 @@ For example, the `addons` attribute can be used to combine multiple `keys`, i.e.
|
|||
dynamic_collections:
|
||||
networks:
|
||||
type: network
|
||||
addons:
|
||||
MTV Worldwide:
|
||||
- MTV
|
||||
- MTV2
|
||||
- MTV3
|
||||
- MTV (UK)
|
||||
addons:
|
||||
MTV Worldwide:
|
||||
- MTV
|
||||
- MTV2
|
||||
- MTV3
|
||||
- MTV (UK)
|
||||
```
|
||||
|
||||
## Template
|
||||
|
|
|
@ -558,10 +558,12 @@ class CollectionBuilder:
|
|||
self.custom_sort = False
|
||||
|
||||
for method_key, method_data in self.data.items():
|
||||
if method_key.lower() in ignored_details:
|
||||
continue
|
||||
logger.debug("")
|
||||
method_name, method_mod, method_final = self._split(method_key)
|
||||
if method_name in ignored_details:
|
||||
continue
|
||||
logger.debug("")
|
||||
logger.debug(f"Validating Method: {method_key}")
|
||||
logger.debug(f"Value: {method_data}")
|
||||
try:
|
||||
|
|
|
@ -13,13 +13,16 @@ github_base = "https://raw.githubusercontent.com/meisnate12/Plex-Meta-Manager-Co
|
|||
all_auto = ["genre"]
|
||||
ms_auto = ["actor", "year", "original_language", "tmdb_popular_people", "trakt_user_lists", "trakt_liked_lists", "trakt_people_list"]
|
||||
auto = {
|
||||
"Movie": ["tmdb_collection", "decade", "country"] + all_auto + ms_auto,
|
||||
"Movie": ["tmdb_collection", "decade", "country", "director", "producer", "writer"] + all_auto + ms_auto,
|
||||
"Show": ["network"] + all_auto + ms_auto,
|
||||
"Artist": ["mood", "style", "country"] + all_auto,
|
||||
"Video": ["country"] + all_auto
|
||||
}
|
||||
default_templates = {
|
||||
"actor": {"tmdb_person": f"<<actor>>", "plex_search": {"all": {"actor": "tmdb"}}},
|
||||
"director": {"tmdb_person": f"<<director>>", "plex_search": {"all": {"director": "tmdb"}}},
|
||||
"producer": {"tmdb_person": f"<<producer>>", "plex_search": {"all": {"producer": "tmdb"}}},
|
||||
"writer": {"tmdb_person": f"<<writer>>", "plex_search": {"all": {"writer": "tmdb"}}},
|
||||
"original_language": {"plex_all": True, "filters": {"original_language": "<<original_language>>"}},
|
||||
"tmdb_collection": {"tmdb_collection_details": "<<tmdb_collection>>"},
|
||||
"trakt_user_lists": {"trakt_list_details": "<<trakt_user_lists>>"},
|
||||
|
@ -265,7 +268,9 @@ class MetadataFile(DataFile):
|
|||
raise Failed(f"Config Error: {map_name} cannot have both include and exclude attributes")
|
||||
addons = util.parse("Config", "addons", dynamic, parent=map_name, methods=methods, datatype="dictlist") if "addons" in methods else {}
|
||||
for k, v in addons.items():
|
||||
exclude.extend(v)
|
||||
if k in v:
|
||||
logger.warning(f"Config Warning: {k} cannot be an addon for itself")
|
||||
exclude.extend([vv for vv in v if vv != k])
|
||||
default_title_format = "<<title>>"
|
||||
default_template = None
|
||||
auto_list = {}
|
||||
|
@ -303,40 +308,47 @@ class MetadataFile(DataFile):
|
|||
auto_list[tmdb_item.original_language.iso_639_1] = tmdb_item.original_language.english_name
|
||||
logger.exorcise()
|
||||
default_title_format = "<<title>> <<library_type>>s"
|
||||
elif auto_type == "actor":
|
||||
elif auto_type in ["actor", "director", "writer", "producer"]:
|
||||
people = {}
|
||||
if "data" in methods:
|
||||
dynamic_data = util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="dict")
|
||||
else:
|
||||
raise Failed(f"Config Error: {map_name} data attribute not found")
|
||||
actor_methods = {am.lower(): am for am in dynamic_data}
|
||||
actor_depth = util.parse("Config", "actor_depth", dynamic_data, parent=f"{map_name} data", methods=actor_methods, datatype="int", default=3, minimum=1)
|
||||
actor_minimum = util.parse("Config", "actor_minimum", dynamic_data, parent=f"{map_name} data", methods=actor_methods, datatype="int", default=3, minimum=1) if "actor_minimum" in actor_methods else None
|
||||
number_of_actors = util.parse("Config", "number_of_actors", dynamic_data, parent=f"{map_name} data", methods=actor_methods, datatype="int", default=25, minimum=1) if "number_of_actors" in actor_methods else None
|
||||
if not actor_minimum and not number_of_actors:
|
||||
actor_minimum = 3
|
||||
person_methods = {am.lower(): am for am in dynamic_data}
|
||||
if "actor_depth" in person_methods:
|
||||
person_methods["depth"] = person_methods.pop("actor_depth")
|
||||
if "actor_minimum" in person_methods:
|
||||
person_methods["minimum"] = person_methods.pop("actor_minimum")
|
||||
if "number_of_actors" in person_methods:
|
||||
person_methods["limit"] = person_methods.pop("number_of_actors")
|
||||
person_depth = util.parse("Config", "depth", dynamic_data, parent=f"{map_name} data", methods=person_methods, datatype="int", default=3, minimum=1)
|
||||
person_minimum = util.parse("Config", "minimum", dynamic_data, parent=f"{map_name} data", methods=person_methods, datatype="int", default=3, minimum=1) if "minimum" in person_methods else None
|
||||
person_limit = util.parse("Config", "limit", dynamic_data, parent=f"{map_name} data", methods=person_methods, datatype="int", default=25, minimum=1) if "limit" in person_methods else None
|
||||
if not person_minimum and not person_limit:
|
||||
person_minimum = 3
|
||||
if not all_items:
|
||||
all_items = library.get_all()
|
||||
for i, item in enumerate(all_items, 1):
|
||||
try:
|
||||
self.library.reload(item)
|
||||
for actor in item.actors[:actor_depth]:
|
||||
if actor.id not in people:
|
||||
people[actor.id] = {"name": actor.tag, "count": 0}
|
||||
people[actor.id]["count"] += 1
|
||||
for person in getattr(item, f"{auto_type}s")[:person_depth]:
|
||||
if person.id not in people:
|
||||
people[person.id] = {"name": person.tag, "count": 0}
|
||||
people[person.id]["count"] += 1
|
||||
except Failed as e:
|
||||
logger.error(f"Plex Error: {e}")
|
||||
roles = [data for _, data in people.items()]
|
||||
roles.sort(key=operator.itemgetter('count'), reverse=True)
|
||||
actor_count = 0
|
||||
person_count = 0
|
||||
for role in roles:
|
||||
if (number_of_actors and actor_count >= number_of_actors) or (actor_minimum and role["count"] < actor_minimum):
|
||||
if (person_limit and person_count >= person_limit) or (person_minimum and role["count"] < person_minimum):
|
||||
break
|
||||
if role["name"] not in exclude:
|
||||
try:
|
||||
results = self.config.TMDb.search_people(role["name"])
|
||||
auto_list[results[0].id] = results[0].name
|
||||
actor_count += 1
|
||||
if results[0].id not in exclude:
|
||||
auto_list[results[0].id] = results[0].name
|
||||
person_count += 1
|
||||
except TMDbNotFound:
|
||||
logger.error(f"TMDb Error: Actor {role['name']} Not Found")
|
||||
elif auto_type == "trakt_user_lists":
|
||||
|
@ -397,6 +409,9 @@ class MetadataFile(DataFile):
|
|||
logger.debug(f"Sync: {sync}")
|
||||
logger.debug(f"Include: {include}")
|
||||
logger.debug(f"Other Name: {other_name}")
|
||||
logger.debug(f"Keys (Title)")
|
||||
for key, value in auto_list.items():
|
||||
logger.debug(f" - {key}{'' if key == value else f' ({value})'}")
|
||||
|
||||
for key, value in auto_list.items():
|
||||
if include and key not in include:
|
||||
|
|
|
@ -702,6 +702,7 @@ class Plex(Library):
|
|||
for d in cols:
|
||||
if d.title == data:
|
||||
return d
|
||||
logger.debug("")
|
||||
for d in cols:
|
||||
logger.debug(f"Found: {d.title}")
|
||||
logger.debug(f"Looking for: {data}")
|
||||
|
|
|
@ -213,7 +213,7 @@ def update_libraries(config):
|
|||
logger.debug(f"Create Asset Folders: {library.create_asset_folders}")
|
||||
logger.debug(f"Download URL Assets: {library.download_url_assets}")
|
||||
logger.debug(f"Sync Mode: {library.sync_mode}")
|
||||
logger.debug(f"Collection Minimum: {library.minimum_items}")
|
||||
logger.debug(f"Minimum Items: {library.minimum_items}")
|
||||
logger.debug(f"Delete Below Minimum: {library.delete_below_minimum}")
|
||||
logger.debug(f"Delete Not Scheduled: {library.delete_not_scheduled}")
|
||||
logger.debug(f"Default Collection Order: {library.default_collection_order}")
|
||||
|
|
Loading…
Reference in a new issue