Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #2

Merged
merged 13 commits into from
Nov 20, 2019
Merged
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
/.gitignore
/.idea
/build
/plex_auto_collections.spec
/plex_auto_collections.spec
/__pycache__/
96 changes: 60 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
![https://i.imgur.com/iHAYFIZ.png](https://i.imgur.com/iHAYFIZ.png)
Plex Auto Collections is a Python 3 script/[standalone builds](https://github.com/vladimir-tutin/Plex-Auto-Collections/tree/master/dist) that
works off a configuration file to create/update Plex collection. Collection management with this tool can be automated
in a varying degree of customizability. Supports IMDB and TMDb lists as well as built in Plex
in a varying degree of customizability. Supports IMDB, TMDb, and Trakt lists as well as built in Plex
filters such as actors, genres, year, studio and more. For more filters refer to the
[plexapi.video.Movie](https://python-plexapi.readthedocs.io/en/latest/modules/video.html#plexapi.video.Movie)
documentation. Not everything has been tested, so results may vary based off the filter. A TMDb api key is required to
scan TMDb URLs.

When parsing IMDB or TMBd lists the script will create a list of movies that are missing from Plex. If an TMDb and
Radarr api-key are supplied then the option will be presented to pass the list of movies along to Radarr.
Radarr api-key are supplied then the option will be presented to pass the list of movies along to Radarr. Trakt lists will be matched against items in both a Movie and a TV library, each.

As well as updating collections based off configuration files there is the ability to add new collections based off
filters, delete collections, search for collections and manage the collections in the configuration file. Collection
Expand All @@ -29,7 +29,7 @@ If using TMDb lists be sure to include your TMDb api-key. If you do not have an
[document](https://developers.themoviedb.org/3/getting-started/introduction).

If you do not want it to have the option to submit movies that are missing from IMDB or TMBd lists do not include the
api-key for Radarr.
api-key for Radarr. Radarr support has not been tested with Trakt lists. Sonarr support has not yet been implemented.

Adding a summary to the collection is possible by either pulling the overview from TMDb or by using a custom entry. To
use a TMDb entry a TMDb api-key as well as language is required, the default language is set to en. Match the following
Expand All @@ -55,7 +55,7 @@ Adding a poster can be done by adding the URL to the image.
details:
tmdb-summary: 328
poster: https://i.imgur.com/QMjbyCX.png

Local assets are supported by running the script with the image server running. If there are no details filled out for
the poster in the configuration file and the image server is running the script will attempt to match a collection name
with an image file of the same name. Images should be placed in the ./images folder. Port forwarding is not required.
Expand All @@ -65,6 +65,13 @@ If you want movies to add to Radarr but not automatically search, change search
In order to find your Plex token follow
[this guide](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/).

Trakt lists require a client id and client secret.
1. [Create](https://trakt.tv/oauth/applications/new) a Trakt API application.
2. Enter a `Name` for the application.
3. Enter `urn:ietf:wg:oauth:2.0:oob` for `Redirect uri`.
4. Click the `SAVE APP` button.
5. Record the `Client ID` and `Client Secret`.

Library should be the name of the Plex library that you are wanting to search and create collections in.

Main filters allowed are actors, imdb-list as well as many attributes that can found in the [plexapi.video.Movie
Expand All @@ -80,38 +87,55 @@ each must match what Plex has including special characters in order to match.
If you do not want to use subfilters simply remove the section.

**Once complete it should look like**

collections:
Jurassic Park:
tmdb-list: https://www.themoviedb.org/collection/328
details:
tmdb-summary: 328
poster: https://i.imgur.com/QMjbyCX.png
1080p Documentaries:
genres: Documentary
subfilters:
video-resolution: 1080
details:
summary: A collection of 1080p Documentaries
Daniel Craig only James Bonds:
imdb-list: https://www.imdb.com/list/ls006405458/
subfilters:
actors: Daniel Craig
plex:
library: Movies
token: ###################
url: http://192.168.1.5:32400
radarr:
url: http://192.168.1.5:7878/radarr/
token: ###########################
quality_profile_id: 4
search: true
tmdb:
apikey: ############################
language: en
image-server:
host: 192.168.1.41
port: 5000
```
collections:
Jurassic Park:
tmdb-list: https://www.themoviedb.org/collection/328
details:
tmdb-summary: 328
poster: https://i.imgur.com/QMjbyCX.png
1080p Documentaries:
genres: Documentary
subfilters:
video-resolution: 1080
details:
summary: A collection of 1080p Documentaries
Daniel Craig only James Bonds:
imdb-list: https://www.imdb.com/list/ls006405458/
subfilters:
actors: Daniel Craig
Disney+ TV:
trakt-list: https://trakt.tv/users/drew-casteo/lists/disney-tv-shows
Disney+ Movies:
trakt-list: https://trakt.tv/users/drew-casteo/lists/disney-movies
plex:
movie_library: Movies
show_library: TV Shows
token: ################
url: http://192.168.1.5:32400
radarr:
url: http://192.168.1.5:7878/radarr/
token: ################
quality_profile_id: 4
search: true
tmdb:
apikey: ################
language: en
trakt:
client_id: ################
client_secret: ################
# Below is filled in automatically when the script is run
authorization:
access_token:
token_type:
expires_in:
refresh_token:
scope:
created_at:
image-server:
host: 192.168.1.41
port: 5000
```

# Usage
[Standalone binaries](https://github.com/vladimir-tutin/Plex-Auto-Collections/tree/master/dist) have been created for both Windows and Linux.
Expand Down
19 changes: 15 additions & 4 deletions config.yml.template
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,34 @@ collections:
actors: Dave Chappelle
details:
tmdb-summary: 4169
Marvel Cinematic Universe:
trakt-list: https://trakt.tv/users/signoftheshadow/lists/mcu
Disney+ TV:
trakt-list: https://trakt.tv/users/drew-casteo/lists/disney-tv-shows
Disney+ Movies:
trakt-list: https://trakt.tv/users/drew-casteo/lists/disney-movies
plex:
library: Movies
movie_library: Movies
show_library: TV Shows
token: ###################
url: http://192.168.1.5:32400
radarr:
url: http://192.168.1.5:7878/radarr/
token: ###########################
quality_profile_id: 4
search: true
search: false
tmdb:
apikey: ############################
language: en
trakt:
client_id: ############################
client_secret: ############################
# Below is filled in automatically when the script is run
authorization:
access_token:
token_type:
expires_in:
refresh_token:
scope:
created_at:
image-server:
host: 0.0.0.0
port: 5000
71 changes: 57 additions & 14 deletions config_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
import socket
from plexapi.server import PlexServer
from plexapi.video import Movie
from plexapi.video import Show
from plexapi.library import MovieSection
from plexapi.library import ShowSection
from plex_tools import get_actor_rkey
from plex_tools import add_to_collection
from plex_tools import get_collection
from radarr_tools import add_to_radarr
from imdb_tools import tmdb_get_summary
from trakt import Trakt
import trakt_helpers

class Config:
def __init__(self):
Expand All @@ -29,10 +34,16 @@ def __init__(self):
config = Config().plex
self.url = config['url']
self.token = config['token']
self.library = config['library']
self.Server = PlexServer(self.url, self.token)
self.MovieLibrary = self.Server.library.section(self.library)
self.timeout = 60
# self.library = config['library']
self.movie_library = config['movie_library']
self.show_library = config['show_library']
self.Server = PlexServer(self.url, self.token, timeout=self.timeout)
self.Sections = self.Server.library.sections()
self.MovieLibrary = next((s for s in self.Sections if (s.title == self.movie_library) and (isinstance(s, MovieSection))), None)
self.ShowLibrary = next((s for s in self.Sections if (s.title == self.show_library) and (isinstance(s, ShowSection))), None)
self.Movie = Movie
self.Show = Show


class Radarr:
Expand All @@ -50,11 +61,18 @@ def __init__(self):
self.language = config['language']


class Trakt:
class TraktClient:
def __init__(self):
config = Config().trakt
self.client_id = config['client_id']
self.client_secret = config['client_secret']
self.authorization = config['authorization']
Trakt.configuration.defaults.client(self.client_id, self.client_secret)
# Try the token from the config
self.updated_authorization = trakt_helpers.authenticate(self.authorization)
Trakt.configuration.defaults.oauth.from_response(self.updated_authorization)
if self.updated_authorization != self.authorization:
trakt_helpers.save_authorization(Config().config_path, self.updated_authorization)


class ImageServer:
Expand Down Expand Up @@ -91,34 +109,59 @@ def update_from_config(plex, skip_radarr=False):
if m == "actors" or m == "actor":
v = get_actor_rkey(plex, v)
try:
missing = add_to_collection(plex, m, v, c, subfilters)
missing_movies, missing_shows = add_to_collection(plex, m, v, c, subfilters)
except UnboundLocalError: # No sub-filters
missing = add_to_collection(plex, m, v, c)
except KeyError as e:
missing_movies, missing_shows = add_to_collection(plex, m, v, c)
except (KeyError, ValueError) as e:
print(e)
missing = False
if missing:
missing_movies = False
missing_shows = False
if missing_movies:
if "imdb" in m:
m = "IMDb"
elif "trakt in m":
m = "Trakt"
else:
m = "TMDb"
print("{} missing movies from {} List: {}".format(len(missing), m, v))
print("{} missing movies from {} List: {}".format(len(missing_movies), m, v))
if not skip_radarr:
if input("Add missing movies to Radarr? (y/n): ").upper() == "Y":
add_to_radarr(missing)
add_to_radarr(missing_movies)
if missing_shows:
if "trakt in m":
m = "Trakt"
else:
m = "TMDb"
print("{} missing shows from {} List: {}".format(len(missing_shows), m, v))
# if not skip_sonarr:
# if input("Add missing shows to Sonarr? (y/n): ").upper() == "Y":
# add_to_radarr(missing_shows)
# Multiple collections of the same name
if "details" in collections[c]:
# Check if there are multiple collections with the same name
movie_collections = plex.MovieLibrary.search(title=c, libtype="collection")
show_collections = plex.ShowLibrary.search(title=c, libtype="collection")
if len(movie_collections + show_collections) > 1:
print("Multiple collections named {}.\nUpdate of \"details\" is currently unsupported.".format(c))
continue
plex_collection = get_collection(plex, c)
for dt_m in collections[c]["details"]:
rkey = get_collection(plex, c).ratingKey
rkey = plex_collection.ratingKey
subtype = plex_collection.subtype
dt_v = collections[c]["details"][dt_m]
if "summary" in dt_m:
if "tmdb" in dt_m:
try:
dt_v = tmdb_get_summary(dt_v, "overview")
except AttributeError:
dt_v = tmdb_get_summary(dt_v, "biography")
if subtype == 'movie':
library_name = plex.MovieLibrary
elif subtype == 'show':
library_name = plex.ShowLibrary

library_name = plex.library
section = plex.Server.library.section(library_name).key
#section = plex.Server.library.section(library_name).key
section = library_name.key
url = plex.url + "/library/sections/" + str(section) + "/all"

querystring = {"type":"18",
Expand Down
49 changes: 0 additions & 49 deletions imdb_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
from tmdbv3api import Collection
from tmdbv3api import Person
import config_tools
from urllib.parse import urlparse
import trakt


def imdb_get_movies(plex, data):
Expand Down Expand Up @@ -116,53 +114,6 @@ def tmdb_get_movies(plex, data):
return matched, missing


def trakt_get_movies(plex, data):
tmdb = TMDb()
tmdb.api_key = config_tools.TMDB().apikey # Set TMDb api key for Collection
trakt.Trakt.configuration.defaults.client(config_tools.Trakt().client_id, config_tools.Trakt().client_secret)
movie = Movie()
tmdb.api_key = config_tools.TMDB().apikey
trakt_url = data
if trakt_url[-1:] == " ":
trakt_url = trakt_url[:-1]
imdb_map = {}
library_language = plex.MovieLibrary.language
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)]

plex_movies = plex.MovieLibrary.all()
if title_ids:
for m in plex_movies:
if 'themoviedb://' in m.guid:
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
else:
imdb_id = None
elif 'imdb://' in m.guid:
imdb_id = m.guid.split('imdb://')[1].split('?')[0]
else:
imdb_id = None

if imdb_id and imdb_id in title_ids:
imdb_map[imdb_id] = m
else:
imdb_map[m.ratingKey] = m

matched_imbd_movies = []
missing_imdb_movies = []
for imdb_id in title_ids:
movie = imdb_map.pop(imdb_id, None)
if movie:
matched_imbd_movies.append(plex.Server.fetchItem(movie.ratingKey))
else:
missing_imdb_movies.append(imdb_id)

return matched_imbd_movies, missing_imdb_movies


def tmdb_get_summary(data, type):
collection = Collection()
person = Person()
Expand Down
Loading