jellyfin-kodi/jellyfin_kodi/objects/obj.py

168 lines
4.9 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals
##################################################################################################
import json
import os
from six import ensure_text
from ..helper import LazyLogger, get_filesystem_encoding
##################################################################################################
LOG = LazyLogger(__name__)
##################################################################################################
class Objects(object):
# Borg - multiple instances, shared state
_shared_state = {}
def __init__(self):
"""Hold all persistent data here."""
self.__dict__ = self._shared_state
def mapping(self):
"""Load objects mapping."""
file_dir = os.path.dirname(ensure_text(__file__, get_filesystem_encoding()))
with open(os.path.join(file_dir, "obj_map.json")) as infile:
self.objects = json.load(infile)
def map(self, item, mapping_name):
"""Syntax to traverse the item dictionary.
This of the query almost as a url.
Item is the Jellyfin item json object structure
",": each element will be used as a fallback until a value is found.
"?": split filters and key name from the query part, i.e. MediaSources/0?$Name
"$": lead the key name with $. Only one key value can be requested per element.
":": indicates it's a list of elements [], i.e. MediaSources/0/MediaStreams:?$Name
MediaStreams is a list.
"/": indicates where to go directly
"""
self.mapped_item = {}
if not mapping_name:
raise Exception("execute mapping() first")
mapping = self.objects[mapping_name]
for key, value in mapping.items():
self.mapped_item[key] = None
params = value.split(",")
for param in params:
obj = item
obj_param = param
obj_key = ""
obj_filters = {}
if "?" in obj_param:
if "$" in obj_param:
obj_param, obj_key = obj_param.rsplit("$", 1)
obj_param, filters = obj_param.rsplit("?", 1)
if filters:
for filter in filters.split("&"):
filter_key, filter_value = filter.split("=")
obj_filters[filter_key] = filter_value
if ":" in obj_param:
result = []
for d in self.__recursiveloop__(obj, obj_param):
if not obj_filters or self.__filters__(d, obj_filters):
result.append(d)
obj = result
obj_filters = {}
elif "/" in obj_param:
obj = self.__recursive__(obj, obj_param)
elif obj is item and obj is not None:
obj = item.get(obj_param)
if obj_filters and obj and not self.__filters__(obj, obj_filters):
obj = None
if obj is None and len(params) != params.index(param):
continue
if obj_key:
obj = (
[d[obj_key] for d in obj if d.get(obj_key)]
if isinstance(obj, list)
else obj.get(obj_key)
)
self.mapped_item[key] = obj
break
if (
not mapping_name.startswith("Browse")
and not mapping_name.startswith("Artwork")
and not mapping_name.startswith("UpNext")
):
self.mapped_item["ProviderName"] = self.objects.get(
"%sProviderName" % mapping_name
)
self.mapped_item["Checksum"] = json.dumps(item["UserData"])
return self.mapped_item
def __recursiveloop__(self, obj, keys):
first, rest = keys.split(":", 1)
obj = self.__recursive__(obj, first)
if obj:
for item in obj:
if rest:
self.__recursiveloop__(item, rest)
else:
yield item
def __recursive__(self, obj, keys):
for string in keys.split("/"):
if not obj:
return
obj = obj[int(string)] if string.isdigit() else obj.get(string)
return obj
def __filters__(self, obj, filters):
result = False
for key, value in filters.items():
inverse = False
if value.startswith("!"):
inverse = True
value = value.split("!", 1)[1]
if value.lower() == "null":
value = None
result = obj.get(key) != value if inverse else obj.get(key) == value
return result