mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-12-27 03:06:10 +00:00
430 lines
15 KiB
Python
430 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
|
|
|
##################################################################################################
|
|
|
|
from six.moves.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"],
|
|
)
|