jellyfin-kodi/jellyfin_kodi/objects/movies.py

432 lines
15 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals
##################################################################################################
from urllib.parse import urlencode
from kodi_six.utils import py2_encode
from .. import downloader as server
from ..database import jellyfin_db, queries as QUEM
from ..helper import (
api,
stop,
validate,
validate_bluray_dir,
validate_dvd_dir,
jellyfin_item,
values,
Local,
)
from ..helper import LazyLogger
from ..helper.utils import find_library
from ..helper.exceptions import PathValidationException
from .obj import Objects
from .kodi import Movies as KodiDb, queries as QU
##################################################################################################
LOG = LazyLogger(__name__)
##################################################################################################
class Movies(KodiDb):
def __init__(self, server, jellyfindb, videodb, direct_path, library=None):
self.server = server
self.jellyfin = jellyfindb
self.video = videodb
self.direct_path = direct_path
self.jellyfin_db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)
self.objects = Objects()
self.item_ids = []
self.library = library
KodiDb.__init__(self, videodb.cursor)
@stop
@jellyfin_item
def movie(self, item, e_item):
"""If item does not exist, entry will be added.
If item exists, entry will be updated.
"""
server_address = self.server.auth.get_server_info(self.server.auth.server_id)[
"address"
]
API = api.API(item, server_address)
obj = self.objects.map(item, "Movie")
update = True
try:
obj["MovieId"] = e_item[0]
obj["FileId"] = e_item[1]
obj["PathId"] = e_item[2]
obj["LibraryId"] = e_item[6]
obj["LibraryName"] = self.jellyfin_db.get_view_name(obj["LibraryId"])
except TypeError:
update = False
LOG.debug("MovieId %s not found", obj["Id"])
library = self.library or find_library(self.server, item)
if not library:
# This item doesn't belong to a whitelisted library
return
obj["MovieId"] = self.create_entry()
obj["LibraryId"] = library["Id"]
obj["LibraryName"] = library["Name"]
else:
if self.get(*values(obj, QU.get_movie_obj)) is None:
update = False
LOG.info(
"MovieId %s missing from kodi. repairing the entry.", obj["MovieId"]
)
obj["Path"] = API.get_file_path(obj["Path"])
obj["Genres"] = obj["Genres"] or []
obj["Studios"] = [
API.validate_studio(studio) for studio in (obj["Studios"] or [])
]
obj["People"] = obj["People"] or []
obj["Genre"] = " / ".join(obj["Genres"])
obj["Writers"] = " / ".join(obj["Writers"] or [])
obj["Directors"] = " / ".join(obj["Directors"] or [])
obj["Plot"] = API.get_overview(obj["Plot"])
obj["Mpaa"] = API.get_mpaa(obj["Mpaa"])
obj["Resume"] = API.adjust_resume((obj["Resume"] or 0) / 10000000.0)
obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
obj["People"] = API.get_people_artwork(obj["People"])
obj["DateAdded"] = Local(obj["DateAdded"]).split(".")[0].replace("T", " ")
obj["DatePlayed"] = (
None
if not obj["DatePlayed"]
else Local(obj["DatePlayed"]).split(".")[0].replace("T", " ")
)
obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"])
obj["Artwork"] = API.get_all_artwork(self.objects.map(item, "Artwork"))
obj["Video"] = API.video_streams(obj["Video"] or [], obj["Container"])
obj["Audio"] = API.audio_streams(obj["Audio"] or [])
obj["Streams"] = API.media_streams(obj["Video"], obj["Audio"], obj["Subtitles"])
if obj["Premiere"] is not None:
obj["Premiere"] = str(obj["Premiere"]).split("T")[0]
self.get_path_filename(obj)
self.trailer(obj)
if obj["Countries"]:
self.add_countries(*values(obj, QU.update_country_obj))
tags = list(obj["Tags"] or [])
tags.append(obj["LibraryName"])
if obj["Favorite"]:
tags.append("Favorite movies")
obj["Tags"] = tags
if update:
self.movie_update(obj)
else:
self.movie_add(obj)
self.update_path(*values(obj, QU.update_path_movie_obj))
self.update_file(*values(obj, QU.update_file_obj))
self.add_tags(*values(obj, QU.add_tags_movie_obj))
self.add_genres(*values(obj, QU.add_genres_movie_obj))
self.add_studios(*values(obj, QU.add_studios_movie_obj))
self.add_playstate(*values(obj, QU.add_bookmark_obj))
self.add_people(*values(obj, QU.add_people_movie_obj))
self.add_streams(*values(obj, QU.add_streams_obj))
self.artwork.add(obj["Artwork"], obj["MovieId"], "movie")
self.item_ids.append(obj["Id"])
return not update
def movie_add(self, obj):
"""Add object to kodi."""
obj["RatingId"] = self.create_entry_rating()
self.add_ratings(*values(obj, QU.add_rating_movie_obj))
obj["Unique"] = self.create_entry_unique_id()
self.add_unique_id(*values(obj, QU.add_unique_id_movie_obj))
obj["PathId"] = self.add_path(*values(obj, QU.add_path_obj))
obj["FileId"] = self.add_file(*values(obj, QU.add_file_obj))
self.add(*values(obj, QU.add_movie_obj))
self.add_videoversion(*values(obj, QU.add_video_version_obj))
self.jellyfin_db.add_reference(*values(obj, QUEM.add_reference_movie_obj))
LOG.debug(
"ADD movie [%s/%s/%s] %s: %s",
obj["PathId"],
obj["FileId"],
obj["MovieId"],
obj["Id"],
obj["Title"],
)
def movie_update(self, obj):
"""Update object to kodi."""
obj["RatingId"] = self.get_rating_id(*values(obj, QU.get_rating_movie_obj))
self.update_ratings(*values(obj, QU.update_rating_movie_obj))
obj["Unique"] = self.get_unique_id(*values(obj, QU.get_unique_id_movie_obj))
self.update_unique_id(*values(obj, QU.update_unique_id_movie_obj))
self.update(*values(obj, QU.update_movie_obj))
self.jellyfin_db.update_reference(*values(obj, QUEM.update_reference_obj))
LOG.debug(
"UPDATE movie [%s/%s/%s] %s: %s",
obj["PathId"],
obj["FileId"],
obj["MovieId"],
obj["Id"],
obj["Title"],
)
def trailer(self, obj):
try:
if obj["LocalTrailer"]:
trailer = self.server.jellyfin.get_local_trailers(obj["Id"])
obj["Trailer"] = (
"plugin://plugin.video.jellyfin/trailer?id=%s&mode=play"
% trailer[0]["Id"]
)
elif obj["Trailer"]:
obj["Trailer"] = (
"plugin://plugin.video.youtube/play/?video_id=%s"
% obj["Trailer"].rsplit("=", 1)[1]
)
except Exception as error:
LOG.exception("Failed to get trailer: %s", error)
obj["Trailer"] = None
def get_path_filename(self, obj):
"""Get the path and filename and build it into protocol://path"""
obj["Filename"] = (
obj["Path"].rsplit("\\", 1)[1]
if "\\" in obj["Path"]
else obj["Path"].rsplit("/", 1)[1]
)
if self.direct_path:
if not validate(obj["Path"]):
raise PathValidationException("Failed to validate path. User stopped.")
obj["Path"] = obj["Path"].replace(obj["Filename"], "")
"""check dvd directories and point it to ./VIDEO_TS/VIDEO_TS.IFO"""
if validate_dvd_dir(obj["Path"] + obj["Filename"]):
obj["Path"] = obj["Path"] + obj["Filename"] + "/VIDEO_TS/"
obj["Filename"] = "VIDEO_TS.IFO"
LOG.debug("DVD directory %s", obj["Path"])
"""check bluray directories and point it to ./BDMV/index.bdmv"""
if validate_bluray_dir(obj["Path"] + obj["Filename"]):
obj["Path"] = obj["Path"] + obj["Filename"] + "/BDMV/"
obj["Filename"] = "index.bdmv"
LOG.debug("Bluray directory %s", obj["Path"])
else:
obj["Path"] = "plugin://plugin.video.jellyfin/%s/" % obj["LibraryId"]
params = {
"filename": py2_encode(obj["Filename"], "utf-8"),
"id": obj["Id"],
"dbid": obj["MovieId"],
"mode": "play",
}
obj["Filename"] = "%s?%s" % (obj["Path"], urlencode(params))
@stop
@jellyfin_item
def boxset(self, item, e_item):
"""If item does not exist, entry will be added.
If item exists, entry will be updated.
Process movies inside boxset.
Process removals from boxset.
"""
server_address = self.server.auth.get_server_info(self.server.auth.server_id)[
"address"
]
API = api.API(item, server_address)
obj = self.objects.map(item, "Boxset")
obj["Overview"] = API.get_overview(obj["Overview"])
try:
obj["SetId"] = e_item[0]
self.update_boxset(*values(obj, QU.update_set_obj))
except TypeError:
LOG.debug("SetId %s not found", obj["Id"])
obj["SetId"] = self.add_boxset(*values(obj, QU.add_set_obj))
self.boxset_current(obj)
obj["Artwork"] = API.get_all_artwork(self.objects.map(item, "Artwork"))
for movie in obj["Current"]:
temp_obj = dict(obj)
temp_obj["Movie"] = movie
temp_obj["MovieId"] = obj["Current"][temp_obj["Movie"]]
self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
self.jellyfin_db.update_parent_id(
*values(temp_obj, QUEM.delete_parent_boxset_obj)
)
LOG.debug(
"DELETE from boxset [%s] %s: %s",
temp_obj["SetId"],
temp_obj["Title"],
temp_obj["MovieId"],
)
self.artwork.add(obj["Artwork"], obj["SetId"], "set")
self.jellyfin_db.add_reference(*values(obj, QUEM.add_reference_boxset_obj))
LOG.debug("UPDATE boxset [%s] %s", obj["SetId"], obj["Title"])
def boxset_current(self, obj):
"""Add or removes movies based on the current movies found in the boxset."""
try:
current = self.jellyfin_db.get_item_id_by_parent_id(
*values(obj, QUEM.get_item_id_by_parent_boxset_obj)
)
movies = dict(current)
except ValueError:
movies = {}
obj["Current"] = movies
for all_movies in server.get_movies_by_boxset(obj["Id"]):
for movie in all_movies["Items"]:
temp_obj = dict(obj)
temp_obj["Title"] = movie["Name"]
temp_obj["Id"] = movie["Id"]
try:
temp_obj["MovieId"] = self.jellyfin_db.get_item_by_id(
*values(temp_obj, QUEM.get_item_obj)
)[0]
except TypeError:
LOG.info("Failed to process %s to boxset.", temp_obj["Title"])
continue
if temp_obj["Id"] not in obj["Current"]:
self.set_boxset(*values(temp_obj, QU.update_movie_set_obj))
self.jellyfin_db.update_parent_id(
*values(temp_obj, QUEM.update_parent_movie_obj)
)
LOG.debug(
"ADD to boxset [%s/%s] %s: %s to boxset",
temp_obj["SetId"],
temp_obj["MovieId"],
temp_obj["Title"],
temp_obj["Id"],
)
else:
obj["Current"].pop(temp_obj["Id"])
def boxsets_reset(self):
"""Special function to remove all existing boxsets."""
boxsets = self.jellyfin_db.get_items_by_media("set")
for boxset in boxsets:
self.remove(boxset[0])
@stop
@jellyfin_item
def userdata(self, item, e_item):
"""This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
Poster with progress bar
"""
server_address = self.server.auth.get_server_info(self.server.auth.server_id)[
"address"
]
API = api.API(item, server_address)
obj = self.objects.map(item, "MovieUserData")
try:
obj["MovieId"] = e_item[0]
obj["FileId"] = e_item[1]
except TypeError:
return
obj["Resume"] = API.adjust_resume((obj["Resume"] or 0) / 10000000.0)
obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"])
if obj["DatePlayed"]:
obj["DatePlayed"] = Local(obj["DatePlayed"]).split(".")[0].replace("T", " ")
if obj["Favorite"]:
self.get_tag(*values(obj, QU.get_tag_movie_obj))
else:
self.remove_tag(*values(obj, QU.delete_tag_movie_obj))
LOG.debug("New resume point %s: %s", obj["Id"], obj["Resume"])
self.add_playstate(*values(obj, QU.add_bookmark_obj))
self.jellyfin_db.update_reference(*values(obj, QUEM.update_reference_obj))
LOG.debug(
"USERDATA movie [%s/%s] %s: %s",
obj["FileId"],
obj["MovieId"],
obj["Id"],
obj["Title"],
)
@stop
@jellyfin_item
def remove(self, item_id, e_item):
"""Remove movieid, fileid, jellyfin reference.
Remove artwork, boxset
"""
obj = {"Id": item_id}
try:
obj["KodiId"] = e_item[0]
obj["FileId"] = e_item[1]
obj["Media"] = e_item[4]
except TypeError:
return
self.artwork.delete(obj["KodiId"], obj["Media"])
if obj["Media"] == "movie":
self.delete(*values(obj, QU.delete_movie_obj))
elif obj["Media"] == "set":
for movie in self.jellyfin_db.get_item_by_parent_id(
*values(obj, QUEM.get_item_by_parent_movie_obj)
):
temp_obj = dict(obj)
temp_obj["MovieId"] = movie[1]
temp_obj["Movie"] = movie[0]
self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
self.jellyfin_db.update_parent_id(
*values(temp_obj, QUEM.delete_parent_boxset_obj)
)
self.delete_boxset(*values(obj, QU.delete_set_obj))
self.jellyfin_db.remove_item(*values(obj, QUEM.delete_item_obj))
LOG.debug(
"DELETE %s [%s/%s] %s",
obj["Media"],
obj["FileId"],
obj["KodiId"],
obj["Id"],
)