Merge pull request #113 from meisnate12/tmdb_discover

2.6.0 TMDb discover & other TMDb special lists
This commit is contained in:
mza921 2020-11-12 20:57:15 -08:00 committed by GitHub
commit f8ce499534
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 855 additions and 308 deletions

View file

@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.6.0] - 2020-11-12 - [#113](https://github.com/mza921/Plex-Auto-Collections/pull/113)
### Added
- [#107](https://github.com/mza921/Plex-Auto-Collections/issues/107) - Added `plex_collection`
- [#86](https://github.com/mza921/Plex-Auto-Collections/issues/86) - Added `tmdb_company` and `tmdb_network`
- [#41](https://github.com/mza921/Plex-Auto-Collections/issues/41) - Added `tmdb_discover`
- Added `tmdb_popular`, `tmdb_top_rated`, `tmdb_now_playing`
### Fixed
- [#108](https://github.com/mza921/Plex-Auto-Collections/issues/108) - Fixed TMDb error
- [#102](https://github.com/mza921/Plex-Auto-Collections/issues/102) - If any APIs are invalid the collection switches to append
## [2.5.0] - 2020-11-11 - [#112](https://github.com/mza921/Plex-Auto-Collections/pull/112)
### Added
- [#72](https://github.com/mza921/Plex-Auto-Collections/issues/72) - `trakt_watchlist` support
@ -12,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- [#50](https://github.com/mza921/Plex-Auto-Collections/issues/50) - Trakt access_token refresh
## [2.4.7] - 2020-11-09 - [#103](https://github.com/mza921/Plex-Auto-Collections/pull/103)
### Fixed
- [#92](https://github.com/mza921/Plex-Auto-Collections/issues/92) - fixed New Plex Movie Agent id lookup behind a proxy
@ -25,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- [#93](https://github.com/mza921/Plex-Auto-Collections/issues/93) - actually fixed `max_age`
## [2.4.5] - 2020-11-05 - [#97](https://github.com/mza921/Plex-Auto-Collections/pull/97)
### Changed
- `max_age` no longer takes years

271
README.md
View file

@ -1,5 +1,5 @@
# Plex Auto Collections
##### Version 2.5.0
##### Version 2.6.0
Plex Auto Collections is a Python 3 script that works off a configuration file to create/update Plex collections. Collection management with this tool can be automated in a varying degree of customizability. Supports IMDB, TMDb, and Trakt lists as well as built in Plex Searches using actors, genres, year, studio and more.
![https://i.imgur.com/iHAYFIZ.png](https://i.imgur.com/iHAYFIZ.png)
@ -12,8 +12,15 @@ Plex Auto Collections is a Python 3 script that works off a configuration file t
- [Collections](#collections)
- [List Type](#list-type-collection-attribute)
- [Plex Search (List Type)](#plex-search-list-type)
- [Plex Collection (List Type)](#plex-collection-list-type)
- [TMDb Collection (List Type)](#tmdb-collection-list-type)
- [TMDb People (List Type)](#tmdb-people-list-type)
- [TMDb Company (List Type)](#tmdb-company-list-type)
- [TMDb Network (List Type)](#tmdb-network-list-type)
- [TMDb Popular (List Type)](#tmdb-popular-list-type)
- [TMDb Top Rated (List Type)](#tmdb-top-rated-list-type)
- [TMDb Now Playing (List Type)](#tmdb-now-playing-list-type)
- [TMDb Discover (List Type)](#tmdb-discover-list-type)
- [TMDb List (List Type)](#tmdb-list-list-type)
- [TMDb Movie (List Type)](#tmdb-movie-list-type)
- [TMDb Show (List Type)](#tmdb-show-list-type)
@ -149,8 +156,15 @@ Each collection is defined by the mapping name which becomes the name of the Ple
The only required attribute for each collection is the list type. There are many different list types to choose from:
- [Plex Search](#plex-search-list-type)
- [Plex Collection](#plex-collection-list-type)
- [TMDb Collection](#tmdb-collection-list-type)
- [TMDb People](#tmdb-people-list-type)
- [TMDb Company](#tmdb-company-list-type)
- [TMDb Network](#tmdb-network-list-type)
- [TMDb Popular](#tmdb-popular-list-type)
- [TMDb Top Rated](#tmdb-top-rated-list-type)
- [TMDb Now Playing](#tmdb-now-playing-list-type)
- [TMDb Discover](#tmdb-discover-list-type)
- [TMDb List](#tmdb-list-list-type)
- [TMDb Movie](#tmdb-movie-list-type)
- [TMDb Show](#tmdb-show-list-type)
@ -252,6 +266,28 @@ Notes:
- You can only use each search option once per `plex_search` but you can give the search multiple values.
- If you want to restrict the search by multiples of the same attribute (i.e. You want every movie that is a Romance and Comedy) try using [filters](#collection-filters-collection-attribute).
#### Plex Collection (List Type)
###### Works with Movie and TV Show Libraries
You can create Collections based on collections already in Plex
```yaml
collections:
Dinosaurs:
plex_collection: Jurassic Park
```
Note if you want to add multiple collections you have to use a list. Comma separated values will not work.
```yaml
collections:
Dinosaurs:
plex_collection:
- Jurassic Park
- The Land Before Time
```
#### TMDb Collection (List Type)
###### Works with Movie Libraries
@ -355,6 +391,237 @@ Notes:
- Local posters are loaded over `tmdb_profile` if they exist unless `tmdb_profile` is also specified
- `tmdb_biography` will load unless `summary`,`tmdb_summary`, or `tmdb_biography` is also specified
#### TMDb Company (List Type)
###### Works with Movie and TV Show Libraries
You can use a TMDb Company to build a collection based on all it's movies/shows by using `tmdb_company`. You can use the full url or just type in the TMDb ID for the collection:
```yaml
collections:
Studio Ghibli:
tmdb_company: 10342
```
```yaml
collections:
Studio Ghibli:
tmdb_company: https://www.themoviedb.org/company/10342
```
#### TMDb Network (List Type)
###### Works with Show Libraries
Similarly to using a TMDb Company, you can also use a TMDb Network to build a collection based on all it's shows by using `tmdb_network`. You can use the full url or just type in the TMDb ID for the collection:
```yaml
collections:
CBS:
tmdb_network: 16
```
```yaml
collections:
CBS:
tmdb_network: https://www.themoviedb.org/network/16
```
#### TMDb Popular (List Type)
###### Works with Movie and TV Show Libraries
You can build a collection using TMDb's most popular movies/shows by using `tmdb_popular`. The `tmdb_popular` attribute only supports a single integer value. The `sync_mode: sync` option is recommended since the list is continuously updated.
```yaml
collections:
TMDb Trending:
tmdb_popular: 30
sync_mode: sync
```
#### TMDb Top Rated (List Type)
###### Works with Movie and TV Show Libraries
You can build a collection using TMDb's top rated movies/shows by using `tmdb_top_rated`. The `tmdb_top_rated` attribute only supports a single integer value. The `sync_mode: sync` option is recommended since the list is continuously updated.
```yaml
collections:
TMDb Top Rated:
tmdb_top_rated: 30
sync_mode: sync
```
#### TMDb Now Playing (List Type)
###### Works with Movie Libraries
You can build a collection using TMDb's release_type to get movies that are now in theaters by using `tmdb_now_playing`. The `tmdb_now_playing` attribute only supports a single integer value. The `sync_mode: sync` option is recommended since the list is continuously updated.
```yaml
collections:
TMDb Now Playing:
tmdb_now_playing: 30
sync_mode: sync
```
#### TMDb Discover (List Type)
###### Works with Movie and TV Show Libraries
You can use [TMDb's discover engine](https://www.themoviedb.org/documentation/api/discover) to create a collection based on the search for movies/shows using all different sorts of parameters shown below. The parameters are directly from [TMDb Movie Discover](https://developers.themoviedb.org/3/discover/movie-discover) and [TMDb TV Discover](https://developers.themoviedb.org/3/discover/tv-discover)
##### TMDb Discover Parameters For Movies
- `limit` (Specify how many movies you want returned by the query. Value must be an integer greater then 0. default: 100)
- `language` (Specify a language to query translatable fields with. pattern: `([a-z]{2})-([A-Z]{2})` default: en-US)
- `region` (Specify a [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) to filter release dates. Must be uppercase. pattern: `^[A-Z]{2}$`)
- `sort_by` (Choose from one of the many available sort options. Allowed Values: `popularity.asc`, `popularity.desc`, `release_date.asc`, `release_date.desc`, `revenue.asc`, `revenue.desc`, `primary_release_date.asc`, `primary_release_date.desc`, `original_title.asc`, `original_title.desc`, `vote_average.asc`, `vote_average.desc`, `vote_count.asc`, `vote_count.desc` default: `popularity.desc`)
- `certification_country` (Used in conjunction with the certification parameter, use this to specify a country with a valid certification.)
- `certification` (Filter results with a valid certification from the `certification_country` parameter.)
- `certification.lte` (Filter and only include movies that have a certification that is less than or equal to the specified value.)
- `certification.gte` (Filter and only include movies that have a certification that is greater than or equal to the specified value.)
- `include_adult` (A filter and include or exclude adult movies. Must be `true` or `false`)
- `primary_release_year` (A filter to limit the results to a specific primary release year. Year must be a 4 digit integer i.e. 1990.)
- `primary_release_date.gte` (Filter and only include movies that have a primary release date that is greater or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `primary_release_date.lte` (Filter and only include movies that have a primary release date that is less than or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `release_date.gte` (Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `release_date.lte` (Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `year` (A filter to limit the results to a specific year (looking at all release dates). Year must be a 4 digit integer i.e. 1990.)
- `vote_count.gte` (Filter and only include movies that have a vote count that is greater or equal to the specified value. Value must be an integer greater then 0.)
- `vote_count.lte` (Filter and only include movies that have a vote count that is less than or equal to the specified value. Value must be an integer greater then 0.)
- `vote_average.gte` (Filter and only include movies that have a rating that is greater or equal to the specified value. Value must be a number greater then 0.)
- `vote_average.lte` (Filter and only include movies that have a rating that is less than or equal to the specified value. Value must be an number greater then 0.)
- `with_cast` (A comma separated list of person ID's. Only include movies that have one of the ID's added as an actor.)
- `with_crew` (A comma separated list of person ID's. Only include movies that have one of the ID's added as a crew member.)
- `with_people` (A comma separated list of person ID's. Only include movies that have one of the ID's added as a either a actor or a crew member.)
- `with_companies` (A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company.)
- `with_genres` (Comma separated value of genre ids that you want to include in the results.)
- `without_genres` (Comma separated value of genre ids that you want to exclude from the results.)
- `with_keywords` (A comma separated list of keyword ID's. Only includes movies that have one of the ID's added as a keyword.)
- `without_keywords` (Exclude items with certain keywords. You can comma and pipe seperate these values to create an 'AND' or 'OR' logic.)
- `with_runtime.gte` (Filter and only include movies that have a runtime that is greater or equal to a value. Value must be an integer greater then 0.)
- `with_runtime.lte` (Filter and only include movies that have a runtime that is less than or equal to a value. Value must be an integer greater then 0.)
- `with_original_language` (Specify an ISO 639-1 string to filter results by their original language value.)
##### TMDb Discover Parameters For Shows
- `limit` (Specify how many movies you want returned by the query. Value must be an integer greater then 0. default: 100)
- `language` (Specify a language to query translatable fields with. pattern: `([a-z]{2})-([A-Z]{2})` default: en-US)
- `sort_by` (Choose from one of the many available sort options. Allowed Values: `vote_average.desc`, `vote_average.asc`, `first_air_date.desc`, `first_air_date.asc`, `popularity.desc`, `popularity.asc` default: `popularity.desc`)
- `air_date.gte` (Filter and only include TV shows that have a air date (by looking at all episodes) that is greater or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `air_date.lte` (Filter and only include TV shows that have a air date (by looking at all episodes) that is less than or equal to the specified value. Date must be in the MM/DD/YYYY Format.)
- `first_air_date.gte` (Filter and only include TV shows that have a original air date that is greater or equal to the specified value. Can be used in conjunction with the `include_null_first_air_dates` filter if you want to include items with no air date. Date must be in the MM/DD/YYYY Format.)
- `first_air_date.lte` (Filter and only include TV shows that have a original air date that is less than or equal to the specified value. Can be used in conjunction with the `include_null_first_air_dates` filter if you want to include items with no air date. Date must be in the MM/DD/YYYY Format.)
- `first_air_date_year` (Filter and only include TV shows that have a original air date year that equal to the specified value. Can be used in conjunction with the `include_null_first_air_dates` filter if you want to include items with no air date. Year must be a 4 digit integer i.e. 1990.)
- `include_null_first_air_dates` (Use this filter to include TV shows that don't have an air date while using any of the `first_air_date` filters. Must be `true` or `false`.)
"- `timezone` (Used in conjunction with the `air_date.gte/lte` filter to calculate the proper UTC offset. default: America/New_York)
- `vote_count.gte` (Filter and only include TV that have a vote count that is greater or equal to the specified value. Value must be an integer greater then 0.)
- `vote_count.lte` (Filter and only include TV that have a vote count that is less than or equal to the specified value. Value must be an integer greater then 0.)
- `vote_average.gte` (Filter and only include TV that have a rating that is greater or equal to the specified value. Value must be a number greater then 0.)
- `vote_average.lte` (Filter and only include TV that have a rating that is less than or equal to the specified value. Value must be an number greater then 0.)
- `with_networks` (Comma separated value of network ids that you want to include in the results.)
- `with_companies` (A comma separated list of production company ID's. Only include movies that have one of the ID's added as a production company.)
- `with_genres` (Comma separated value of genre ids that you want to include in the results.)
- `without_genres` (Comma separated value of genre ids that you want to exclude from the results.)
- `with_keywords` (A comma separated list of keyword ID's. Only includes TV shows that have one of the ID's added as a keyword.)
- `without_keywords` (Exclude items with certain keywords. You can comma and pipe seperate these values to create an 'AND' or 'OR' logic.)
- `with_runtime.gte` (Filter and only include TV shows with an episode runtime that is greater than or equal to a value.)
- `with_runtime.lte` (Filter and only include TV shows with an episode runtime that is less than or equal to a value.)
- `with_original_language` (Specify an ISO 639-1 string to filter results by their original language value.)
- `screened_theatrically` (Filter results to include items that have been screened theatrically. Must be `true` or `false`.)
```yaml
collections:
Movies Released in October 2020:
tmdb_discover:
primary_release_date.gte: 10/01/2020
primary_release_date.lte: 10/31/2020
```
```yaml
collections:
Popular Movies:
tmdb_discover:
sort_by: popularity.desc
```
```yaml
collections:
Highest Rated R Movies:
tmdb_discover:
certification_country: US
certification: R
sort_by: vote_average.desc
```
```yaml
collections:
Most Popular Kids Movies:
tmdb_discover:
certification_country: US
certification.lte: G
sort_by: popularity.desc
```
```yaml
collections:
Highest Rated Movies From 2010:
tmdb_discover:
primary_release_year: 2010
sort_by: vote_average.desc
```
```yaml
collections:
Best Dramas From 2014:
tmdb_discover:
with_genres: 18
primary_release_year: 2014
sort_by: vote_average.desc
```
```yaml
collections:
Highest Rated Science Fiction Movies with Tom Cruise:
tmdb_discover:
with_genres: 878
with_cast: 500
sort_by: vote_average.desc
```
```yaml
collections:
Highest Grossing Comedy Movies with Will Ferrell:
tmdb_discover:
with_genres: 35
with_cast: 23659
sort_by: revenue.desc
```
```yaml
collections:
Top Rated Movies with Brad Pitt and Edward Norton:
tmdb_discover:
with_people: 287,819
sort_by: vote_average.desc
```
```yaml
collections:
Popular Movies with David Fincher and Rooney Mara:
tmdb_discover:
with_people: 108916,7467
sort_by: popularity.desc
```
```yaml
collections:
Top Rated Dramas:
tmdb_discover:
with_genres: 18
sort_by: vote_average.desc
vote_count.gte: 10
```
```yaml
collections:
Highest Grossing R Movies with Liam Neeson:
tmdb_discover:
certification_country: US
certification: R
sort_by: revenue.desc
with_cast: 3896
```
#### TMDb List (List Type)
###### Works with Movie and TV Show Libraries
@ -476,7 +743,7 @@ This script can pull a number of items from the Trakt Trending List for [Movies]
```yaml
collections:
Trending:
Trakt Trending:
trakt_trending: 30
sync_mode: sync
```

View file

@ -2,14 +2,19 @@ import re
import requests
import math
import sys
import os
from urllib.parse import urlparse
from lxml import html
from tmdbv3api import TMDb
from tmdbv3api import Movie
from tmdbv3api import List
from tmdbv3api import TV
from tmdbv3api import Discover
from tmdbv3api import Collection
from tmdbv3api import Company
#from tmdbv3api import Network #TURNON:Trending
from tmdbv3api import Person
#from tmdbv3api import Trending #TURNON:Trending
import config_tools
import plex_tools
import trakt
@ -95,7 +100,7 @@ def imdb_get_movies(config_path, plex, data):
if not tmdb.api_key == "None":
tmdb_id = m.guid.split('themoviedb://')[1].split('?')[0]
tmdbapi = movie.details(tmdb_id)
imdb_id = tmdbapi.imdb_id
imdb_id = tmdbapi.imdb_id if hasattr(tmdbapi, 'imdb_id') else None
else:
imdb_id = None
elif 'imdb://' in m.guid:
@ -119,36 +124,93 @@ def imdb_get_movies(config_path, plex, data):
return matched_imdb_movies, missing_imdb_movies
def tmdb_get_movies(config_path, plex, data, is_list=False):
tmdb_id = int(data)
def tmdb_get_movies(config_path, plex, data, method):
t_movs = []
t_movie = Movie()
t_movie.api_key = config_tools.TMDB(config_path).apikey # Set TMDb api key for Movie
if t_movie.api_key == "None":
raise KeyError("Invalid TMDb API Key")
tmdb = List() if is_list else Collection()
tmdb.api_key = t_movie.api_key
t_col = tmdb.details(tmdb_id)
if is_list:
try:
for tmovie in t_col:
if tmovie.media_type == "movie":
t_movs.append(tmovie.id)
except:
raise ValueError("| Config Error: TMDb List: {} not found".format(tmdb_id))
count = 0
if method == "tmdb_discover":
discover = Discover()
discover.api_key = t_movie.api_key
discover.discover_movies(data)
total_pages = int(os.environ["total_pages"])
total_results = int(os.environ["total_results"])
limit = int(data.pop('limit'))
amount = total_results if total_results < limit else limit
print("| Processing {}: {} items".format(method, amount))
for attr, value in data.items():
print("| {}: {}".format(attr, value))
for x in range(total_pages):
data["page"] = x + 1
tmdb_movies = discover.discover_movies(data)
for tmovie in tmdb_movies:
count += 1
t_movs.append(tmovie.id)
if count == amount:
break
if count == amount:
break
elif method in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]:
#trending = Trending() #TURNON:Trending
#trending.api_key = t_movie.api_key #TURNON:Trending
for x in range(int(data / 20) + 1):
if method == "tmdb_popular":
tmdb_movies = t_movie.popular(x + 1)
elif method == "tmdb_top_rated":
tmdb_movies = t_movie.top_rated(x + 1)
elif method == "tmdb_now_playing":
tmdb_movies = t_movie.now_playing(x + 1)
#elif method == "tmdb_trending_daily": #TURNON:Trending
# tmdb_movies = trending.movie_day(x + 1) #TURNON:Trending
#elif method == "tmdb_trending_weekly": #TURNON:Trending
# tmdb_movies = trending.movie_week(x + 1) #TURNON:Trending
for tmovie in tmdb_movies:
count += 1
t_movs.append(tmovie.id)
if count == data:
break
if count == data:
break
print("| Processing {}: {} Items".format(method, data))
else:
try:
for tmovie in t_col.parts:
t_movs.append(tmovie['id'])
except AttributeError:
tmdb_id = int(data)
if method == "tmdb_list":
tmdb = List()
tmdb.api_key = t_movie.api_key
try:
t_movie.details(tmdb_id).imdb_id
t_movs.append(tmdb_id)
t_col = tmdb.details(tmdb_id)
tmdb_name = str(t_col)
for tmovie in t_col:
if tmovie.media_type == "movie":
t_movs.append(tmovie.id)
except:
raise ValueError("| Config Error: TMDb ID: {} not found".format(tmdb_id))
raise ValueError("| Config Error: TMDb List: {} not found".format(tmdb_id))
elif method == "tmdb_company":
tmdb = Company()
tmdb.api_key = t_movie.api_key
tmdb_name = str(tmdb.details(tmdb_id))
company_movies = tmdb.movies(tmdb_id)
for tmovie in company_movies:
t_movs.append(tmovie.id)
else:
tmdb = Collection()
tmdb.api_key = t_movie.api_key
t_col = tmdb.details(tmdb_id)
tmdb_name = str(t_col)
try:
for tmovie in t_col.parts:
t_movs.append(tmovie['id'])
except AttributeError:
try:
t_movie.details(tmdb_id).imdb_id
tmdb_name = str(t_movie.details(tmdb_id))
t_movs.append(tmdb_id)
except:
raise ValueError("| Config Error: TMDb ID: {} not found".format(tmdb_id))
print("| Processing {}: ({}) {}".format(method, tmdb_id, tmdb_name))
# Create dictionary of movies and their guid
@ -250,33 +312,92 @@ def get_tvdb_id_from_tmdb_id(id):
else:
return None
def tmdb_get_shows(config_path, plex, data, is_list=False):
def tmdb_get_shows(config_path, plex, data, method):
config_tools.TraktClient(config_path)
tmdb_id = int(data)
t_tvs = []
t_tv = TV()
t_tv.api_key = config_tools.TMDB(config_path).apikey # Set TMDb api key for Movie
if t_tv.api_key == "None":
raise KeyError("Invalid TMDb API Key")
discover = Discover()
discover.api_key = t_tv.api_key
if is_list:
tmdb = List()
tmdb.api_key = t_tv.api_key
try:
t_col = tmdb.details(tmdb_id)
for ttv in t_col:
if ttv.media_type == "tv":
t_tvs.append(ttv.id)
except:
raise ValueError("| Config Error: TMDb List: {} not found".format(tmdb_id))
count = 0
if method == "tmdb_discover":
discover.discover_tv_shows(data)
total_pages = int(os.environ["total_pages"])
total_results = int(os.environ["total_results"])
limit = int(data.pop('limit'))
amount = total_results if total_results < limit else limit
print("| Processing {}: {} items".format(method, amount))
for attr, value in data.items():
print("| {}: {}".format(attr, value))
for x in range(total_pages):
data["page"] = x + 1
tmdb_shows = discover.discover_tv_shows(data)
for tshow in tmdb_shows:
count += 1
t_tvs.append(tshow.id)
if count == amount:
break
if count == amount:
break
run_discover(data)
elif method in ["tmdb_popular", "tmdb_top_rated", "tmdb_trending_daily", "tmdb_trending_weekly"]:
#trending = Trending() #TURNON:Trending
#trending.api_key = t_movie.api_key #TURNON:Trending
for x in range(int(data / 20) + 1):
if method == "tmdb_popular":
tmdb_shows = t_tv.popular(x + 1)
elif method == "tmdb_top_rated":
tmdb_shows = t_tv.top_rated(x + 1)
#elif method == "tmdb_trending_daily": #TURNON:Trending
# tmdb_shows = trending.tv_day(x + 1) #TURNON:Trending
#elif method == "tmdb_trending_weekly": #TURNON:Trending
# tmdb_shows = trending.tv_week(x + 1) #TURNON:Trending
for tshow in tmdb_shows:
count += 1
t_tvs.append(tshow.id)
if count == amount:
break
if count == amount:
break
print("| Processing {}: {} Items".format(method, data))
else:
try:
t_tv.details(tmdb_id).number_of_seasons
t_tvs.append(tmdb_id)
except:
raise ValueError("| Config Error: TMDb ID: {} not found".format(tmdb_id))
tmdb_id = int(data)
if method == "tmdb_list":
tmdb = List()
tmdb.api_key = t_tv.api_key
try:
t_col = tmdb.details(tmdb_id)
tmdb_name = str(t_col)
for ttv in t_col:
if ttv.media_type == "tv":
t_tvs.append(ttv.id)
except:
raise ValueError("| Config Error: TMDb List: {} not found".format(tmdb_id))
elif method in ["tmdb_company", "tmdb_network"]:
if method == "tmdb_company":
tmdb = Company()
tmdb.api_key = t_tv.api_key
tmdb_name = str(tmdb.details(tmdb_id))
else:
#tmdb = Network() #TURNON:Trending
#tmdb.api_key = t_tv.api_key #TURNON:Trending
tmdb_name = ""#str(tmdb.details(tmdb_id)) #TURNON:Trending
discover_method = "with_companies" if method == "tmdb_company" else "with_networks"
tmdb_shows = discover.discover_tv_shows({discover_method: tmdb_id})
for tshow in tmdb_shows:
t_tvs.append(tshow.id)
else:
try:
t_tv.details(tmdb_id).number_of_seasons
tmdb_name = str(t_tv.details(tmdb_id))
t_tvs.append(tmdb_id)
except:
raise ValueError("| Config Error: TMDb ID: {} not found".format(tmdb_id))
print("| Processing {}: ({}) {}".format(method, tmdb_id, tmdb_name))
p_tv_map = {}
for item in plex.Library.all():
@ -310,7 +431,7 @@ def tmdb_get_shows(config_path, plex, data, is_list=False):
return matched, missing
def tvdb_get_shows(config_path, plex, data, is_list=False):
def tvdb_get_shows(config_path, plex, data):
config_tools.TraktClient(config_path)
id = int(data)

View file

@ -131,12 +131,21 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
"collection_sort": "collection_order"
}
all_lists = [
"plex_search"
"plex_search",
"plex_collection",
"tmdb_collection",
"tmdb_id",
"tmdb_actor",
"tmdb_director"
"tmdb_writer"
"tmdb_director",
"tmdb_writer",
"tmdb_company",
"tmdb_network",
"tmdb_discover",
"tmdb_popular",
"tmdb_top_rated",
"tmdb_now_playing",
#"tmdb_trending_daily", #TURNON:Trending
#"tmdb_trending_weekly", #TURNON:Trending
"tmdb_list",
"tmdb_movie",
"tmdb_show",
@ -161,13 +170,16 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
show_only_lists = [
"tmdb_show",
"tvdb_show"
"tmdb_network",
]
movie_only_lists = [
"tmdb_collection",
"tmdb_id",
"tmdb_actor",
"tmdb_company",
"tmdb_director",
"tmdb_writer",
"tmdb_now_playing",
"tmdb_movie",
"imdb_list",
]
@ -211,11 +223,53 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
"background", "file_background",
"name_mapping"
]
discover_movie = [
"language", "with_original_language", "region", "sort_by",
"certification_country", "certification", "certification.lte", "certification.gte",
"include_adult",
"primary_release_year", "primary_release_date.gte", "primary_release_date.lte",
"release_date.gte", "release_date.lte", "year",
"vote_count.gte", "vote_count.lte",
"vote_average.gte", "vote_average.lte",
"with_cast", "with_crew", "with_people",
"with_companies",
"with_genres", "without_genres",
"with_keywords", "without_keywords",
"with_runtime.gte", "with_runtime.lte"
]
discover_tv = [
"language", "with_original_language", "timezone", "sort_by",
"air_date.gte", "air_date.lte",
"first_air_date.gte", "first_air_date.lte", "first_air_date_year",
"vote_count.gte", "vote_count.lte",
"vote_average.gte", "vote_average.lte",
"with_genres", "without_genres",
"with_keywords", "without_keywords",
"with_networks", "with_companies",
"with_runtime.gte", "with_runtime.lte",
"include_null_first_air_dates",
"screened_theatrically"
]
discover_movie_sort = [
"popularity.asc", "popularity.desc",
"release_date.asc", "release_date.desc",
"revenue.asc", "revenue.desc",
"primary_release_date.asc", "primary_release_date.desc",
"original_title.asc", "original_title.desc",
"vote_average.asc", "vote_average.desc",
"vote_count.asc", "vote_count.desc"
]
discover_tv_sort = [
"vote_average.desc", "vote_average.asc",
"first_air_date.desc", "first_air_date.asc",
"popularity.desc", "popularity.asc"
]
print("|\n| Running collection update press Ctrl+C to abort at anytime")
for c in collections:
print("| \n|===================================================================================================|\n|")
print("| Updating collection: {}...".format(c))
map = {}
sync_collection = True if plex.sync_mode == "sync" else False
if "sync_mode" in collections[c]:
if collections[c]["sync_mode"]:
@ -230,10 +284,12 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
print("| Config Error: sync_mode attribute is blank")
if sync_collection == True:
print("| Sync Mode: sync")
plex_collection = get_collection(plex, c, headless)
if isinstance(plex_collection, Collections):
try:
plex_collection = get_collection(plex, c, headless)
for item in plex_collection.children:
map[item.ratingKey] = item
except ValueError as e:
print("| Config Error: {}".format(e))
else:
print("| Sync Mode: append")
@ -251,10 +307,13 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
for m in collections[c]:
if ("tmdb" in m or "imdb" in m) and not TMDB.valid:
print("| Config Error: {} skipped. tmdb incorrectly configured".format(m))
map = {}
elif ("trakt" in m or (("tmdb" in m or "tvdb" in m) and plex.library_type == "show")) and not TraktClient.valid:
print("| Config Error: {} skipped. trakt incorrectly configured".format(m))
map = {}
elif m == "tautulli" and not Tautulli.valid:
print("| Config Error: {} skipped. tautulli incorrectly configured".format(m))
map = {}
elif collections[c][m]:
if m in alias:
method_name = alias[m]
@ -331,45 +390,6 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
print("| Config Error: {} attribute not supported".format(detail_name))
elif method_name in all_details:
check_details(method_name, collections[c][m])
elif method_name == "filters":
for filter in collections[c][m]:
if filter in alias or (filter.endswith(".not") and filter[:-4] in alias):
final_filter = (alias[filter[:-4]] + filter[-4:]) if filter.endswith(".not") else alias[filter]
print("| Config Warning: {} filter will run as {}".format(filter, final_filter))
else:
final_filter = filter
if final_filter in movie_only_filters and libtype == "show":
print("| Config Error: {} filter only works for movie libraries".format(final_filter))
elif final_filter in all_filters:
filters.append((final_filter, collections[c][m][filter])) #TODO: validate filters contents
else:
print("| Config Error: {} filter not supported".format(filter))
elif method_name == "plex_search":
search = []
searches_used = []
for search_attr in collections[c][m]:
if search_attr in alias or (search_attr.endswith(".not") and search_attr[:-4] in alias):
final_attr = (alias[search_attr[:-4]] + search_attr[-4:]) if search_attr.endswith(".not") else alias[search_attr]
print("| Config Warning: {} plex search attribute will run as {}".format(search_attr, final_attr))
else:
final_attr = search_attr
if final_attr in movie_only_searches and libtype == "show":
print("| Config Error: {} plex search attribute only works for movie libraries".format(final_attr))
elif (final_attr[:-4] if final_attr.endswith(".not") else final_attr) in searches_used:
print("| Config Error: Only one instance of {} can be used try using it as a filter instead".format(final_attr))
elif final_attr in ["year", "year.not"]:
year_pair = get_method_pair_year(final_attr, collections[c][m][search_attr])
if len(year_pair[1]) > 0:
searches_used.append(final_attr[:-4] if final_attr.endswith(".not") else final_attr)
search.append(get_method_pair_int(final_attr, collections[c][m][search_attr], final_attr[:-4] if final_attr.endswith(".not") else final_attr))
elif final_attr in plex_searches:
if final_attr.startswith("tmdb_"):
final_attr = final_attr[5:]
searches_used.append(final_attr[:-4] if final_attr.endswith(".not") else final_attr)
search.append((final_attr, get_attribute_list(collections[c][m][search_attr])))
else:
print("| Config Error: {} plex search attribute not supported".format(search_attr))
methods.append((method_name, [search]))
elif method_name in movie_only_searches and libtype == "show":
print("| Config Error: {} plex search only works for movie libraries".format(method_name))
elif method_name in ["year", "year.not"]:
@ -396,8 +416,20 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
methods.append(("plex_search", [[(method_name[5:], new_ids)]]))
elif method_name in plex_searches:
methods.append(("plex_search", [[(method_name, get_attribute_list(collections[c][m]))]]))
elif method_name == "plex_collection":
collection_list = collections[c][m] if isinstance(collections[c][m], list) else [collections[c][m]]
final_collections = []
for new_collection in collection_list:
try:
final_collections.append(get_collection(plex, new_collection, headless))
except ValueError as e:
print("| Config Error: {} {}".format(method_name, new_collection))
if len(final_collections) > 0:
methods.append(("plex_collection", final_collections))
elif method_name == "tmdb_collection":
methods.append(get_method_pair_tmdb(method_name, collections[c][m], "TMDb Collection ID"))
elif method_name == "tmdb_company":
methods.append(get_method_pair_int(method_name, collections[c][m], "TMDb Company ID"))
elif method_name == "tmdb_id":
id = get_method_pair_tmdb(method_name, collections[c][m], "TMDb ID")
if tmdb_id is None:
@ -407,10 +439,14 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
details["poster"] = ["url", tmdb_get_metadata(config_path, id[1][0], "backdrop_path"), method_name]
tmdb_id = id[1][0]
methods.append(id)
elif method_name == "tmdb_list": #TODO: validate
elif method_name in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]:
methods.append((method_name, [regex_first_int(collections[c][m], method_name, default=20)]))
elif method_name == "tmdb_list":
methods.append(get_method_pair_int(method_name, collections[c][m], "TMDb List ID"))
elif method_name == "tmdb_movie":
methods.append(get_method_pair_tmdb(method_name, collections[c][m], "TMDb Movie ID"))
elif method_name == "tmdb_network":
methods.append(get_method_pair_int(method_name, collections[c][m], "TMDb Network ID"))
elif method_name == "tmdb_show":
methods.append(get_method_pair_tmdb(method_name, collections[c][m], "TMDb Show ID"))
elif method_name == "tvdb_show":
@ -429,16 +465,129 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
methods.append((method_name, [regex_first_int(collections[c][m], method_name, default=30)]))
elif method_name == "trakt_watchlist":
methods.append((method_name, get_attribute_list(collections[c][m])))
elif method_name == "tautulli": #TODO:test
try:
new_dictionary = {}
new_dictionary["list_type"] = check_for_attribute(collections[c][m], "list_type", parent="tautulli", test_list=["popular", "watched"], options="| \tpopular (Most Popular List)\n| \twatched (Most Watched List)", throw=True, save=False)
new_dictionary["list_days"] = check_for_attribute(collections[c][m], "list_days", parent="tautulli", var_type="int", default=30, save=False)
new_dictionary["list_size"] = check_for_attribute(collections[c][m], "list_size", parent="tautulli", var_type="int", default=10, save=False)
new_dictionary["list_buffer"] = check_for_attribute(collections[c][m], "list_buffer", parent="tautulli", var_type="int", default=20, save=False)
methods.append((method_name, [new_dictionary]))
except SystemExit as e:
print(e)
elif method_name in ["filters", "plex_search", "tmdb_discover", "tautulli"]:
if isinstance(collections[c][m], dict):
if method_name == "filters":
for filter in collections[c][m]:
if filter in alias or (filter.endswith(".not") and filter[:-4] in alias):
final_filter = (alias[filter[:-4]] + filter[-4:]) if filter.endswith(".not") else alias[filter]
print("| Config Warning: {} filter will run as {}".format(filter, final_filter))
else:
final_filter = filter
if final_filter in movie_only_filters and libtype == "show":
print("| Config Error: {} filter only works for movie libraries".format(final_filter))
elif final_filter in all_filters:
filters.append((final_filter, collections[c][m][filter])) #TODO: validate filters contents
else:
print("| Config Error: {} filter not supported".format(filter))
elif method_name == "plex_search":
search = []
searches_used = []
for search_attr in collections[c][m]:
if search_attr in alias or (search_attr.endswith(".not") and search_attr[:-4] in alias):
final_attr = (alias[search_attr[:-4]] + search_attr[-4:]) if search_attr.endswith(".not") else alias[search_attr]
print("| Config Warning: {} plex search attribute will run as {}".format(search_attr, final_attr))
else:
final_attr = search_attr
if final_attr in movie_only_searches and libtype == "show":
print("| Config Error: {} plex search attribute only works for movie libraries".format(final_attr))
elif (final_attr[:-4] if final_attr.endswith(".not") else final_attr) in searches_used:
print("| Config Error: Only one instance of {} can be used try using it as a filter instead".format(final_attr))
elif final_attr in ["year", "year.not"]:
year_pair = get_method_pair_year(final_attr, collections[c][m][search_attr])
if len(year_pair[1]) > 0:
searches_used.append(final_attr[:-4] if final_attr.endswith(".not") else final_attr)
search.append(get_method_pair_int(final_attr, collections[c][m][search_attr], final_attr[:-4] if final_attr.endswith(".not") else final_attr))
elif final_attr in plex_searches:
if final_attr.startswith("tmdb_"):
final_attr = final_attr[5:]
searches_used.append(final_attr[:-4] if final_attr.endswith(".not") else final_attr)
search.append((final_attr, get_attribute_list(collections[c][m][search_attr])))
else:
print("| Config Error: {} plex search attribute not supported".format(search_attr))
methods.append((method_name, [search]))
elif method_name == "tmdb_discover":
new_dictionary = {"limit": 100}
for attr in collections[c][m]:
if collections[c][m][attr]:
attr_data = collections[c][m][attr]
if (libtype == "movie" and attr in discover_movie) or (libtype == "show" and attr in discover_tv):
if attr == "language":
if re.compile("([a-z]{2})-([A-Z]{2})").match(str(attr_data)):
new_dictionary[attr] = str(attr_data)
else:
print("| Config Error: Skipping {} attribute {}: {} must match pattern ([a-z]{2})-([A-Z]{2}) e.g. en-US".format(m, attr, attr_data))
elif attr == "region":
if re.compile("^[A-Z]{2}$").match(str(attr_data)):
new_dictionary[attr] = str(attr_data)
else:
print("| Config Error: Skipping {} attribute {}: {} must match pattern ^[A-Z]{2}$ e.g. US".format(m, attr, attr_data))
elif attr == "sort_by":
if (libtype == "movie" and attr_data in discover_movie_sort) or (libtype == "show" and attr_data in discover_tv_sort):
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: {} is invalid".format(m, attr, attr_data))
elif attr == "certification_country":
if "certification" in collections[c][m] or "certification.lte" in collections[c][m] or "certification.gte" in collections[c][m]:
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: must be used with either certification, certification.lte, or certification.gte".format(m, attr))
elif attr in ["certification", "certification.lte", "certification.gte"]:
if "certification_country" in collections[c][m]:
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: must be used with certification_country".format(m, attr))
elif attr in ["include_adult", "include_null_first_air_dates", "screened_theatrically"]:
if attr_data is True:
new_dictionary[attr] = attr_data
elif attr in ["primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte"]:
if re.compile("[0-1]?[0-9][/-][0-3]?[0-9][/-][1-2][890][0-9][0-9]").match(str(attr_data)):
the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-")
new_dictionary[attr] = "{}-{}-{}".format(the_date[2], the_date[0], the_date[1])
elif re.compile("[1-2][890][0-9][0-9][/-][0-1]?[0-9][/-][0-3]?[0-9]").match(str(attr_data)):
the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-")
new_dictionary[attr] = "{}-{}-{}".format(the_date[0], the_date[1], the_date[2])
else:
print("| Config Error: Skipping {} attribute {}: {} must match pattern MM/DD/YYYY e.g. 12/25/2020".format(m, attr, attr_data))
elif attr in ["primary_release_year", "year", "first_air_date_year"]:
if isinstance(attr_data, int) and 1800 < attr_data and attr_data < 2200:
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: must be a valid year e.g. 1990".format(m, attr))
elif attr in ["vote_count.gte", "vote_count.lte", "vote_average.gte", "vote_average.lte", "with_runtime.gte", "with_runtime.lte"]:
if (isinstance(attr_data, int) or isinstance(attr_data, float)) and 0 < attr_data:
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: must be a valid number greater then 0".format(m, attr))
elif attr in ["with_cast", "with_crew", "with_people", "with_companies", "with_networks", "with_genres", "without_genres", "with_keywords", "without_keywords", "with_original_language", "timezone"]:
new_dictionary[attr] = attr_data
else:
print("| Config Error: {} attribute {} not supported".format(m, attr))
elif attr == "limit":
if isinstance(attr_data, int) and attr_data > 0:
new_dictionary[attr] = attr_data
else:
print("| Config Error: Skipping {} attribute {}: must be a valid number greater then 0".format(m, attr))
else:
print("| Config Error: {} attribute {} not supported".format(m, attr))
else:
print("| Config Error: {} parameter {} is blank".format(m, attr))
if len(new_dictionary) > 1:
methods.append((method_name, [new_dictionary]))
else:
print("| Config Error: {} had no valid fields".format(m))
elif method_name == "tautulli":
try:
new_dictionary = {}
new_dictionary["list_type"] = check_for_attribute(collections[c][m], "list_type", parent="tautulli", test_list=["popular", "watched"], options="| \tpopular (Most Popular List)\n| \twatched (Most Watched List)", throw=True, save=False)
new_dictionary["list_days"] = check_for_attribute(collections[c][m], "list_days", parent="tautulli", var_type="int", default=30, save=False)
new_dictionary["list_size"] = check_for_attribute(collections[c][m], "list_size", parent="tautulli", var_type="int", default=10, save=False)
new_dictionary["list_buffer"] = check_for_attribute(collections[c][m], "list_buffer", parent="tautulli", var_type="int", default=20, save=False)
methods.append((method_name, [new_dictionary]))
except SystemExit as e:
print(e)
else:
print("| Config Error: {} attribute is not a dictionary: {}".format(m, collections[c][m]))
elif method_name == "all":
methods.append((method_name, [""]))
elif method_name != "sync_mode":
@ -458,7 +607,9 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
for v in values:
if m == "imdb_list":
print("| \n| Processing {}: {}".format(m, v[0]))
elif m != "plex_search":
elif m == "plex_collection":
print("| \n| Processing {}: {}".format(m, v.title))
elif m not in ["plex_search", "tmdb_list", "tmdb_id", "tmdb_movie", "tmdb_collection", "tmdb_company", "tmdb_network", "tmdb_discover", "tmdb_show"]:
print("| \n| Processing {}: {}".format(m, v))
try:
missing, map = add_to_collection(config_path, plex, m, v, c, map, filters)
@ -505,9 +656,10 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
print("| ")
plex_collection = get_collection(plex, c, headless)
if not isinstance(plex_collection, Collections):
try:
plex_collection = get_collection(plex, c, headless)
except ValueError as e:
print("| Config Error: {}".format(e))
continue # No collections created with requested criteria
if not no_meta:
@ -620,162 +772,164 @@ def update_from_config(config_path, plex, headless=False, no_meta=False, no_imag
def append_collection(config_path, config_update=None):
while True:
if config_update:
collection_name = config_update
selected_collection = get_collection(plex, collection_name, True)
else:
collection_name = input("| Enter collection to add to: ")
selected_collection = get_collection(plex, collection_name)
selected_collection = None
try:
if not isinstance(selected_collection, str):
print("| \"{}\" Selected.".format(selected_collection.title))
finished = False
while not finished:
try:
collection_type = selected_collection.subtype
if collection_type == 'movie':
method = input("| Add Movie(m), Actor(a), IMDb/TMDb/Trakt List(l), Custom(c), Back(b)?: ")
else:
method = input("| Add Show(s), Actor(a), IMDb/TMDb/Trakt List(l), Custom(c), Back(b)?: ")
if method == "m":
if not config_update:
method = "movie"
value = input("| Enter Movie (Name or Rating Key): ")
if value is int:
plex_movie = get_movie(plex, int(value))
print('| +++ Adding %s to collection %s' % (
plex_movie.title, selected_collection.title))
plex_movie.addCollection(selected_collection.title)
else:
results = get_movie(plex, value)
if len(results) > 1:
while True:
i = 1
for result in results:
print("| {POS}) {TITLE} - {RATINGKEY}".format(POS=i, TITLE=result.title,
RATINGKEY=result.ratingKey))
i += 1
s = input("| Select movie (N for None): ")
if int(s):
s = int(s)
if len(results) >= s > 0:
result = results[s - 1]
print('| +++ Adding %s to collection %s' % (
result.title, selected_collection.title))
result.addCollection(selected_collection.title)
break
else:
break
if config_update:
collection_name = config_update
selected_collection = get_collection(plex, collection_name, True)
else:
collection_name = input("| Enter collection to add to: ")
selected_collection = get_collection(plex, collection_name)
except ValueError as e:
print("| Config Error: {}".format(e))
break
try:
print("| \"{}\" Selected.".format(selected_collection.title))
finished = False
while not finished:
try:
collection_type = selected_collection.subtype
if collection_type == 'movie':
method = input("| Add Movie(m), Actor(a), IMDb/TMDb/Trakt List(l), Custom(c), Back(b)?: ")
else:
method = input("| Add Show(s), Actor(a), IMDb/TMDb/Trakt List(l), Custom(c), Back(b)?: ")
if method == "m":
if not config_update:
method = "movie"
value = input("| Enter Movie (Name or Rating Key): ")
if value is int:
plex_movie = get_movie(plex, int(value))
print('| +++ Adding %s to collection %s' % (
plex_movie.title, selected_collection.title))
plex_movie.addCollection(selected_collection.title)
else:
print("| Movies in configuration file not yet supported")
results = get_movie(plex, value)
if len(results) > 1:
while True:
i = 1
for result in results:
print("| {POS}) {TITLE} - {RATINGKEY}".format(POS=i, TITLE=result.title,
RATINGKEY=result.ratingKey))
i += 1
s = input("| Select movie (N for None): ")
if int(s):
s = int(s)
if len(results) >= s > 0:
result = results[s - 1]
print('| +++ Adding %s to collection %s' % (
result.title, selected_collection.title))
result.addCollection(selected_collection.title)
break
else:
break
else:
print("| Movies in configuration file not yet supported")
# elif method == "s":
# if not config_update:
# method = "show"
# value = input("Enter Show (Name or Rating Key): ")
# if value is int:
# plex_show = get_show(int(value))
# print('+++ Adding %s to collection %s' % (
# plex_show.title, selected_collection.title))
# plex_show.addCollection(selected_collection.title)
# else:
# results = get_show(plex, value)
# if len(results) > 1:
# while True:
# i = 1
# for result in results:
# print("{POS}) {TITLE} - {RATINGKEY}".format(POS=i, TITLE=result.title,
# RATINGKEY=result.ratingKey))
# i += 1
# s = input("Select show (N for None): ")
# if int(s):
# s = int(s)
# if len(results) >= s > 0:
# result = results[s - 1]
# print('+++ Adding %s to collection %s' % (
# result.title, selected_collection.title))
# result.addCollection(selected_collection.title)
# break
# else:
# break
# else:
# print("Shows in configuration file not yet supported")
# elif method == "s":
# if not config_update:
# method = "show"
# value = input("Enter Show (Name or Rating Key): ")
# if value is int:
# plex_show = get_show(int(value))
# print('+++ Adding %s to collection %s' % (
# plex_show.title, selected_collection.title))
# plex_show.addCollection(selected_collection.title)
# else:
# results = get_show(plex, value)
# if len(results) > 1:
# while True:
# i = 1
# for result in results:
# print("{POS}) {TITLE} - {RATINGKEY}".format(POS=i, TITLE=result.title,
# RATINGKEY=result.ratingKey))
# i += 1
# s = input("Select show (N for None): ")
# if int(s):
# s = int(s)
# if len(results) >= s > 0:
# result = results[s - 1]
# print('+++ Adding %s to collection %s' % (
# result.title, selected_collection.title))
# result.addCollection(selected_collection.title)
# break
# else:
# break
# else:
# print("Shows in configuration file not yet supported")
elif method == "a":
method = "actors"
value = input("| Enter Actor Name: ")
try:
a_rkey = get_actor_rkey(plex, value)
elif method == "a":
method = "actors"
value = input("| Enter Actor Name: ")
try:
a_rkey = get_actor_rkey(plex, value)
if config_update:
modify_config(config_path, collection_name, method, value)
else:
add_to_collection(config_path, plex, method, a_rkey, selected_collection.title)
except ValueError as e:
print(e)
elif method == "l":
l_type = input("| Enter list type IMDb(i) TMDb(t) Trakt(k): ")
if l_type == "i":
l_type = "IMDb"
method = "imdb_list"
elif l_type == "t":
l_type = "TMDb"
method = "tmdb_collection"
elif l_type == "k":
l_type = "Trakt"
method = "trakt_list"
else:
return
url = input("| Enter {} List URL: ".format(l_type)).strip()
print("| Processing {} List: {}".format(l_type, url))
if config_update:
modify_config(config_path, collection_name, method, url)
else:
missing = add_to_collection(config_path, plex, method, url, selected_collection.title)
if missing:
if collection_type == 'movie':
print("| {} missing movies from {} List: {}".format(len(missing), l_type, url))
if input("| Add missing movies to Radarr? (y/n)").upper() == "Y":
add_to_radarr(config_path, missing)
# elif collection_type == 'show':
# print("{} missing shows from {} List: {}".format(len(missing_shows), l_type, url))
# if input("Add missing shows to Sonarr? (y/n)").upper() == "Y":
# add_to_sonarr(missing_shows)
print("| Bad {} List URL".format(l_type))
elif method == "c":
print("| Please read the below link to see valid search types. "
"Please note not all have been tested")
print(
"| https://python-plexapi.readthedocs.io/en/latest/modules/video.html?highlight=plexapi.video.Movie#plexapi.video.Movie")
while True:
method = input("| Enter Search method (q to quit): ")
if method in "quit":
break
m_search = " " + method + " "
if m_search in Movie.__doc__ or hasattr(Movie, m_search):
if method[-1:] == "s":
method_p = method[:-1]
else:
method_p = method
value = input("| Enter {}: ".format(method_p))
if config_update:
modify_config(config_path, collection_name, method, value)
else:
add_to_collection(config_path, plex, method, a_rkey, selected_collection.title)
except ValueError as e:
print(e)
elif method == "l":
l_type = input("| Enter list type IMDb(i) TMDb(t) Trakt(k): ")
if l_type == "i":
l_type = "IMDb"
method = "imdb_list"
elif l_type == "t":
l_type = "TMDb"
method = "tmdb_collection"
elif l_type == "k":
l_type = "Trakt"
method = "trakt_list"
add_to_collection(config_path, plex, method, value, selected_collection.title)
break
else:
return
url = input("| Enter {} List URL: ".format(l_type)).strip()
print("| Processing {} List: {}".format(l_type, url))
if config_update:
modify_config(config_path, collection_name, method, url)
else:
missing = add_to_collection(config_path, plex, method, url, selected_collection.title)
if missing:
if collection_type == 'movie':
print("| {} missing movies from {} List: {}".format(len(missing), l_type, url))
if input("| Add missing movies to Radarr? (y/n)").upper() == "Y":
add_to_radarr(config_path, missing)
# elif collection_type == 'show':
# print("{} missing shows from {} List: {}".format(len(missing_shows), l_type, url))
# if input("Add missing shows to Sonarr? (y/n)").upper() == "Y":
# add_to_sonarr(missing_shows)
print("| Bad {} List URL".format(l_type))
elif method == "c":
print("| Please read the below link to see valid search types. "
"Please note not all have been tested")
print(
"| https://python-plexapi.readthedocs.io/en/latest/modules/video.html?highlight=plexapi.video.Movie#plexapi.video.Movie")
while True:
method = input("| Enter Search method (q to quit): ")
if method in "quit":
break
m_search = " " + method + " "
if m_search in Movie.__doc__ or hasattr(Movie, m_search):
if method[-1:] == "s":
method_p = method[:-1]
else:
method_p = method
value = input("| Enter {}: ".format(method_p))
if config_update:
modify_config(config_path, collection_name, method, value)
else:
add_to_collection(config_path, plex, method, value, selected_collection.title)
break
else:
print("| Search method did not match an attribute for plexapi.video.Movie")
except TypeError:
print("| Bad {} URL".format(l_type))
except KeyError as e:
print("| " + str(e))
if input("| Add more to collection? (y/n): ") == "n":
finished = True
break
else:
print("| " + selected_collection)
break
print("| Search method did not match an attribute for plexapi.video.Movie")
except TypeError:
print("| Bad {} URL".format(l_type))
except KeyError as e:
print("| " + str(e))
if input("| Add more to collection? (y/n): ") == "n":
finished = True
break
except AttributeError:
print("| No collection found")
@ -808,12 +962,11 @@ print("| | _/| |/ -_)\ \ / / _ \| || || _|/ _ \ | (__ / _ \| || |/ -_)/ _|
print("| |_| |_|\___|/_\_\ /_/ \_\\\\_,_| \__|\___/ \___|\___/|_||_|\___|\__| \__||_|\___/|_||_|/__/ |")
print("| |")
print("|===================================================================================================|")
print("| Version 2.5.0")
print("| Version 2.6.0")
print("| Locating config...")
config_path = None
app_dir = os.path.dirname(os.path.abspath(__file__))
if args.config_path and os.path.exists(args.config_path):
config_path = os.path.abspath(args.config_path) # Set config_path from command line switch
elif args.config_path and not os.path.exists(args.config_path):
@ -831,6 +984,7 @@ if args.update:
config = Config(config_path, headless=True)
plex = Plex(config_path)
update_from_config(config_path, plex, True, args.no_meta, args.no_images)
print("|\n|===================================================================================================|")
sys.exit(0)
config = Config(config_path)
@ -920,24 +1074,23 @@ while not mode == "q":
elif mode == "-":
print("|\n|===================================================================================================|")
data = input("| \n| Enter collection name to search for (blank for all): ")
collection = get_collection(plex, data)
if not isinstance(collection, str):
delete_collection(collection)
else:
print("| " + collection)
try:
delete_collection(get_collection(plex, data))
except ValueError as e:
print("| Config Error: {}".format(e))
elif mode == "s":
print("|\n|===================================================================================================|")
data = input("| \n| Enter collection name to search for (blank for all): ")
collection = get_collection(plex, data)
if not isinstance(collection, str):
try:
collection = get_collection(plex, data)
print("| Found {} collection {}".format(collection.subtype, collection.title))
items = collection.children
print("| {}s in collection: ".format(collection.subtype).capitalize())
for i, m in enumerate(items):
print("| {}) {}".format(i + 1, m.title))
else:
print("| " + collection)
except ValueError as e:
print("| Error: {}".format(e))
except KeyboardInterrupt:
print()
pass

View file

@ -100,22 +100,15 @@ def get_collection(plex, data, exact=None, subtype=None):
if selection >= 0:
return collection_list[selection]
elif selection == -1:
return "No collection selected"
raise ValueError("No collection selected")
else:
print("| Invalid entry")
except (IndexError, ValueError) as E:
print("| Invalid entry")
elif len(collection_list) == 1:
if exact:
# if collection_list[0] == data:
if collection_list[0].title == data:
return collection_list[0]
else:
return "Collection not in Plex, please update from config first"
else:
return collection_list[0]
elif len(collection_list) == 1 and (exact is None or (exact and collection_list[0].title == data)):
return collection_list[0]
else:
return "No collection found"
raise ValueError("Collection {} not found".format(data))
def add_to_collection(config_path, plex, method, value, c, map, filters=None):
movies = []
@ -150,16 +143,14 @@ def add_to_collection(config_path, plex, method, value, c, map, filters=None):
elif method == "tautulli" and not Tautulli.valid:
raise KeyError("| tautulli connection required for {}",format(method))
elif plex.library_type == "movie":
if method == "imdb_list":
if method == "plex_collection":
movies = value.children
elif method == "imdb_list":
movies, missing = imdb_tools.imdb_get_movies(config_path, plex, value)
elif method in ["tmdb_list", "tmdb_id", "tmdb_movie", "tmdb_collection", "tmdb_company"]:
elif "tmdb" in method:
movies, missing = imdb_tools.tmdb_get_movies(config_path, plex, value, method)
elif method == "trakt_list":
movies, missing = trakt_tools.trakt_get_movies(config_path, plex, value)
elif method == "trakt_trending":
movies, missing = trakt_tools.trakt_get_movies(config_path, plex, value, list_type='trending')
elif method == "trakt_watchlist":
movies, missing = trakt_tools.trakt_get_movies(config_path, plex, value, list_type='watchlist')
elif "trakt" in method:
movies, missing = trakt_tools.trakt_get_movies(config_path, plex, value, method)
elif method == "tautulli":
movies, missing = imdb_tools.get_tautulli(config_path, plex, value)
elif method == "all":
@ -169,18 +160,14 @@ def add_to_collection(config_path, plex, method, value, c, map, filters=None):
else:
print("| Config Error: {} method not supported".format(method))
elif plex.library_type == "show":
if method == "tmdb_list":
shows, missing = imdb_tools.tmdb_get_shows(config_path, plex, value, is_list=True)
elif method in ["tmdb_id", "tmdb_show"]:
shows, missing = imdb_tools.tmdb_get_shows(config_path, plex, value)
if method == "plex_collection":
shows = value.children
elif "tmdb" in method:
shows, missing = imdb_tools.tmdb_get_shows(config_path, plex, value, method)
elif method == "tvdb_show":
shows, missing = imdb_tools.tvdb_get_shows(config_path, plex, value)
elif method == "trakt_list":
shows, missing = trakt_tools.trakt_get_shows(config_path, plex, value)
elif method == "trakt_trending":
shows, missing = trakt_tools.trakt_get_shows(config_path, plex, value, list_type='trending')
elif method == "trakt_watchlist":
shows, missing = trakt_tools.trakt_get_shows(config_path, plex, value, list_type='watchlist')
elif "trakt" in method:
shows, missing = trakt_tools.trakt_get_shows(config_path, plex, value, method)
elif method == "tautulli":
shows, missing = imdb_tools.get_tautulli(config_path, plex, value)
elif method == "all":

View file

@ -4,23 +4,24 @@ import plex_tools
import trakt
import os
def trakt_get_movies(config_path, plex, data, list_type='userlist'):
def trakt_get_movies(config_path, plex, data, method):
config_tools.TraktClient(config_path)
if list_type == 'userlist':
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = urlparse(trakt_url).path
trakt_list_items = trakt.Trakt[trakt_list_path].items()
elif list_type == 'trending':
if method == "trakt_trending":
max_items = int(data)
trakt_list_items = trakt.Trakt['movies'].trending(per_page=max_items)
elif list_type == 'watchlist':
elif method == "trakt_watchlist":
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = 'users/{}/watchlist'.format(data)
trakt_list_items = [movie for movie in trakt.Trakt[trakt_list_path].movies()]
else:
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = urlparse(trakt_url).path
trakt_list_items = trakt.Trakt[trakt_list_path].items()
title_ids = [m.pk[1] for m in trakt_list_items if isinstance(m, trakt.objects.movie.Movie)]
imdb_map = {}
@ -68,23 +69,23 @@ def trakt_get_movies(config_path, plex, data, list_type='userlist'):
# No movies
return None, None
def trakt_get_shows(config_path, plex, data, list_type='userlist'):
def trakt_get_shows(config_path, plex, data, method):
config_tools.TraktClient(config_path)
if list_type == 'userlist':
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = urlparse(trakt_url).path
trakt_list_items = trakt.Trakt[trakt_list_path].items()
elif list_type == 'trending':
if method == "trakt_trending":
max_items = int(data)
trakt_list_items = trakt.Trakt['shows'].trending(per_page=max_items)
elif list_type == 'watchlist':
elif method == "trakt_watchlist":
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = 'users/{}/watchlist'.format(data)
trakt_list_items = [show for show in trakt.Trakt[trakt_list_path].shows()]
else:
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
trakt_list_path = urlparse(trakt_url).path
trakt_list_items = trakt.Trakt[trakt_list_path].items()
tvdb_map = {}
title_ids = []

View file

@ -37,6 +37,10 @@ collections:
content_rating: G
collection_mode: show_items
collection_order: alpha
Studio Ghibli:
tmdb_company: https://www.themoviedb.org/company/10342
CBS:
tmdb_network: https://www.themoviedb.org/network/16
90s Comedies:
plex_search:
year: [1990,1991,1992,1993,1994,1995,1996,1997,1998,1999]