Reworking the network stack

This commit is contained in:
Matt
2022-02-27 22:15:54 -05:00
parent 816277512d
commit 36b23d0b19
16 changed files with 875 additions and 1130 deletions

152
resources/lib/api.py Normal file
View File

@@ -0,0 +1,152 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcaddon
from kodi_six.utils import py2_decode
import requests
from .utils import get_device_id, get_version
from .loghandler import LazyLogger
log = LazyLogger(__name__)
class API:
def __init__(self, server=None, user_id=None, token=None):
self.server = server
self.user_id = user_id
self.token = token
self.settings = xbmcaddon.Addon()
self.headers = {}
self.create_headers()
def get(self, path):
if not self.headers:
self.create_headers()
url = '{}{}'.format(self.server, path)
r = requests.get(url, headers=self.headers)
return r.json()
def post(self, url, payload):
if not self.headers:
self.create_headers()
url = '{}{}'.format(self.server, url)
r = requests.post(url, json=payload, headers=self.headers)
try:
response_data = r.json()
except:
response_data = {}
return response_data
def delete(self, url):
url = '{}{}'.format(self.server, url)
r = requests.delete(url, headers=self.headers)
def get_user_items(self, path):
url = '/Users/{}/{}'.format(self.user_id, path)
r = self.get('url')
def authenticate(self, auth_data):
response = self.post('/Users/AuthenticateByName', auth_data)
token = response.get('AccessToken')
if token:
self.token = token
self.user_id = response.get('User').get('Id')
# Create headers again to include auth token
self.create_headers()
return response
else:
log.error('Unable to authenticate to Jellyfin server')
return {}
def create_headers(self):
# If the headers already exist with an auth token, return
if self.headers and 'x-mediabrowser-token' in self.headers:
return
headers = {}
device_name = self.settings.getSetting('deviceName')
if len(device_name) == 0:
device_name = "JellyCon"
# Ensure ascii and remove invalid characters
device_name = py2_decode(device_name).replace('"', '_').replace(',', '_')
device_id = get_device_id()
version = get_version()
authorization = (
'MediaBrowser Client="Kodi JellyCon", Device="{device}", '
'DeviceId="{device_id}", Version="{version}"'
).format(
device=device_name,
device_id=device_id,
version=version
)
headers['x-emby-authorization'] = authorization
# If we have a valid token, ensure it's included in the headers
if self.token:
headers['x-mediabrowser-token'] = self.token
# Make headers available to api calls
self.headers = headers
def post_capabilities(self):
url = '{}/Sessions/Capabilities/Full'.format(self.server)
data = {
'SupportsMediaControl': True,
'PlayableMediaTypes': ["Video", "Audio"],
'SupportedCommands': ["MoveUp",
"MoveDown",
"MoveLeft",
"MoveRight",
"Select",
"Back",
"ToggleContextMenu",
"ToggleFullscreen",
"ToggleOsdMenu",
"GoHome",
"PageUp",
"NextLetter",
"GoToSearch",
"GoToSettings",
"PageDown",
"PreviousLetter",
"TakeScreenshot",
"VolumeUp",
"VolumeDown",
"ToggleMute",
"SendString",
"DisplayMessage",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"SetRepeatMode",
"Mute",
"Unmute",
"SetVolume",
"PlayNext",
"Play",
"Playstate",
"PlayMediaSource"]
}
self.post(url, data)
def speedtest(self, test_data_size):
self.create_headers()
url = '{}/playback/bitratetest?size={}'.format(self.server, test_data_size)
response = requests.get(url, stream=True, headers=self.headers)
return response

View File

@@ -14,15 +14,13 @@ import xbmcplugin
import xbmc
import xbmcaddon
from .downloadutils import DownloadUtils
from .loghandler import LazyLogger
from .jsonrpc import JsonRpc, get_value
from .datamanager import DataManager
from .utils import translate_string
from .utils import translate_string, load_user_details
from .kodi_utils import HomeWindow
from .item_functions import get_art
downloadUtils = DownloadUtils()
log = LazyLogger(__name__)
@@ -214,9 +212,11 @@ class CacheArtwork(threading.Thread):
def get_jellyfin_artwork(self, progress):
log.debug("get_jellyfin_artwork")
user_details = load_user_details()
user_id = user_details.get('user_id')
url = ""
url += "{server}/Users/{userid}/Items"
url += "/Users/{}/Items".format(user_id)
url += "?Recursive=true"
url += "&EnableUserData=False"
url += "&Fields=BasicSyncInfo"
@@ -232,7 +232,7 @@ class CacheArtwork(threading.Thread):
if isinstance(results, dict):
results = results.get("Items")
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
log.debug("Jellyfin Item Count Count: {0}".format(len(results)))
if self.stop_all_activity:

View File

@@ -7,13 +7,13 @@ import os
import time
from six.moves import cPickle
from .downloadutils import DownloadUtils
from .api import API
from .loghandler import LazyLogger
from .item_functions import extract_item_info
from .kodi_utils import HomeWindow
from .tracking import timer
from .filelock import FileLock
from .utils import translate_string
from .utils import translate_string, load_user_details
import xbmc
import xbmcaddon
@@ -42,11 +42,18 @@ class DataManager:
addon_dir = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('profile'))
def __init__(self, *args):
pass
self.user_details = load_user_details()
settings = xbmcaddon.Addon()
self.api = API(
settings.getSetting('server_address'),
self.user_details.get('user_id'),
self.user_details.get('token')
)
@timer
def get_content(self, url):
return DownloadUtils().download_url(url)
return self.api.get(url)
@timer
def get_items(self, url, gui_options, use_cache=False):
@@ -55,9 +62,8 @@ class DataManager:
log.debug("last_content_url : use_cache={0} url={1}".format(use_cache, url))
home_window.set_property("last_content_url", url)
download_utils = DownloadUtils()
user_id = download_utils.get_user_id()
server = download_utils.get_server()
user_id = self.user_details.get('user_id')
server = self.api.server
m = hashlib.md5()
m.update('{}|{}|{}'.format(user_id, server, url).encode())

View File

@@ -10,10 +10,9 @@ import sys
import re
from .datamanager import DataManager
from .downloadutils import DownloadUtils
from .loghandler import LazyLogger
from .item_functions import add_gui_item, ItemDetails
from .utils import send_event_notification, translate_string
from .utils import send_event_notification, translate_string, load_user_details, get_default_filters
from .tracking import timer
log = LazyLogger(__name__)
@@ -218,8 +217,9 @@ def process_directory(url, progress, params, use_cache_data=False):
data_manager = DataManager()
settings = xbmcaddon.Addon()
download_utils = DownloadUtils()
server = download_utils.get_server()
server = settings.getSetting('server_address')
user_details = load_user_details()
user_id = user_details.get('user_id')
name_format = params.get("name_format", None)
name_format_type = None
@@ -248,13 +248,13 @@ def process_directory(url, progress, params, use_cache_data=False):
if flatten_single_season and len(item_list) == 1 and item_list[0].item_type == "Season":
season_id = item_list[0].id
series_id = item_list[0].series_id
season_url = ('{server}/Shows/' + series_id +
season_url = ('/Shows/' + series_id +
'/Episodes'
'?userId={userid}' +
'?userId={}'.format(user_id) +
'&seasonId=' + season_id +
'&IsVirtualUnAired=false' +
'&IsMissing=false' +
'&Fields=SpecialEpisodeNumbers,{field_filters}' +
'&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) +
'&format=json')
if progress is not None:
progress.close()
@@ -313,28 +313,28 @@ def process_directory(url, progress, params, use_cache_data=False):
if item_details.is_folder is True:
if item_details.item_type == "Series":
u = ('{server}/Shows/' + item_details.id +
u = ('/Shows/' + item_details.id +
'/Seasons'
'?userId={userid}' +
'&Fields={field_filters}' +
'?userId={}'.format(user_id) +
'&Fields={}'.format(get_default_filters()) +
'&format=json')
elif item_details.item_type == "Season":
u = ('{server}/Shows/' + item_details.series_id +
u = ('/Shows/' + item_details.series_id +
'/Episodes'
'?userId={userid}' +
'?userId={}'.format(user_id) +
'&seasonId=' + item_details.id +
'&IsVirtualUnAired=false' +
'&IsMissing=false' +
'&Fields=SpecialEpisodeNumbers,{field_filters}' +
'&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) +
'&format=json')
else:
u = ('{server}/Users/{userid}/items' +
u = ('/Users/{}/items'.format(user_id) +
'?ParentId=' + item_details.id +
'&IsVirtualUnAired=false' +
'&IsMissing=false' +
'&Fields={field_filters}' +
'&Fields={}'.format(get_default_filters()) +
'&format=json')
default_sort = item_details.item_type == "Playlist"
@@ -347,7 +347,7 @@ def process_directory(url, progress, params, use_cache_data=False):
log.debug("Dropping empty folder item : {0}".format(item_details.__dict__))
elif item_details.item_type == "MusicArtist":
u = ('{server}/Users/{userid}/items' +
u = ('/Users/{}/items'.format(user_id) +
'?ArtistIds=' + item_details.id +
'&IncludeItemTypes=MusicAlbum' +
'&CollapseBoxSetItems=false' +
@@ -369,12 +369,12 @@ def process_directory(url, progress, params, use_cache_data=False):
and first_season_item is not None
and len(dir_items) > 1
and first_season_item.series_id is not None):
series_url = ('{server}/Shows/' + first_season_item.series_id +
series_url = ('/Shows/' + first_season_item.series_id +
'/Episodes'
'?userId={userid}' +
'?userId={}'.format(user_id) +
'&IsVirtualUnAired=false' +
'&IsMissing=false' +
'&Fields=SpecialEpisodeNumbers,{field_filters}' +
'&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) +
'&format=json')
played = 0
overlay = "7"

View File

@@ -1,615 +0,0 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcgui
import xbmcaddon
import requests
import json
from six.moves.urllib.parse import urlparse
from base64 import b64encode
from collections import defaultdict
from traceback import format_exc
from kodi_six.utils import py2_decode
from .kodi_utils import HomeWindow
from .loghandler import LazyLogger
from .tracking import timer
from .utils import get_device_id, get_version, translate_string, load_user_details, save_user_details
log = LazyLogger(__name__)
def get_details_string():
addon_settings = xbmcaddon.Addon()
include_media = addon_settings.getSetting("include_media") == "true"
include_people = addon_settings.getSetting("include_people") == "true"
include_overview = addon_settings.getSetting("include_overview") == "true"
filer_list = [
"DateCreated",
"EpisodeCount",
"SeasonCount",
"Path",
"Genres",
"Studios",
"Etag",
"Taglines",
"SortName",
"RecursiveItemCount",
"ChildCount",
"ProductionLocations",
"CriticRating",
"OfficialRating",
"CommunityRating",
"PremiereDate",
"ProductionYear",
"AirTime",
"Status",
"Tags"
]
if include_media:
filer_list.append("MediaStreams")
if include_people:
filer_list.append("People")
if include_overview:
filer_list.append("Overview")
return ",".join(filer_list)
class DownloadUtils:
use_https = False
verify_cert = False
def __init__(self, *args):
settings = xbmcaddon.Addon()
self.use_https = False
if settings.getSetting('protocol') == "1":
self.use_https = True
log.debug("use_https: {0}".format(self.use_https))
self.verify_cert = settings.getSetting('verify_cert') == 'true'
log.debug("verify_cert: {0}".format(self.verify_cert))
def post_capabilities(self):
url = "{server}/Sessions/Capabilities/Full?format=json"
data = {
'SupportsMediaControl': True,
'PlayableMediaTypes': ["Video", "Audio"],
'SupportedCommands': ["MoveUp",
"MoveDown",
"MoveLeft",
"MoveRight",
"Select",
"Back",
"ToggleContextMenu",
"ToggleFullscreen",
"ToggleOsdMenu",
"GoHome",
"PageUp",
"NextLetter",
"GoToSearch",
"GoToSettings",
"PageDown",
"PreviousLetter",
"TakeScreenshot",
"VolumeUp",
"VolumeDown",
"ToggleMute",
"SendString",
"DisplayMessage",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"SetRepeatMode",
"Mute",
"Unmute",
"SetVolume",
"PlayNext",
"Play",
"Playstate",
"PlayMediaSource"]
}
self.download_url(url, post_body=data, method="POST")
log.debug("Posted Capabilities: {0}".format(data))
def get_item_playback_info(self, item_id, force_transcode):
addon_settings = xbmcaddon.Addon()
filtered_codecs = []
if addon_settings.getSetting("force_transcode_h265") == "true":
filtered_codecs.append("hevc")
filtered_codecs.append("h265")
if addon_settings.getSetting("force_transcode_mpeg2") == "true":
filtered_codecs.append("mpeg2video")
if addon_settings.getSetting("force_transcode_msmpeg4v3") == "true":
filtered_codecs.append("msmpeg4v3")
if addon_settings.getSetting("force_transcode_mpeg4") == "true":
filtered_codecs.append("mpeg4")
playback_bitrate = addon_settings.getSetting("max_stream_bitrate")
force_playback_bitrate = addon_settings.getSetting("force_max_stream_bitrate")
if force_transcode:
playback_bitrate = force_playback_bitrate
audio_codec = addon_settings.getSetting("audio_codec")
audio_playback_bitrate = addon_settings.getSetting("audio_playback_bitrate")
audio_max_channels = addon_settings.getSetting("audio_max_channels")
audio_bitrate = int(audio_playback_bitrate) * 1000
bitrate = int(playback_bitrate) * 1000
profile = {
"Name": "Kodi",
"MaxStaticBitrate": bitrate,
"MaxStreamingBitrate": bitrate,
"MusicStreamingTranscodingBitrate": audio_bitrate,
"TimelineOffsetSeconds": 5,
"TranscodingProfiles": [
{
"Type": "Audio"
},
{
"Container": "ts",
"Protocol": "hls",
"Type": "Video",
"AudioCodec": audio_codec,
"VideoCodec": "h264",
"MaxAudioChannels": audio_max_channels
},
{
"Container": "jpeg",
"Type": "Photo"
}
],
"DirectPlayProfiles": [
{
"Type": "Video"
},
{
"Type": "Audio"
},
{
"Type": "Photo"
}
],
"ResponseProfiles": [],
"ContainerProfiles": [],
"CodecProfiles": [],
"SubtitleProfiles": [
{
"Format": "srt",
"Method": "External"
},
{
"Format": "srt",
"Method": "Embed"
},
{
"Format": "ass",
"Method": "External"
},
{
"Format": "ass",
"Method": "Embed"
},
{
"Format": "sub",
"Method": "Embed"
},
{
"Format": "sub",
"Method": "External"
},
{
"Format": "ssa",
"Method": "Embed"
},
{
"Format": "ssa",
"Method": "External"
},
{
"Format": "smi",
"Method": "Embed"
},
{
"Format": "smi",
"Method": "External"
},
{
"Format": "pgssub",
"Method": "Embed"
},
{
"Format": "pgssub",
"Method": "External"
},
{
"Format": "dvdsub",
"Method": "Embed"
},
{
"Format": "dvdsub",
"Method": "External"
},
{
"Format": "pgs",
"Method": "Embed"
},
{
"Format": "pgs",
"Method": "External"
}
]
}
if len(filtered_codecs) > 0:
profile['DirectPlayProfiles'][0]['VideoCodec'] = "-%s" % ",".join(filtered_codecs)
if force_transcode:
profile['DirectPlayProfiles'] = []
if addon_settings.getSetting("playback_video_force_8") == "true":
profile['CodecProfiles'].append(
{
"Type": "Video",
"Codec": "h264",
"Conditions": [
{
"Condition": "LessThanEqual",
"Property": "VideoBitDepth",
"Value": "8",
"IsRequired": False
}
]
}
)
profile['CodecProfiles'].append(
{
"Type": "Video",
"Codec": "h265,hevc",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
"Value": "main"
}
]
}
)
playback_info = {
'UserId': self.get_user_id(),
'DeviceProfile': profile,
'AutoOpenLiveStream': True
}
if force_transcode:
url = "{server}/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate)
else:
url = "{server}/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate)
log.debug("PlaybackInfo : {0}".format(url))
log.debug("PlaybackInfo : {0}".format(profile))
play_info_result = self.download_url(url, post_body=playback_info, method="POST")
log.debug("PlaybackInfo : {0}".format(play_info_result))
return play_info_result
def get_server(self):
settings = xbmcaddon.Addon()
# For migration from storing URL parts to just one URL
if settings.getSetting('ipaddress') != "" and settings.getSetting('ipaddress') != "<none>":
log.info("Migrating to new server url storage")
url = ("http://" if settings.getSetting('protocol') == "0" else "https://") + settings.getSetting('ipaddress') + ":" + settings.getSetting('port')
settings.setSetting('server_address', url)
settings.setSetting('ipaddress', "")
return settings.getSetting('server_address')
@staticmethod
def get_all_artwork(item, server):
all_art = defaultdict(lambda: "")
item_id = item["Id"]
item_type = item["Type"]
image_tags = item["ImageTags"]
# All the image tags
for tag_name in image_tags:
tag = image_tags[tag_name]
art_url = "%s/Items/%s/Images/%s/0?Format=original&Tag=%s" % (server, item_id, tag_name, tag)
all_art[tag_name] = art_url
# Series images
if item_type in ["Episode", "Season"]:
image_tag = item["SeriesPrimaryImageTag"]
series_id = item["SeriesId"]
if image_tag and series_id:
art_url = "%s/Items/%s/Images/Primary/0?Format=original&Tag=%s" % (server, series_id, image_tag)
all_art["Primary.Series"] = art_url
return all_art
def get_artwork(self, data, art_type, parent=False, index=0, server=None):
item_id = data["Id"]
item_type = data["Type"]
if item_type in ["Episode", "Season"]:
if art_type != "Primary" or parent is True:
item_id = data["SeriesId"]
image_tag = ""
# for episodes always use the parent BG
if item_type == "Episode" and art_type == "Backdrop":
item_id = data.get("ParentBackdropItemId")
bg_item_tags = data.get("ParentBackdropImageTags", [])
if bg_item_tags:
image_tag = bg_item_tags[0]
elif art_type == "Backdrop" and parent is True:
item_id = data.get("ParentBackdropItemId")
bg_item_tags = data.get("ParentBackdropImageTags", [])
if bg_item_tags:
image_tag = bg_item_tags[0]
elif art_type == "Backdrop":
bg_tags = data.get("BackdropImageTags", [])
if bg_tags:
image_tag = bg_tags[index]
elif parent is False:
image_tags = data.get("ImageTags", [])
if image_tags:
image_tag_type = image_tags.get(art_type)
if image_tag_type:
image_tag = image_tag_type
elif parent is True:
if (item_type == "Episode" or item_type == "Season") and art_type == 'Primary':
tag_name = 'SeriesPrimaryImageTag'
id_name = 'SeriesId'
else:
tag_name = 'Parent%sImageTag' % art_type
id_name = 'Parent%sItemId' % art_type
parent_image_id = data.get(id_name)
parent_image_tag = data.get(tag_name)
if parent_image_id is not None and parent_image_tag is not None:
item_id = parent_image_id
image_tag = parent_image_tag
# ParentTag not passed for Banner and Art
if not image_tag and not ((art_type == 'Banner' or art_type == 'Art') and parent is True):
return ""
artwork = "%s/Items/%s/Images/%s/%s?Format=original&Tag=%s" % (server, item_id, art_type, index, image_tag)
if self.use_https and not self.verify_cert:
artwork += "|verifypeer=false"
return artwork
def image_url(self, item_id, art_type, index, width, height, image_tag, server):
# test imageTag e3ab56fe27d389446754d0fb04910a34
artwork = "%s/Items/%s/Images/%s/%s?Format=original&Tag=%s" % (server, item_id, art_type, index, image_tag)
if int(width) > 0:
artwork += '&MaxWidth=%s' % width
if int(height) > 0:
artwork += '&MaxHeight=%s' % height
if self.use_https and not self.verify_cert:
artwork += "|verifypeer=false"
return artwork
def get_user_artwork(self, user, item_type):
if "PrimaryImageTag" not in user:
return ""
user_id = user.get("Id")
tag = user.get("PrimaryImageTag")
server = self.get_server()
artwork = "%s/Users/%s/Images/%s?Format=original&tag=%s" % (server, user_id, item_type, tag)
if self.use_https and not self.verify_cert:
artwork += "|verifypeer=false"
return artwork
def get_user_id(self):
user_details = load_user_details()
user_id = user_details.get('user_id', '')
return user_id
def authenticate(self):
window = HomeWindow()
user_name = window.get_property('user_name')
user_details = load_user_details()
if user_details.get('token', ''):
log.debug("JellyCon DownloadUtils -> Returning saved AccessToken")
# Resave credentials to update settings file
save_user_details(user_details.get('user_name'),
user_details.get('user_id'),
user_details.get('token'))
return user_details.get('token')
settings = xbmcaddon.Addon()
url = "{server}/Users/AuthenticateByName"
'''
TODO: Make the authenticate function operate with arguments during
network rework. Password could be leaked like this, but it's better
than it was previously
'''
password = window.get_property('password')
window.clear_property('password')
message_data = {'username': user_name, 'pw': password}
result = self.download_url(url, post_body=message_data, method="POST", suppress=True, authenticate=False)
access_token = result.get("AccessToken")
userid = result["User"].get("Id")
if access_token is not None:
log.debug('User authenticated successfully')
save_user_details(user_name, userid, access_token)
self.post_capabilities()
else:
log.debug("User NOT Authenticated")
def get_auth_header(self, authenticate=True):
device_id = get_device_id()
version = get_version()
client = 'Kodi JellyCon'
settings = xbmcaddon.Addon()
device_name = settings.getSetting('deviceName')
# remove none ascii chars
device_name = py2_decode(device_name)
# remove some chars not valid for names
device_name = device_name.replace("\"", "_")
if len(device_name) == 0:
device_name = "JellyCon"
headers = {}
headers["Accept-encoding"] = "gzip"
headers["Accept-Charset"] = "UTF-8,*"
if authenticate is False:
auth_string = 'MediaBrowser Client="{}",Device="{}",DeviceId="{}",Version="{}'.format(client, device_name, device_id, version)
headers['X-Emby-Authorization'] = auth_string
return headers
else:
userid = self.get_user_id()
auth_string = 'MediaBrowser UserId="{}",Client="{}",Device="{}",DeviceId="{}",Version="{}"'.format(userid, client, device_name, device_id, version)
headers['X-Emby-Authorization'] = auth_string
auth_token = self.authenticate()
if auth_token != "":
headers["X-MediaBrowser-Token"] = auth_token
log.debug("JellyCon Authentication Header: {0}".format(headers))
return headers
@timer
def download_url(self, url, suppress=False, post_body=None, method="GET", authenticate=True, headers=None):
log.debug("downloadUrl")
home_window = HomeWindow()
settings = xbmcaddon.Addon()
user_details = load_user_details()
username = user_details.get('user_name')
user_id = user_details.get('user_id', '')
server = None
http_timeout = int(settings.getSetting("http_timeout"))
if authenticate and username == "":
return {}
if settings.getSetting("suppressErrors") == "true":
suppress = True
log.debug("Before: {0}".format(url))
if url.find("{server}") != -1:
server = self.get_server()
if server is None:
return {}
url = url.replace("{server}", server)
if url.find("{userid}") != -1:
url = url.replace("{userid}", user_id)
if url.find("{ItemLimit}") != -1:
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
url = url.replace("{ItemLimit}", show_x_filtered_items)
if url.find("{field_filters}") != -1:
filter_string = get_details_string()
url = url.replace("{field_filters}", filter_string)
if url.find("{random_movies}") != -1:
random_movies = home_window.get_property("random-movies")
if not random_movies:
return {}
url = url.replace("{random_movies}", random_movies)
log.debug("After: {0}".format(url))
try:
url_bits = urlparse(url.strip())
user_name = url_bits.username
user_password = url_bits.password
head = self.get_auth_header(authenticate)
if user_name and user_password:
log.info("Replacing username & Password info")
# add basic auth headers
user_and_pass = b64encode(b"%s:%s" % (user_name, user_password)).decode("ascii")
head["Authorization"] = 'Basic %s' % user_and_pass
head["User-Agent"] = "JellyCon-" + get_version()
http_request = getattr(requests, method.lower())
if post_body:
if isinstance(post_body, dict):
head["Content-Type"] = "application/json"
post_body = json.dumps(post_body)
else:
head["Content-Type"] = "application/x-www-form-urlencoded"
log.debug("Content-Type: {0}".format(head["Content-Type"]))
log.debug("POST DATA: {0}".format(post_body))
data = http_request(url, data=post_body, headers=head)
else:
data = http_request(url, headers=head)
if data.status_code == 200:
if headers is not None and isinstance(headers, dict):
headers.update(data.headers)
log.debug("{0}".format(data.json()))
elif data.status_code >= 400:
if data.status_code == 401:
# remove any saved password
log.error("HTTP response error 401 auth error, removing any saved passwords for user: {0}".format(username))
save_user_details(username, user_id, '')
log.error("HTTP response error for {0}: {1} {2}".format(url, data.status_code, data.content))
if suppress is False:
xbmcgui.Dialog().notification(translate_string(30316),
'{}: {}'.format(translate_string(30200), data.content),
icon="special://home/addons/plugin.video.jellycon/icon.png")
try:
result = data.json()
except:
result = {}
return result
except Exception as msg:
log.error("{0}".format(format_exc()))
log.error("Unable to connect to {0} : {1}".format(server, msg))
if not suppress:
xbmcgui.Dialog().notification(translate_string(30316),
str(msg),
icon="special://home/addons/plugin.video.jellycon/icon.png")

View File

@@ -14,8 +14,8 @@ import xbmcgui
import xbmcaddon
import xbmc
from .downloadutils import DownloadUtils
from .utils import convert_size, translate_string, get_version, load_user_details
from .api import API
from .utils import convert_size, translate_string, get_version, load_user_details, get_art_url, get_default_filters
from .item_functions import get_art
from .kodi_utils import HomeWindow
from .datamanager import DataManager, clear_cached_server_data
@@ -42,7 +42,14 @@ log = LazyLogger(__name__)
kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
downloadUtils = DownloadUtils()
settings = xbmcaddon.Addon()
user_details = load_user_details()
api = API(
settings.getSetting('server_address'),
user_details.get('user_id'),
user_details.get('token')
)
dataManager = DataManager()
@@ -66,13 +73,13 @@ def main_entry_point():
log.debug("Script argument data: {0}".format(sys.argv))
params = get_params()
log.info("Script params: {0}".format(params))
log.debug("Script params: {0}".format(params))
request_path = params.get("request_path", None)
param_url = params.get('url', None)
if param_url:
param_url = unquote(param_url)
#if param_url:
# param_url = unquote(param_url)
mode = params.get("mode", None)
@@ -187,9 +194,9 @@ def __get_parent_id_from(params):
show_provider_ids = params.get("show_ids")
if show_provider_ids is not None:
log.debug("TV show providers IDs: {}".format(show_provider_ids))
get_show_url = "{server}/Users/{userid}/Items?fields=MediaStreams&Recursive=true" \
get_show_url = "/Users/{}/Items?fields=MediaStreams&Recursive=true" \
"&IncludeItemTypes=series&IncludeMedia=true&ImageTypeLimit=1&Limit=16" \
"&AnyProviderIdEquals=" + show_provider_ids
"&AnyProviderIdEquals={}".format(api.user_id, item_id, show_provider_ids)
content = dataManager.get_content(get_show_url)
show = content.get("Items")
if len(show) == 1:
@@ -206,7 +213,7 @@ def toggle_watched(params):
item_id = params.get("item_id", None)
if item_id is None:
return
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id)
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("toggle_watched item info: {0}".format(result))
@@ -223,8 +230,8 @@ def toggle_watched(params):
def mark_item_watched(item_id):
log.debug("Mark Item Watched: {0}".format(item_id))
url = "{server}/Users/{userid}/PlayedItems/" + item_id
downloadUtils.download_url(url, post_body="", method="POST")
url = "/Users/{}/PlayedItems/{}".format(user_details.get('user_id'), item_id)
api.post(url)
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
@@ -237,8 +244,8 @@ def mark_item_watched(item_id):
def mark_item_unwatched(item_id):
log.debug("Mark Item UnWatched: {0}".format(item_id))
url = "{server}/Users/{userid}/PlayedItems/" + item_id
downloadUtils.download_url(url, method="DELETE")
url = "/Users/{}/PlayedItems/".format(user_details.get('user_id'), item_id)
api.delete(url)
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
@@ -251,8 +258,8 @@ def mark_item_unwatched(item_id):
def mark_item_favorite(item_id):
log.debug("Add item to favourites: {0}".format(item_id))
url = "{server}/Users/{userid}/FavoriteItems/" + item_id
downloadUtils.download_url(url, post_body="", method="POST")
url = "/Users/{}/FavoriteItems/{}".format(user_details.get('user_id'), item_id)
api.post(url)
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
@@ -264,8 +271,8 @@ def mark_item_favorite(item_id):
def unmark_item_favorite(item_id):
log.debug("Remove item from favourites: {0}".format(item_id))
url = "{server}/Users/{userid}/FavoriteItems/" + item_id
downloadUtils.download_url(url, method="DELETE")
url = "/Users/{}/FavoriteItems/{}".format(user_details.get('user_id'), item_id)
api.delete(url)
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
@@ -277,7 +284,7 @@ def unmark_item_favorite(item_id):
def delete(item_id):
item = downloadUtils.download_url("{server}/Users/{userid}/Items/" + item_id + "?format=json")
item = api.delete("/Users/{}/Items/{}".format(user_details.get('user_id'), item_id))
item_id = item.get("Id")
item_name = item.get("Name", "")
@@ -301,10 +308,10 @@ def delete(item_id):
return_value = xbmcgui.Dialog().yesno(translate_string(30091), '{}\n{}'.format(final_name, translate_string(30092)))
if return_value:
log.debug('Deleting Item: {0}'.format(item_id))
url = '{server}/Items/' + item_id
url = '/Items/{}'.format(item_id)
progress = xbmcgui.DialogProgress()
progress.create(translate_string(30052), translate_string(30053))
downloadUtils.download_url(url, method="DELETE")
api.delete(url)
progress.close()
check_for_new_content()
home_window = HomeWindow()
@@ -343,7 +350,7 @@ def show_menu(params):
settings = xbmcaddon.Addon()
item_id = params["item_id"]
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id)
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("Menu item info: {0}".format(result))
@@ -485,22 +492,22 @@ def show_menu(params):
settings.setSetting(view_key, "")
elif selected_action == "refresh_server":
url = ("{server}/Items/" + item_id + "/Refresh" +
url = ("/Items/" + item_id + "/Refresh" +
"?Recursive=true" +
"&ImageRefreshMode=FullRefresh" +
"&MetadataRefreshMode=FullRefresh" +
"&ReplaceAllImages=true" +
"&ReplaceAllMetadata=true")
res = downloadUtils.download_url(url, post_body="", method="POST")
res = api.post(url)
log.debug("Refresh Server Responce: {0}".format(res))
elif selected_action == "hide":
user_details = load_user_details()
user_name = user_details["user_name"]
hide_tag_string = "hide-" + user_name
url = "{server}/Items/" + item_id + "/Tags/Add"
url = "/Items/{}/Tags/Add".format(item_id)
post_tag_data = {"Tags": [{"Name": hide_tag_string}]}
res = downloadUtils.download_url(url, post_body=post_tag_data, method="POST")
res = api.post(url, post_tag_data)
log.debug("Add Tag Responce: {0}".format(res))
check_for_new_content()
@@ -554,62 +561,8 @@ def show_menu(params):
elif selected_action == "delete":
delete(item_id)
elif selected_action == "safe_delete":
url = "{server}/jellyfin_safe_delete/delete_item/" + item_id
result = downloadUtils.download_url(url)
dialog = xbmcgui.Dialog()
if result:
log.debug("Safe_Delete_Action: {0}".format(result))
action_token = result["action_token"]
message = "You are about to delete the following item[CR][CR]"
message += "Type: " + result["item_info"]["Item_type"] + "[CR]"
if result["item_info"]["Item_type"] == "Series":
message += "Name: " + result["item_info"]["item_name"] + "[CR]"
elif result["item_info"]["Item_type"] == "Season":
message += "Season: " + str(result["item_info"]["season_number"]) + "[CR]"
message += "Name: " + result["item_info"]["season_name"] + "[CR]"
elif result["item_info"]["Item_type"] == "Episode":
message += "Series: " + result["item_info"]["series_name"] + "[CR]"
message += "Season: " + result["item_info"]["season_name"] + "[CR]"
message += "Episode: " + str(result["item_info"]["episode_number"]) + "[CR]"
message += "Name: " + result["item_info"]["item_name"] + "[CR]"
else:
message += "Name: " + result["item_info"]["item_name"] + "[CR]"
message += "[CR]File List[CR][CR]"
for file_info in result["file_list"]:
message += " - " + file_info["Key"] + " (" + convert_size(file_info["Value"]) + ")[CR]"
message += "[CR][CR]Are you sure?[CR][CR]"
confirm_dialog = SafeDeleteDialog("SafeDeleteDialog.xml", PLUGINPATH, "default", "720p")
confirm_dialog.message = message
confirm_dialog.heading = "Confirm delete files?"
confirm_dialog.doModal()
log.debug("safe_delete_confirm_dialog: {0}".format(confirm_dialog.confirm))
if confirm_dialog.confirm:
url = "{server}/jellyfin_safe_delete/delete_item_action"
playback_info = {
'item_id': item_id,
'action_token': action_token
}
delete_action = downloadUtils.download_url(url, method="POST", post_body=playback_info)
log.debug("Delete result action: {0}".format(delete_action))
if not delete_action:
dialog.ok("Error", "Error deleting files", "Error in responce from server")
elif not delete_action.get("result"):
dialog.ok("Error", "Error deleting files", delete_action["message"])
else:
dialog.ok("Deleted", "Files deleted")
else:
dialog.ok("Error", "Error getting safe delete confirmation")
elif selected_action == "show_extras":
u = "{server}/Users/{userid}/Items/" + item_id + "/SpecialFeatures"
u = "/Users/{}/Items/{}/SpecialFeatures".format(api.user_id, item_id)
action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Videos")
built_in_command = 'ActivateWindow(Videos, ' + action_url + ', return)'
xbmc.executebuiltin(built_in_command)
@@ -618,13 +571,13 @@ def show_menu(params):
xbmc.executebuiltin("Dialog.Close(all,true)")
parent_id = result["ParentId"]
series_id = result["SeriesId"]
u = ('{server}/Shows/' + series_id +
u = ('/Shows/' + series_id +
'/Episodes'
'?userId={userid}' +
'?userId={}'.format(api.user_id) +
'&seasonId=' + parent_id +
'&IsVirtualUnAired=false' +
'&IsMissing=false' +
'&Fields=SpecialEpisodeNumbers,{field_filters}' +
'&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) +
'&format=json')
action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Season")
built_in_command = 'ActivateWindow(Videos, ' + action_url + ', return)'
@@ -637,10 +590,10 @@ def show_menu(params):
if not series_id:
series_id = item_id
u = ('{server}/Shows/' + series_id +
u = ('/Shows/' + series_id +
'/Seasons'
'?userId={userid}' +
'&Fields={field_filters}' +
'?userId={}'.format(api.user_id) +
'&Fields={}'.format(get_default_filters()) +
'&format=json')
action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Series")
@@ -663,15 +616,15 @@ def show_menu(params):
def populate_listitem(item_id):
log.debug("populate_listitem: {0}".format(item_id))
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
result = downloadUtils.download_url(url)
url = "/Users/{}/Items/{}".format(user_details.get('user_id'), item_id)
result = api.get(url)
log.debug("populate_listitem item info: {0}".format(result))
item_title = result.get("Name", translate_string(30280))
list_item = xbmcgui.ListItem(label=item_title)
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
art = get_art(result, server=server)
list_item.setIconImage(art['thumb']) # back compat
@@ -704,11 +657,11 @@ def show_content(params):
if item_type.lower().find("movie") == -1:
group_movies = False
content_url = ("{server}/Users/{userid}/Items" +
content_url = ("/Users/{}/Items".format(api.user_id) +
"?format=json" +
"&ImageTypeLimit=1" +
"&IsMissing=False" +
"&Fields={field_filters}" +
"&Fields={}".format(get_default_filters()) +
'&CollapseBoxSetItems=' + str(group_movies) +
'&GroupItemsIntoCollections=' + str(group_movies) +
"&Recursive=true" +
@@ -726,10 +679,10 @@ def search_results_person(params):
handle = int(sys.argv[1])
person_id = params.get("person_id")
details_url = ('{server}/Users/{userid}/items' +
details_url = ('/Users/{}/Items'.format(api.user_id) +
'?PersonIds=' + person_id +
'&Recursive=true' +
'&Fields={field_filters}' +
'&Fields={}'.format(get_default_filters()) +
'&format=json')
params["name_format"] = "Episode|episode_name_format"
@@ -831,7 +784,7 @@ def search_results(params):
# what type of search
if item_type == "person":
search_url = ("{server}/Persons" +
search_url = ("/Persons" +
"?searchTerm=" + query +
"&IncludePeople=true" +
"&IncludeMedia=false" +
@@ -843,7 +796,7 @@ def search_results(params):
"&Recursive=true" +
"&EnableTotalRecordCount=false" +
"&ImageTypeLimit=1" +
"&userId={userid}")
"&userId={}".format(api.user_id))
person_search_results = dataManager.get_content(search_url)
log.debug("Person Search Result : {0}".format(person_search_results))
@@ -852,12 +805,12 @@ def search_results(params):
person_items = person_search_results.get("Items", [])
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
list_items = []
for item in person_items:
person_id = item.get('Id')
person_name = item.get('Name')
person_thumbnail = downloadUtils.get_artwork(item, "Primary", server=server)
person_thumbnail = get_art_url(item, "Primary", server=server)
action_url = sys.argv[0] + "?mode=NEW_SEARCH_PERSON&person_id=" + person_id
@@ -879,7 +832,7 @@ def search_results(params):
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
else:
search_url = ("{server}/Users/{userid}/Items" +
search_url = ("/Users/{}/Items".format(api.user_id) +
"?searchTerm=" + query +
"&IncludePeople=false" +
"&IncludeMedia=true" +
@@ -888,7 +841,7 @@ def search_results(params):
"&IncludeArtists=false" +
"&IncludeItemTypes=" + item_type +
"&Limit=16" +
"&Fields={field_filters}" +
"&Fields={}".format(get_default_filters()) +
"&Recursive=true" +
"&EnableTotalRecordCount=false" +
"&ImageTypeLimit=1")
@@ -955,9 +908,9 @@ def play_action(params):
def play_item_trailer(item_id):
log.debug("== ENTER: playTrailer ==")
url = ("{server}/Users/{userid}/Items/%s/LocalTrailers?format=json" % item_id)
url = "/Users/{}/Items//LocalTrailers?format=json".format(user_details.get('user_id'), item_id)
result = downloadUtils.download_url(url)
result = api.get(url)
if result is None:
return
@@ -980,8 +933,8 @@ def play_item_trailer(item_id):
trailer_names.append(name)
trailer_list.append(info)
url = ("{server}/Users/{userid}/Items/%s?format=json&Fields=RemoteTrailers" % item_id)
result = downloadUtils.download_url(url)
url = "/Users/{}/Items/{}?format=json&Fields=RemoteTrailers".format(user_details.get('user_id'), item_id)
result = api.get(url)
log.debug("RemoteTrailers: {0}".format(result))
count = 1

View File

@@ -2,6 +2,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import xbmcvfs
import xbmc
import xbmcaddon
import base64
import re
from random import shuffle
@@ -15,7 +16,6 @@ import io
from .loghandler import LazyLogger
from .datamanager import DataManager
from .downloadutils import DownloadUtils
from .item_functions import get_art
pil_loaded = False
@@ -31,8 +31,8 @@ log = LazyLogger(__name__)
def get_image_links(url):
download_utils = DownloadUtils()
server = download_utils.get_server()
settings = xbmcaddon.Addon()
server = settings.getSetting('server_address')
if server is None:
return []

View File

@@ -10,9 +10,8 @@ import xbmc
import xbmcaddon
import xbmcgui
from .utils import datetime_from_string
from .utils import datetime_from_string, get_art_url, image_url
from .loghandler import LazyLogger
from .downloadutils import DownloadUtils
from six import ensure_text
log = LazyLogger(__name__)
@@ -244,7 +243,6 @@ def extract_item_info(item, gui_options):
people = item.get("People", [])
if people is not None:
cast = []
download_utils = DownloadUtils()
for person in people:
person_type = person.get("Type")
if person_type == "Director":
@@ -257,10 +255,9 @@ def extract_item_info(item, gui_options):
person_id = person.get("Id")
person_tag = person.get("PrimaryImageTag")
if person_tag:
person_thumbnail = download_utils.image_url(person_id,
"Primary", 0, 400, 400,
person_tag,
server=gui_options["server"])
person_thumbnail = image_url(person_id, "Primary", 0, 400,
400, person_tag,
server=gui_options["server"])
else:
person_thumbnail = ""
person = {"name": person_name, "role": person_role, "thumbnail": person_thumbnail}
@@ -613,7 +610,6 @@ def add_gui_item(url, item_details, display_options, folder=True, default_sort=F
def get_art(item, server):
download_utils = DownloadUtils()
art = {
'thumb': '',
@@ -634,63 +630,63 @@ def get_art(item, server):
image_tags = item.get("ImageTags", {})
if image_tags and image_tags.get("Primary"):
art['thumb'] = download_utils.get_artwork(item, "Primary", server=server)
art['thumb'] = get_art_url(item, "Primary", server=server)
item_type = item["Type"]
if item_type == "Genre":
art['poster'] = download_utils.get_artwork(item, "Primary", server=server)
art['poster'] = get_art_url(item, "Primary", server=server)
elif item_type == "Episode":
art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=True, server=server)
art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server)
art['clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server)
art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server)
art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server)
art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server)
art['banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server)
art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server)
art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server)
art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server)
art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server)
art['tvshow.poster'] = get_art_url(item, "Primary", parent=True, server=server)
art['tvshow.clearart'] = get_art_url(item, "Art", parent=True, server=server)
art['clearart'] = get_art_url(item, "Art", parent=True, server=server)
art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=True, server=server)
art['clearlogo'] = get_art_url(item, "Logo", parent=True, server=server)
art['tvshow.banner'] = get_art_url(item, "Banner", parent=True, server=server)
art['banner'] = get_art_url(item, "Banner", parent=True, server=server)
art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=True, server=server)
art['landscape'] = get_art_url(item, "Thumb", parent=True, server=server)
art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=True, server=server)
art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server)
elif item_type == "Season":
art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=True, server=server)
art['season.poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server)
art['poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server)
art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server)
art['clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server)
art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server)
art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server)
art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server)
art['season.banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server)
art['banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server)
art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server)
art['season.landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server)
art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server)
art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server)
art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server)
art['tvshow.poster'] = get_art_url(item, "Primary", parent=True, server=server)
art['season.poster'] = get_art_url(item, "Primary", parent=False, server=server)
art['poster'] = get_art_url(item, "Primary", parent=False, server=server)
art['tvshow.clearart'] = get_art_url(item, "Art", parent=True, server=server)
art['clearart'] = get_art_url(item, "Art", parent=True, server=server)
art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=True, server=server)
art['clearlogo'] = get_art_url(item, "Logo", parent=True, server=server)
art['tvshow.banner'] = get_art_url(item, "Banner", parent=True, server=server)
art['season.banner'] = get_art_url(item, "Banner", parent=False, server=server)
art['banner'] = get_art_url(item, "Banner", parent=False, server=server)
art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=True, server=server)
art['season.landscape'] = get_art_url(item, "Thumb", parent=False, server=server)
art['landscape'] = get_art_url(item, "Thumb", parent=False, server=server)
art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=True, server=server)
art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server)
elif item_type == "Series":
art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server)
art['poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server)
art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=False, server=server)
art['clearart'] = download_utils.get_artwork(item, "Art", parent=False, server=server)
art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=False, server=server)
art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=False, server=server)
art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server)
art['banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server)
art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server)
art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server)
art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=False, server=server)
art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=False, server=server)
art['tvshow.poster'] = get_art_url(item, "Primary", parent=False, server=server)
art['poster'] = get_art_url(item, "Primary", parent=False, server=server)
art['tvshow.clearart'] = get_art_url(item, "Art", parent=False, server=server)
art['clearart'] = get_art_url(item, "Art", parent=False, server=server)
art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=False, server=server)
art['clearlogo'] = get_art_url(item, "Logo", parent=False, server=server)
art['tvshow.banner'] = get_art_url(item, "Banner", parent=False, server=server)
art['banner'] = get_art_url(item, "Banner", parent=False, server=server)
art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=False, server=server)
art['landscape'] = get_art_url(item, "Thumb", parent=False, server=server)
art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=False, server=server)
art['fanart'] = get_art_url(item, "Backdrop", parent=False, server=server)
elif item_type == "Movie" or item_type == "BoxSet":
art['poster'] = download_utils.get_artwork(item, "Primary", server=server)
art['landscape'] = download_utils.get_artwork(item, "Thumb", server=server)
art['banner'] = download_utils.get_artwork(item, "Banner", server=server)
art['clearlogo'] = download_utils.get_artwork(item, "Logo", server=server)
art['clearart'] = download_utils.get_artwork(item, "Art", server=server)
art['discart'] = download_utils.get_artwork(item, "Disc", server=server)
art['poster'] = get_art_url(item, "Primary", server=server)
art['landscape'] = get_art_url(item, "Thumb", server=server)
art['banner'] = get_art_url(item, "Banner", server=server)
art['clearlogo'] = get_art_url(item, "Logo", server=server)
art['clearart'] = get_art_url(item, "Art", server=server)
art['discart'] = get_art_url(item, "Disc", server=server)
art['fanart'] = download_utils.get_artwork(item, "Backdrop", server=server)
art['fanart'] = get_art_url(item, "Backdrop", server=server)
if not art['fanart']:
art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server)
art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server)
return art

View File

@@ -11,17 +11,18 @@ import string
import xbmcplugin
import xbmcaddon
from .downloadutils import DownloadUtils
from .kodi_utils import add_menu_directory_item, HomeWindow
from .loghandler import LazyLogger
from .datamanager import DataManager
from .utils import get_jellyfin_url, translate_string
from .utils import get_jellyfin_url, translate_string, get_art_url, load_user_details, get_default_filters
from .item_functions import get_art
log = LazyLogger(__name__)
downloadUtils = DownloadUtils()
__addon__ = xbmcaddon.Addon()
user_details = load_user_details()
user_id = user_details.get('user_id')
settings = xbmcaddon.Addon()
def show_movie_tags(menu_params):
@@ -29,7 +30,7 @@ def show_movie_tags(menu_params):
parent_id = menu_params.get("parent_id")
url_params = {}
url_params["UserId"] = "{userid}"
url_params["UserId"] = user_id
url_params["SortBy"] = "SortName"
url_params["SortOrder"] = "Ascending"
url_params["CollapseBoxSetItems"] = False
@@ -43,7 +44,7 @@ def show_movie_tags(menu_params):
if parent_id:
url_params["ParentId"] = parent_id
url = get_jellyfin_url("{server}/Tags", url_params)
url = get_jellyfin_url("/Tags", url_params)
data_manager = DataManager()
result = data_manager.get_content(url)
@@ -67,13 +68,13 @@ def show_movie_tags(menu_params):
url_params["ImageTypeLimit"] = 1
url_params["SortBy"] = "Name"
url_params["SortOrder"] = "Ascending"
url_params["Fields"] = "{field_filters}"
url_params["Fields"] = get_default_filters()
url_params["TagIds"] = tag_id
if parent_id:
menu_params["ParentId"] = parent_id
item_url = get_jellyfin_url("{server}/Users/{userid}/Items", url_params)
item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), url_params)
art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(item_url))))}
@@ -94,7 +95,7 @@ def show_movie_years(menu_params):
group_into_decades = menu_params.get("group") == "true"
url_params = {}
url_params["UserId"] = "{userid}"
url_params["UserId"] = user_id
url_params["SortBy"] = "SortName"
url_params["SortOrder"] = "Ascending"
url_params["CollapseBoxSetItems"] = False
@@ -108,7 +109,7 @@ def show_movie_years(menu_params):
if parent_id:
url_params["ParentId"] = parent_id
url = get_jellyfin_url("{server}/Years", url_params)
url = get_jellyfin_url("/Years", url_params)
data_manager = DataManager()
result = data_manager.get_content(url)
@@ -153,13 +154,13 @@ def show_movie_years(menu_params):
params["ImageTypeLimit"] = 1
params["SortBy"] = "Name"
params["SortOrder"] = "Ascending"
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["Years"] = value
if parent_id:
params["ParentId"] = parent_id
item_url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(item_url))))}
@@ -178,7 +179,6 @@ def show_movie_pages(menu_params):
log.debug("showMoviePages: {0}".format(menu_params))
parent_id = menu_params.get("parent_id")
settings = xbmcaddon.Addon()
group_movies = settings.getSetting('group_movies') == "true"
params = {}
@@ -192,7 +192,7 @@ def show_movie_pages(menu_params):
if parent_id:
params["ParentId"] = parent_id
url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
url = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
data_manager = DataManager()
result = data_manager.get_content(url)
@@ -224,14 +224,14 @@ def show_movie_pages(menu_params):
params["ImageTypeLimit"] = 1
params["SortBy"] = "Name"
params["SortOrder"] = "Ascending"
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["StartIndex"] = start_index
params["Limit"] = page_limit
if parent_id:
params["ParentId"] = parent_id
item_url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
page_upper = start_index + page_limit
if page_upper > total_results:
@@ -261,7 +261,7 @@ def show_movie_pages(menu_params):
def show_genre_list(menu_params):
log.debug("showGenreList: {0}".format(menu_params))
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
if server is None:
return
@@ -276,7 +276,7 @@ def show_genre_list(menu_params):
params = {}
params["IncludeItemTypes"] = jellyfin_type
params["UserId"] = "{userid}"
params["UserId"] = user_id
params["Recursive"] = True
params["SortBy"] = "Name"
params["SortOrder"] = "Ascending"
@@ -285,7 +285,7 @@ def show_genre_list(menu_params):
if parent_id is not None:
params["ParentId"] = parent_id
url = get_jellyfin_url("{server}/Genres", params)
url = get_jellyfin_url("/Genres", params)
data_manager = DataManager()
result = data_manager.get_content(url)
@@ -295,7 +295,6 @@ def show_genre_list(menu_params):
else:
result = []
settings = xbmcaddon.Addon()
group_movies = settings.getSetting('group_movies') == "true"
collections = []
@@ -316,12 +315,12 @@ def show_genre_list(menu_params):
params["GenreIds"] = genre.get("Id")
params["IncludeItemTypes"] = jellyfin_type
params["ImageTypeLimit"] = 1
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
if parent_id is not None:
params["ParentId"] = parent_id
url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
url = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(url))))}
item_data['art'] = art
@@ -344,8 +343,7 @@ def show_movie_alpha_list(menu_params):
xbmcplugin.setContent(int(sys.argv[1]), 'movies')
settings = xbmcaddon.Addon()
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
if server is None:
return
@@ -356,7 +354,7 @@ def show_movie_alpha_list(menu_params):
url_params["IncludeItemTypes"] = "Movie"
url_params["Recursive"] = True
url_params["GroupItemsIntoCollections"] = group_movies
url_params["UserId"] = "{userid}"
url_params["UserId"] = user_id
url_params["SortBy"] = "Name"
url_params["SortOrder"] = "Ascending"
if parent_id is not None:
@@ -371,7 +369,7 @@ def show_movie_alpha_list(menu_params):
item_data['media_type'] = "Movies"
params = {}
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["CollapseBoxSetItems"] = group_movies
params["GroupItemsIntoCollections"] = group_movies
params["Recursive"] = True
@@ -388,7 +386,7 @@ def show_movie_alpha_list(menu_params):
else:
params["NameStartsWith"] = alpha_name
url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
url = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
item_data['path'] = url
art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(url))))}
@@ -408,7 +406,7 @@ def show_movie_alpha_list(menu_params):
def show_tvshow_alpha_list(menu_params):
log.debug("== ENTER: showTvShowAlphaList() ==")
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
if server is None:
return
@@ -417,7 +415,7 @@ def show_tvshow_alpha_list(menu_params):
url_params = {}
url_params["IncludeItemTypes"] = "Series"
url_params["Recursive"] = True
url_params["UserId"] = "{userid}"
url_params["UserId"] = user_id
url_params["SortBy"] = "Name"
url_params["SortOrder"] = "Ascending"
if parent_id is not None:
@@ -432,7 +430,7 @@ def show_tvshow_alpha_list(menu_params):
item_data['media_type'] = "tvshows"
params = {}
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Series"
params["SortBy"] = "Name"
@@ -448,7 +446,7 @@ def show_tvshow_alpha_list(menu_params):
else:
params["NameStartsWith"] = alpha_name
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
item_data['path'] = path
@@ -518,8 +516,7 @@ def show_global_types(params):
def display_homevideos_type(menu_params, view):
handle = int(sys.argv[1])
view_name = view.get("Name")
settings = xbmcaddon.Addon()
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
item_limit = settings.getSetting("show_x_filtered_items")
hide_watched = settings.getSetting("hide_watched") == "true"
# All Home Movies
@@ -527,9 +524,9 @@ def display_homevideos_type(menu_params, view):
base_params["ParentId"] = view.get("Id")
base_params["Recursive"] = False
base_params["IsMissing"] = False
base_params["Fields"] = "{field_filters}"
base_params["Fields"] = get_default_filters()
base_params["ImageTypeLimit"] = 1
path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos"
add_menu_directory_item(view_name + translate_string(30405), url)
@@ -538,10 +535,10 @@ def display_homevideos_type(menu_params, view):
params.update(base_params)
params["Filters"] = "IsResumable"
params["Recursive"] = True
params["Limit"] = "{ItemLimit}"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
params["Limit"] = item_limit
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos"
add_menu_directory_item(view_name + translate_string(30267) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30267) + " (" + item_limit + ")", url)
# Recently added
params = {}
@@ -552,10 +549,10 @@ def display_homevideos_type(menu_params, view):
params["Filters"] = "IsNotFolder"
if hide_watched:
params["IsPlayed"] = False
params["Limit"] = "{ItemLimit}"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
params["Limit"] = item_limit
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos"
add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url)
xbmcplugin.endOfDirectory(handle)
@@ -583,19 +580,18 @@ def display_tvshow_type(menu_params, view):
if view is not None:
view_name = view.get("Name")
settings = xbmcaddon.Addon()
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
item_limit = settings.getSetting("show_x_filtered_items")
# All TV Shows
base_params = {}
if view is not None:
base_params["ParentId"] = view.get("Id")
base_params["Fields"] = "{field_filters}"
base_params["Fields"] = get_default_filters()
base_params["ImageTypeLimit"] = 1
base_params["IsMissing"] = False
base_params["IncludeItemTypes"] = "Series"
base_params["Recursive"] = True
path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows"
add_menu_directory_item(view_name + translate_string(30405), url)
@@ -603,7 +599,7 @@ def display_tvshow_type(menu_params, view):
params = {}
params.update(base_params)
params["Filters"] = "IsFavorite"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows"
add_menu_directory_item(view_name + translate_string(30414), url)
@@ -611,60 +607,60 @@ def display_tvshow_type(menu_params, view):
params = {}
params.update(base_params)
params["IsPlayed"] = False
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows"
add_menu_directory_item(view_name + translate_string(30285), url)
# In progress episodes
params = {}
params.update(base_params)
params["Limit"] = "{ItemLimit}"
params["Limit"] = item_limit
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
params["Filters"] = "IsResumable"
params["IncludeItemTypes"] = "Episode"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none"
url += "&name_format=" + quote('Episode|episode_name_format')
add_menu_directory_item(view_name + translate_string(30267) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30267) + " (" + item_limit + ")", url)
# Latest Episodes
params = {}
params.update(base_params)
params["Limit"] = "{ItemLimit}"
params["Limit"] = item_limit
params["SortBy"] = "DateCreated"
params["SortOrder"] = "Descending"
params["IncludeItemTypes"] = "Episode"
path = get_jellyfin_url("{server}/Users/{userid}/Items/Latest", params)
path = get_jellyfin_url("/Users/{}/Items/Latest".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows&sort=none"
add_menu_directory_item(view_name + translate_string(30288) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30288) + " (" + item_limit + ")", url)
# Recently Added
params = {}
params.update(base_params)
params["Limit"] = "{ItemLimit}"
params["Limit"] = item_limit
params["SortBy"] = "DateCreated"
params["SortOrder"] = "Descending"
params["Filters"] = "IsNotFolder"
params["IncludeItemTypes"] = "Episode"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none"
url += "&name_format=" + quote('Episode|episode_name_format')
add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url)
# Next Up Episodes
params = {}
params.update(base_params)
params["Limit"] = "{ItemLimit}"
params["Userid"] = "{userid}"
params["Limit"] = item_limit
params["Userid"] = user_id
params["SortBy"] = "DateCreated"
params["SortOrder"] = "Descending"
params["Filters"] = "IsNotFolder"
params["IncludeItemTypes"] = "Episode"
path = get_jellyfin_url("{server}/Shows/NextUp", params)
path = get_jellyfin_url("/Shows/NextUp", params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none"
url += "&name_format=" + quote('Episode|episode_name_format')
add_menu_directory_item(view_name + translate_string(30278) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30278) + " (" + item_limit + ")", url)
# TV Show Genres
path = "plugin://plugin.video.jellycon/?mode=GENRES&item_type=tvshow"
@@ -685,8 +681,7 @@ def display_music_type(menu_params, view):
handle = int(sys.argv[1])
view_name = view.get("Name")
settings = xbmcaddon.Addon()
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
item_limit = settings.getSetting("show_x_filtered_items")
# all albums
params = {}
@@ -694,7 +689,7 @@ def display_music_type(menu_params, view):
params["Recursive"] = True
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "MusicAlbum"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbums"
add_menu_directory_item(view_name + translate_string(30320), url)
@@ -703,10 +698,10 @@ def display_music_type(menu_params, view):
params["ParentId"] = view.get("Id")
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Audio"
params["Limit"] = "{ItemLimit}"
path = get_jellyfin_url("{server}/Users/{userid}/Items/Latest", params)
params["Limit"] = item_limit
path = get_jellyfin_url("/Users/{}/Items/Latest".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbums"
add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url)
# recently played
params = {}
@@ -714,13 +709,13 @@ def display_music_type(menu_params, view):
params["Recursive"] = True
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Audio"
params["Limit"] = "{ItemLimit}"
params["Limit"] = item_limit
params["IsPlayed"] = True
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbum"
add_menu_directory_item(view_name + translate_string(30349) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30349) + " (" + item_limit + ")", url)
# most played
params = {}
@@ -728,20 +723,20 @@ def display_music_type(menu_params, view):
params["Recursive"] = True
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Audio"
params["Limit"] = "{ItemLimit}"
params["Limit"] = item_limit
params["IsPlayed"] = True
params["SortBy"] = "PlayCount"
params["SortOrder"] = "Descending"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbum"
add_menu_directory_item(view_name + translate_string(30353) + " (" + show_x_filtered_items + ")", url)
add_menu_directory_item(view_name + translate_string(30353) + " (" + item_limit + ")", url)
# artists
params = {}
params["ParentId"] = view.get("Id")
params["Recursive"] = True
params["ImageTypeLimit"] = 1
path = get_jellyfin_url("{server}/Artists/AlbumArtists", params)
path = get_jellyfin_url("/Artists/AlbumArtists", params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicArtists"
add_menu_directory_item(view_name + translate_string(30321), url)
@@ -760,8 +755,8 @@ def display_musicvideos_type(params, view):
params["Recursive"] = False
params["ImageTypeLimit"] = 1
params["IsMissing"] = False
params["Fields"] = "{field_filters}"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
params["Fields"] = get_default_filters()
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=musicvideos"
add_menu_directory_item(view_name + translate_string(30405), url)
@@ -776,33 +771,33 @@ def display_livetv_type(menu_params, view):
# channels
params = {}
params["UserId"] = "{userid}"
params["UserId"] = user_id
params["Recursive"] = False
params["ImageTypeLimit"] = 1
params["Fields"] = "{field_filters}"
path = get_jellyfin_url("{server}/LiveTv/Channels", params)
params["Fields"] = get_default_filters()
path = get_jellyfin_url("/LiveTv/Channels", params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv"
add_menu_directory_item(view_name + translate_string(30360), url)
# programs
params = {}
params["UserId"] = "{userid}"
params["UserId"] = user_id
params["IsAiring"] = True
params["ImageTypeLimit"] = 1
params["Fields"] = "ChannelInfo,{field_filters}"
params["Fields"] = get_default_filters() + "ChannelInfo"
params["EnableTotalRecordCount"] = False
path = get_jellyfin_url("{server}/LiveTv/Programs/Recommended", params)
path = get_jellyfin_url("/LiveTv/Programs/Recommended", params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv"
add_menu_directory_item(view_name + translate_string(30361), url)
# recordings
params = {}
params["UserId"] = "{userid}"
params["UserId"] = user_id
params["Recursive"] = False
params["ImageTypeLimit"] = 1
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["EnableTotalRecordCount"] = False
path = get_jellyfin_url("{server}/LiveTv/Recordings", params)
path = get_jellyfin_url("/LiveTv/Recordings", params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv"
add_menu_directory_item(view_name + translate_string(30362), url)
@@ -817,8 +812,7 @@ def display_movies_type(menu_params, view):
if view is not None:
view_name = view.get("Name")
settings = xbmcaddon.Addon()
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
item_limit = settings.getSetting("show_x_filtered_items")
group_movies = settings.getSetting('group_movies') == "true"
hide_watched = settings.getSetting("hide_watched") == "true"
@@ -830,11 +824,11 @@ def display_movies_type(menu_params, view):
base_params["GroupItemsIntoCollections"] = str(group_movies)
base_params["Recursive"] = True
base_params["IsMissing"] = False
base_params["Fields"] = "{field_filters}"
base_params["Fields"] = get_default_filters()
base_params["ImageTypeLimit"] = 1
# All Movies
path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies"
add_menu_directory_item('{}{}'.format(view_name, translate_string(30405)), url)
@@ -844,7 +838,7 @@ def display_movies_type(menu_params, view):
params["CollapseBoxSetItems"] = False
params["GroupItemsIntoCollections"] = False
params["Filters"] = "IsFavorite"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies"
add_menu_directory_item('{}{}'.format(view_name, translate_string(30414)), url)
@@ -854,7 +848,7 @@ def display_movies_type(menu_params, view):
params["CollapseBoxSetItems"] = False
params["GroupItemsIntoCollections"] = False
params["IsPlayed"] = False
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies"
add_menu_directory_item('{}{}'.format(view_name, translate_string(30285)), url)
@@ -866,10 +860,10 @@ def display_movies_type(menu_params, view):
params["SortOrder"] = "Descending"
params["CollapseBoxSetItems"] = False
params["GroupItemsIntoCollections"] = False
params["Limit"] = "{ItemLimit}"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
params["Limit"] = item_limit
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none"
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30349), show_x_filtered_items), url)
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30349), item_limit), url)
# Resumable Movies
params = {}
@@ -877,10 +871,10 @@ def display_movies_type(menu_params, view):
params["Filters"] = "IsResumable"
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
params["Limit"] = "{ItemLimit}"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
params["Limit"] = item_limit
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none"
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30267), show_x_filtered_items), url)
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30267), item_limit), url)
# Recently Added Movies
params = {}
@@ -890,25 +884,25 @@ def display_movies_type(menu_params, view):
params["SortBy"] = "DateCreated"
params["SortOrder"] = "Descending"
params["Filters"] = "IsNotFolder"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none"
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30268), show_x_filtered_items), url)
add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30268), item_limit), url)
# Collections
params = {}
if view is not None:
params["ParentId"] = view.get("Id")
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Boxset"
params["Recursive"] = True
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets"
add_menu_directory_item('{}{}'.format(view_name, translate_string(30410)), url)
# Favorite Collections
params["Filters"] = "IsFavorite"
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets"
add_menu_directory_item('{}{}'.format(view_name, translate_string(30415)), url)
@@ -955,12 +949,12 @@ def display_library_views(params):
handle = int(sys.argv[1])
xbmcplugin.setContent(handle, 'files')
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
if server is None:
return
data_manager = DataManager()
views_url = "{server}/Users/{userid}/Views?format=json"
views_url = "/Users/{}/Views?format=json".format(user_id)
views = data_manager.get_content(views_url)
if not views:
return []
@@ -974,7 +968,7 @@ def display_library_views(params):
if collection_type in view_types or item_type == "Channel":
view_name = view.get("Name")
art = get_art(item=view, server=server)
art['landscape'] = downloadUtils.get_artwork(view, "Primary", server=server)
art['landscape'] = get_art_url(view, "Primary", server=server)
plugin_path = "plugin://plugin.video.jellycon/?mode=SHOW_ADDON_MENU&type=library_item&view_id=" + view.get("Id")
@@ -993,10 +987,10 @@ def display_library_views(params):
def get_playlist_path(view_info):
params = {}
params["ParentId"] = view_info.get("Id")
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["ImageTypeLimit"] = 1
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=playlists"
return url
@@ -1004,7 +998,7 @@ def get_playlist_path(view_info):
def get_collection_path(view_info):
params = {}
params["ParentId"] = view_info.get("Id")
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
params["ImageTypeLimit"] = 1
params["IncludeItemTypes"] = "Boxset"
params["CollapseBoxSetItems"] = True
@@ -1012,7 +1006,7 @@ def get_collection_path(view_info):
params["Recursive"] = True
params["IsMissing"] = False
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets"
return url
@@ -1022,9 +1016,9 @@ def get_channel_path(view):
params["ParentId"] = view.get("Id")
params["IsMissing"] = False
params["ImageTypeLimit"] = 1
params["Fields"] = "{field_filters}"
params["Fields"] = get_default_filters()
path = get_jellyfin_url("{server}/Users/{userid}/Items", params)
path = get_jellyfin_url("/Users/{}/Items".format(user_id), params)
url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=files"
return url
@@ -1032,7 +1026,7 @@ def get_channel_path(view):
def display_library_view(params):
node_id = params.get("view_id")
view_info_url = "{server}/Users/{userid}/Items/" + node_id
view_info_url = "/Users/{}/Items/".format(user_id) + node_id
data_manager = DataManager()
view_info = data_manager.get_content(view_info_url)
@@ -1055,28 +1049,27 @@ def display_library_view(params):
def show_widgets():
settings = xbmcaddon.Addon()
show_x_filtered_items = settings.getSetting("show_x_filtered_items")
item_limit = settings.getSetting("show_x_filtered_items")
add_menu_directory_item("All Movies",
'plugin://plugin.video.jellycon/library/movies')
add_menu_directory_item(translate_string(30257) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30257) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_movies')
add_menu_directory_item(translate_string(30258) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30258) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=inprogress_movies')
add_menu_directory_item(translate_string(30269) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30269) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=random_movies')
add_menu_directory_item(translate_string(30403) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30403) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=movie_recommendations')
add_menu_directory_item(translate_string(30287) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30287) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_tvshows')
add_menu_directory_item(translate_string(30263) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30263) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_episodes')
add_menu_directory_item(translate_string(30264) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30264) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=inprogress_episodes')
add_menu_directory_item(translate_string(30265) + " (" + show_x_filtered_items + ")",
add_menu_directory_item(translate_string(30265) + " (" + item_limit + ")",
'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=nextup_episodes')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@@ -1108,14 +1101,14 @@ def set_library_window_values(force=False):
home_window.clear_property("view_item.%i.thumb" % index)
data_manager = DataManager()
url = "{server}/Users/{userid}/Views"
url = "/Users/{}/Views".format(user_id)
result = data_manager.get_content(url)
if result is None:
return
result = result.get("Items")
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
index = 0
for item in result:
@@ -1138,7 +1131,7 @@ def set_library_window_values(force=False):
home_window.set_property(prop_name, collection_type)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, collection_type))
thumb = downloadUtils.get_artwork(item, "Primary", server=server)
thumb = get_art_url(item, "Primary", server=server)
prop_name = "view_item.%i.thumb" % index
home_window.set_property(prop_name, thumb)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, thumb))

View File

@@ -12,10 +12,10 @@ import os
import re
from six.moves.urllib.parse import urlencode
from .api import API
from .loghandler import LazyLogger
from .downloadutils import DownloadUtils
from .dialogs import ResumeDialog
from .utils import send_event_notification, convert_size, get_device_id, translate_string
from .utils import send_event_notification, convert_size, get_device_id, translate_string, load_user_details
from .kodi_utils import HomeWindow
from .datamanager import DataManager, clear_old_cache_data
from .item_functions import extract_item_info, add_gui_item, get_art
@@ -25,13 +25,20 @@ from .tracking import timer
from .playnext import PlayNextDialog
log = LazyLogger(__name__)
download_utils = DownloadUtils()
user_details = load_user_details()
settings = xbmcaddon.Addon()
api = API(
settings.getSetting('server_address'),
user_details.get('user_id'),
user_details.get('token')
)
def play_all_files(items, play_items=True):
home_window = HomeWindow()
log.debug("playAllFiles called with items: {0}", items)
server = download_utils.get_server()
server = settings.getSetting('server_address')
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
@@ -43,7 +50,7 @@ def play_all_files(items, play_items=True):
item_id = item.get("Id")
# get playback info
playback_info = download_utils.get_item_playback_info(item_id, False)
playback_info = get_item_playback_info(item_id, False)
if playback_info is None:
log.debug("playback_info was None, could not get MediaSources so can not play!")
return
@@ -115,8 +122,7 @@ def play_list_of_items(id_list):
items = []
for item_id in id_list:
url = "{server}/Users/{userid}/Items/%s?format=json"
url = url % (item_id,)
url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id)
result = data_manager.get_content(url)
if result is None:
log.debug("Playfile item was None, so can not play!")
@@ -130,12 +136,11 @@ def add_to_playlist(play_info):
log.debug("Adding item to playlist : {0}".format(play_info))
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
server = download_utils.get_server()
server = settings.getSetting('server_address')
item_id = play_info.get("item_id")
url = "{server}/Users/{userid}/Items/%s?format=json"
url = url % (item_id,)
url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id)
data_manager = DataManager()
item = data_manager.get_content(url)
if item is None:
@@ -143,7 +148,7 @@ def add_to_playlist(play_info):
return
# get playback info
playback_info = download_utils.get_item_playback_info(item_id, False)
playback_info = get_item_playback_info(item_id, False)
if playback_info is None:
log.debug("playback_info was None, could not get MediaSources so can not play!")
return
@@ -201,7 +206,7 @@ def add_to_playlist(play_info):
def get_playback_intros(item_id):
log.debug("get_playback_intros")
data_manager = DataManager()
url = "{server}/Users/{userid}/Items/%s/Intros" % item_id
url = "/Users/{}/Items/{}/Intros".format(api.user_id, item_id)
intro_items = data_manager.get_content(url)
if intro_items is None:
@@ -242,15 +247,14 @@ def play_file(play_info):
log.debug("playFile id({0}) resume({1}) force_transcode({2})".format(item_id, auto_resume, force_transcode))
settings = xbmcaddon.Addon()
addon_path = settings.getAddonInfo('path')
force_auto_resume = settings.getSetting('forceAutoResume') == 'true'
jump_back_amount = int(settings.getSetting("jump_back_amount"))
play_cinema_intros = settings.getSetting('play_cinema_intros') == 'true'
server = download_utils.get_server()
server = settings.getSetting('server_address')
url = "{server}/Users/{userid}/Items/%s?format=json" % (item_id,)
url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id)
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("Playfile item: {0}".format(result))
@@ -262,7 +266,7 @@ def play_file(play_info):
# if this is a season, playlist or album then play all items in that parent
if result.get("Type") in ["Season", "MusicAlbum", "Playlist"]:
log.debug("PlayAllFiles for parent item id: {0}".format(item_id))
url = ('{server}/Users/{userid}/items' +
url = ('/Users/{}/items'.format(api.user_id) +
'?ParentId=%s' +
'&Fields=MediaSources' +
'&format=json')
@@ -279,7 +283,7 @@ def play_file(play_info):
# if this is a program from live tv epg then play the actual channel
if result.get("Type") == "Program":
channel_id = result.get("ChannelId")
url = "{server}/Users/{userid}/Items/%s?format=json" % (channel_id,)
url = "/Users/{}/Items/{}?format=json".format(api.user_id, channel_id)
result = data_manager.get_content(url)
item_id = result["Id"]
@@ -294,7 +298,7 @@ def play_file(play_info):
return
# get playback info from the server using the device profile
playback_info = download_utils.get_item_playback_info(item_id, force_transcode)
playback_info = get_item_playback_info(item_id, force_transcode)
if playback_info is None:
log.debug("playback_info was None, could not get MediaSources so can not play!")
return
@@ -546,7 +550,7 @@ def get_next_episode(item):
log.debug("No episode number, can not get next")
return None
url = ('{server}/Users/{userid}/Items?' +
url = ('/Users/{}/Items?'.format(api.user_id) +
'?Recursive=true' +
'&ParentId=' + parent_id +
'&IsVirtualUnaired=false' +
@@ -580,7 +584,7 @@ def send_next_episode_details(item, next_episode):
return
gui_options = {}
gui_options["server"] = download_utils.get_server()
gui_options["server"] = settings.getSetting('server_address')
gui_options["name_format"] = None
gui_options["name_format_type"] = ""
@@ -776,7 +780,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s
# Load subtitles in the listitem if downloadable
if select_subs_index in downloadable_streams:
subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index)
subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index)
log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url))
list_item.setSubtitles([subtitle_url])
else:
@@ -796,7 +800,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s
# Load subtitles in the listitem if downloadable
if select_subs_index in downloadable_streams:
subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index)
subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index)
log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url))
list_item.setSubtitles([subtitle_url])
else:
@@ -824,6 +828,10 @@ def external_subs(media_source, list_item, item_id):
externalsubs = []
sub_names = []
server = settings.getSetting('server_address')
user_details = load_user_details()
token = user_details.get('token')
for stream in media_streams:
if (stream['Type'] == "Subtitle"
@@ -833,8 +841,7 @@ def external_subs(media_source, list_item, item_id):
index = stream['Index']
source_id = media_source['Id']
server = download_utils.get_server()
token = download_utils.authenticate()
language = stream.get('Language', '')
codec = stream.get('Codec', '')
@@ -860,7 +867,6 @@ def external_subs(media_source, list_item, item_id):
if len(externalsubs) == 0:
return
settings = xbmcaddon.Addon()
direct_stream_sub_select = settings.getSetting("direct_stream_sub_select")
if direct_stream_sub_select == "0" or (len(externalsubs) == 1 and not direct_stream_sub_select == "2"):
@@ -928,8 +934,8 @@ def send_progress():
log.debug("Sending POST progress started: {0}".format(postdata))
url = "{server}/Sessions/Playing/Progress"
download_utils.download_url(url, post_body=postdata, method="POST")
url = "/Sessions/Playing/Progress"
api.post(url, postdata)
def get_volume():
@@ -946,7 +952,6 @@ def get_volume():
def prompt_for_stop_actions(item_id, data):
log.debug("prompt_for_stop_actions Called : {0}".format(data))
settings = xbmcaddon.Addon()
current_position = data.get("current_position", 0)
duration = data.get("duration", 0)
next_episode = data.get("next_episode")
@@ -1040,7 +1045,7 @@ def stop_all_playback():
if jellyfin_item_id is not None and current_position >= 0:
log.debug("Playback Stopped at: {0}".format(current_position))
url = "{server}/Sessions/Playing/Stopped"
url = "/Sessions/Playing/Stopped"
postdata = {
'ItemId': jellyfin_item_id,
'MediaSourceId': jellyfin_source_id,
@@ -1048,7 +1053,7 @@ def stop_all_playback():
'RunTimeTicks': int(duration * 10000000),
'PlaySessionId': play_session_id
}
download_utils.download_url(url, post_body=postdata, method="POST")
api.post(url, postdata)
data["currently_playing"] = False
if data.get("play_action_type", "") == "play":
@@ -1058,8 +1063,8 @@ def stop_all_playback():
if data.get('playback_type') == 'Transcode':
device_id = get_device_id()
url = "{server}/Videos/ActiveEncodings?DeviceId=%s" % device_id
download_utils.download_url(url, method="DELETE")
url = "/Videos/ActiveEncodings?DeviceId=%s" % device_id
api.delete(url)
for entry in clear_entries:
del played_information[entry]
@@ -1087,7 +1092,6 @@ def get_playing_data():
item_id = play_data.get("item_id")
settings = xbmcaddon.Addon()
server = settings.getSetting('server_address')
try:
playing_file = player.getPlayingFile()
@@ -1129,11 +1133,10 @@ def get_play_url(media_source, play_session_id):
return playurl, "0", listitem_props
# get all the options
addon_settings = xbmcaddon.Addon()
server = download_utils.get_server()
use_https = addon_settings.getSetting('protocol') == "1"
verify_cert = addon_settings.getSetting('verify_cert') == 'true'
allow_direct_file_play = addon_settings.getSetting('allow_direct_file_play') == 'true'
server = settings.getSetting('server_address')
use_https = settings.getSetting('protocol') == "1"
verify_cert = settings.getSetting('verify_cert') == 'true'
allow_direct_file_play = settings.getSetting('allow_direct_file_play') == 'true'
can_direct_play = media_source["SupportsDirectPlay"]
can_direct_stream = media_source["SupportsDirectStream"]
@@ -1180,15 +1183,16 @@ def get_play_url(media_source, play_session_id):
if can_transcode and playurl is None:
item_id = media_source.get('Id')
device_id = get_device_id()
user_token = download_utils.authenticate()
playback_bitrate = addon_settings.getSetting("force_max_stream_bitrate")
user_token = user_details.get('token')
playback_bitrate = settings.getSetting("force_max_stream_bitrate")
bitrate = int(playback_bitrate) * 1000
playback_max_width = addon_settings.getSetting("playback_max_width")
audio_codec = addon_settings.getSetting("audio_codec")
audio_playback_bitrate = addon_settings.getSetting("audio_playback_bitrate")
playback_max_width = settings.getSetting("playback_max_width")
audio_codec = settings.getSetting("audio_codec")
audio_playback_bitrate = settings.getSetting("audio_playback_bitrate")
audio_bitrate = int(audio_playback_bitrate) * 1000
audio_max_channels = addon_settings.getSetting("audio_max_channels")
playback_video_force_8 = addon_settings.getSetting("playback_video_force_8") == "true"
audio_max_channels = settings.getSetting("audio_max_channels")
playback_video_force_8 = settings.getSetting("playback_video_force_8") == "true"
transcode_params = {
"MediaSourceId": item_id,
@@ -1302,8 +1306,8 @@ class Service(xbmc.Player):
log.debug("Sending POST play started: {0}".format(postdata))
url = "{server}/Sessions/Playing"
download_utils.download_url(url, post_body=postdata, method="POST")
url = "/Sessions/Playing"
api.post(url, postdata)
home_screen = HomeWindow()
home_screen.set_property("currently_playing_id", str(jellyfin_item_id))
@@ -1389,7 +1393,6 @@ class PlaybackService(xbmc.Monitor):
home_screen = HomeWindow()
home_screen.clear_property("skip_select_user")
settings = xbmcaddon.Addon()
stop_playback = settings.getSetting("stopPlaybackOnScreensaver") == 'true'
if stop_playback:
@@ -1415,7 +1418,6 @@ class PlaybackService(xbmc.Monitor):
self.background_image_cache_thread.stop_activity()
self.background_image_cache_thread = None
settings = xbmcaddon.Addon()
show_change_user = settings.getSetting('changeUserOnScreenSaver') == 'true'
if show_change_user:
home_screen = HomeWindow()
@@ -1423,3 +1425,188 @@ class PlaybackService(xbmc.Monitor):
if skip_select_user is not None and skip_select_user == "true":
return
xbmc.executebuiltin("RunScript(plugin.video.jellycon,0,?mode=CHANGE_USER)")
def get_item_playback_info(item_id, force_transcode):
filtered_codecs = []
if settings.getSetting("force_transcode_h265") == "true":
filtered_codecs.append("hevc")
filtered_codecs.append("h265")
if settings.getSetting("force_transcode_mpeg2") == "true":
filtered_codecs.append("mpeg2video")
if settings.getSetting("force_transcode_msmpeg4v3") == "true":
filtered_codecs.append("msmpeg4v3")
if settings.getSetting("force_transcode_mpeg4") == "true":
filtered_codecs.append("mpeg4")
playback_bitrate = settings.getSetting("max_stream_bitrate")
force_playback_bitrate = settings.getSetting("force_max_stream_bitrate")
if force_transcode:
playback_bitrate = force_playback_bitrate
audio_codec = settings.getSetting("audio_codec")
audio_playback_bitrate = settings.getSetting("audio_playback_bitrate")
audio_max_channels = settings.getSetting("audio_max_channels")
audio_bitrate = int(audio_playback_bitrate) * 1000
bitrate = int(playback_bitrate) * 1000
profile = {
"Name": "Kodi",
"MaxStaticBitrate": bitrate,
"MaxStreamingBitrate": bitrate,
"MusicStreamingTranscodingBitrate": audio_bitrate,
"TimelineOffsetSeconds": 5,
"TranscodingProfiles": [
{
"Type": "Audio"
},
{
"Container": "ts",
"Protocol": "hls",
"Type": "Video",
"AudioCodec": audio_codec,
"VideoCodec": "h264",
"MaxAudioChannels": audio_max_channels
},
{
"Container": "jpeg",
"Type": "Photo"
}
],
"DirectPlayProfiles": [
{
"Type": "Video"
},
{
"Type": "Audio"
},
{
"Type": "Photo"
}
],
"ResponseProfiles": [],
"ContainerProfiles": [],
"CodecProfiles": [],
"SubtitleProfiles": [
{
"Format": "srt",
"Method": "External"
},
{
"Format": "srt",
"Method": "Embed"
},
{
"Format": "ass",
"Method": "External"
},
{
"Format": "ass",
"Method": "Embed"
},
{
"Format": "sub",
"Method": "Embed"
},
{
"Format": "sub",
"Method": "External"
},
{
"Format": "ssa",
"Method": "Embed"
},
{
"Format": "ssa",
"Method": "External"
},
{
"Format": "smi",
"Method": "Embed"
},
{
"Format": "smi",
"Method": "External"
},
{
"Format": "pgssub",
"Method": "Embed"
},
{
"Format": "pgssub",
"Method": "External"
},
{
"Format": "dvdsub",
"Method": "Embed"
},
{
"Format": "dvdsub",
"Method": "External"
},
{
"Format": "pgs",
"Method": "Embed"
},
{
"Format": "pgs",
"Method": "External"
}
]
}
if len(filtered_codecs) > 0:
profile['DirectPlayProfiles'][0]['VideoCodec'] = "-%s" % ",".join(filtered_codecs)
if force_transcode:
profile['DirectPlayProfiles'] = []
if settings.getSetting("playback_video_force_8") == "true":
profile['CodecProfiles'].append(
{
"Type": "Video",
"Codec": "h264",
"Conditions": [
{
"Condition": "LessThanEqual",
"Property": "VideoBitDepth",
"Value": "8",
"IsRequired": False
}
]
}
)
profile['CodecProfiles'].append(
{
"Type": "Video",
"Codec": "h265,hevc",
"Conditions": [
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
"Value": "main"
}
]
}
)
playback_info = {
'UserId': api.user_id,
'DeviceProfile': profile,
'AutoOpenLiveStream': True
}
if force_transcode:
url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate)
else:
url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate)
log.debug("PlaybackInfo : {0}".format(url))
log.debug("PlaybackInfo : {0}".format(profile))
play_info_result = api.post(url, playback_info)
log.debug("PlaybackInfo : {0}".format(play_info_result))
return play_info_result

View File

@@ -3,7 +3,6 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import socket
import json
import requests
import time
from datetime import datetime
@@ -12,9 +11,9 @@ import xbmcgui
import xbmc
from .kodi_utils import HomeWindow
from .downloadutils import DownloadUtils
from .api import API
from .loghandler import LazyLogger
from .utils import datetime_from_string, translate_string, get_version, load_user_details
from .utils import datetime_from_string, translate_string, save_user_details, load_user_details
log = LazyLogger(__name__)
@@ -26,26 +25,15 @@ def check_connection_speed():
log.debug("check_connection_speed")
settings = xbmcaddon.Addon()
verify_cert = settings.getSetting('verify_cert') == 'true'
http_timeout = int(settings.getSetting("http_timeout"))
speed_test_data_size = int(settings.getSetting("speed_test_data_size"))
test_data_size = speed_test_data_size * 1000000
user_details = load_user_details()
du = DownloadUtils()
server = du.get_server()
url = server + "/playback/bitratetest?size=%s" % test_data_size
head = du.get_auth_header(True)
head["User-Agent"] = "JellyCon-{}".format(get_version())
request_details = {
"stream": True,
"headers": head
}
if not verify_cert:
request_details["verify"] = False
api = API(
settings.getSetting('server_address'),
user_details.get('user_id'),
user_details.get('token')
)
progress_dialog = xbmcgui.DialogProgress()
message = 'Testing with {0} MB of data'.format(speed_test_data_size)
@@ -53,8 +41,7 @@ def check_connection_speed():
start_time = time.time()
log.debug("Starting Connection Speed Test")
response = requests.get(url, **request_details)
response = api.speedtest(test_data_size)
last_percentage_done = 0
total_data_read = 0
@@ -90,31 +77,6 @@ def check_connection_speed():
return speed
def check_safe_delete_available():
log.debug("check_safe_delete_available")
du = DownloadUtils()
result = du.download_url("{server}/Plugins")
if result:
log.debug("Server Plugin List: {0}".format(result))
safe_delete_found = False
for plugin in result:
if plugin["Name"] == "Safe Delete":
safe_delete_found = True
break
log.debug("Safe Delete Plugin Available: {0}".format(safe_delete_found))
home_window = HomeWindow()
if safe_delete_found:
home_window.set_property("safe_delete_plugin_available", "true")
else:
home_window.clear_property("safe_delete_plugin_available")
else:
log.debug("Error getting server plugin list")
def get_server_details():
log.debug("Getting Server Details from Network")
servers = []
@@ -160,18 +122,17 @@ def check_server(force=False, change_user=False, notify=False):
log.debug("checkServer Called")
settings = xbmcaddon.Addon()
server_url = ""
something_changed = False
du = DownloadUtils()
# Initialize api object
api = API()
if force is False:
# if not forcing use server details from settings
svr = du.get_server()
if svr is not None:
server_url = svr
api.server = settings.getSetting('server_address')
# if the server is not set then try to detect it
if server_url == "":
if not api.server:
# scan for local server
server_info = get_server_details()
@@ -194,9 +155,9 @@ def check_server(force=False, change_user=False, notify=False):
server_list,
useDetails=True)
if return_index != -1:
server_url = server_info[return_index]["Address"]
api.server = server_info[return_index]["Address"]
if not server_url:
if not api.server:
return_index = xbmcgui.Dialog().yesno(__addon_name__, '{}\n{}'.format(translate_string(30282), translate_string(30370)))
if not return_index:
xbmc.executebuiltin("ActivateWindow(Home)")
@@ -205,40 +166,38 @@ def check_server(force=False, change_user=False, notify=False):
while True:
kb = xbmc.Keyboard()
kb.setHeading(translate_string(30372))
if server_url:
kb.setDefault(server_url)
if api.server:
kb.setDefault(api.server)
else:
kb.setDefault("http://")
kb.doModal()
if kb.isConfirmed():
server_url = kb.getText()
api.server = kb.getText()
else:
xbmc.executebuiltin("ActivateWindow(Home)")
return
public_lookup_url = "%s/System/Info/Public?format=json" % (server_url)
log.debug("Testing_Url: {0}".format(public_lookup_url))
progress = xbmcgui.DialogProgress()
progress.create('{} : {}'.format(__addon_name__, translate_string(30376)))
progress.update(0, translate_string(30377))
result = du.download_url(public_lookup_url, authenticate=False)
api.server = api.server
result = api.get('/System/Info/Public')
progress.close()
if result:
xbmcgui.Dialog().ok('{} : {}'.format(__addon_name__, translate_string(30167)),
server_url)
api.server)
break
else:
return_index = xbmcgui.Dialog().yesno('{} : {}'.format(__addon_name__, translate_string(30135)),
server_url,
api.server,
translate_string(30371))
if not return_index:
xbmc.executebuiltin("ActivateWindow(Home)")
return
log.debug("Selected server: {0}".format(server_url))
settings.setSetting("server_address", server_url)
log.debug("Selected server: {0}".format(api.server))
settings.setSetting("server_address", api.server)
something_changed = True
# do we need to change the user
@@ -250,7 +209,7 @@ def check_server(force=False, change_user=False, notify=False):
# stop playback when switching users
xbmc.Player().stop()
users, user_selection = user_select(server_url, current_username)
users, user_selection = user_select(api, current_username)
if user_selection > -1:
@@ -288,33 +247,32 @@ def check_server(force=False, change_user=False, notify=False):
if kb.isConfirmed():
password = kb.getText()
# TODO: Make the authenticate function operate normally. Network rework
home_window.set_property('password', password)
if something_changed:
home_window = HomeWindow()
home_window.clear_property("jellycon_widget_reload")
du.authenticate()
auth_payload = {'username': selected_user_name, 'pw': password}
user = api.authenticate(auth_payload)
token = user.get('AccessToken')
user_id = user.get('User').get('Id')
save_user_details(selected_user_name, user_id, token)
xbmc.executebuiltin("ActivateWindow(Home)")
if "estuary_jellycon" in xbmc.getSkinDir():
xbmc.executebuiltin("SetFocus(9000, 0, absolute)")
xbmc.executebuiltin("ReloadSkin()")
def user_select(server_url, current_username):
def user_select(api, current_username):
'''
Display user selection screen
'''
du = DownloadUtils()
# Retrieve list of public users from server
result = du.download_url('{}/Users/Public'.format(server_url), authenticate=False)
result = api.get('/Users/Public')
# Build user display
selected_id = -1
users = []
for user in result:
user_item = create_user_listitem(du, user)
user_item = create_user_listitem(api.server, user)
if user_item:
users.append(user_item)
name = user.get("Name")
@@ -346,7 +304,7 @@ def user_select(server_url, current_username):
return (users, user_selection)
def create_user_listitem(du, user):
def create_user_listitem(server, user):
'''
Create a user listitem for the user selection screen
'''
@@ -378,9 +336,17 @@ def create_user_listitem(du, user):
time_ago = "Active: {} ago".format(time_ago)
user_item = xbmcgui.ListItem(name)
user_image = du.get_user_artwork(user, 'Primary')
if not user_image:
# If the user doesn't have a profile image, user the default
if 'PrimaryImageTag' not in user:
user_image = "DefaultUser.png"
else:
user_id = user.get('Id')
tag = user.get('PrimaryImageTag')
user_image = '{}/Users/{}/Images/Primary?Format=original&tag={}'.format(
server, user_id, tag
)
art = {"Thumb": user_image}
user_item.setArt(art)

View File

@@ -4,7 +4,6 @@ import sys
import xbmcgui
import xbmcplugin
from .downloadutils import DownloadUtils
from .loghandler import LazyLogger
from .item_functions import get_art
from .datamanager import DataManager
@@ -16,10 +15,9 @@ def show_server_sessions():
log.debug("showServerSessions Called")
handle = int(sys.argv[1])
download_utils = DownloadUtils()
data_manager = DataManager()
url = "{server}/Users/{userid}"
url = "/Users/{}".format(data_manager.api.user_id)
results = data_manager.get_content(url)
is_admin = results.get("Policy", {}).get("IsAdministrator", False)
@@ -27,7 +25,7 @@ def show_server_sessions():
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
return
url = "{server}/Sessions"
url = "/Sessions"
results = data_manager.get_content(url)
log.debug("session_info: {0}".format(results))
@@ -59,7 +57,7 @@ def show_server_sessions():
art = {}
if now_playing:
server = download_utils.get_server()
server = settings.getSetting('server_address')
art = get_art(now_playing, server)
runtime = now_playing.get("RunTimeTicks", 0)

View File

@@ -29,13 +29,10 @@ throwaway = time.strptime('20110101', '%Y%m%d')
log = LazyLogger(__name__)
def get_jellyfin_url(base_url, params):
def get_jellyfin_url(path, params):
params["format"] = "json"
url_params = urlencode(params)
# Filthy hack until I get around to reworking the network flow
# It relies on {thing} strings in downloadutils.py
url_params = url_params.replace('%7B', '{').replace('%7D', '}')
return base_url + "?" + url_params
return '{}?{}'.format(path, url_params)
def get_checksum(item):
@@ -210,3 +207,111 @@ def load_user_details():
else:
return {}
def get_art_url(data, art_type, parent=False, index=0, server=None):
item_id = data["Id"]
item_type = data["Type"]
if item_type in ["Episode", "Season"]:
if art_type != "Primary" or parent is True:
item_id = data["SeriesId"]
image_tag = ""
# for episodes always use the parent BG
if item_type == "Episode" and art_type == "Backdrop":
item_id = data.get("ParentBackdropItemId")
bg_item_tags = data.get("ParentBackdropImageTags", [])
if bg_item_tags:
image_tag = bg_item_tags[0]
elif art_type == "Backdrop" and parent is True:
item_id = data.get("ParentBackdropItemId")
bg_item_tags = data.get("ParentBackdropImageTags", [])
if bg_item_tags:
image_tag = bg_item_tags[0]
elif art_type == "Backdrop":
bg_tags = data.get("BackdropImageTags", [])
if bg_tags:
image_tag = bg_tags[index]
elif parent is False:
image_tags = data.get("ImageTags", [])
if image_tags:
image_tag_type = image_tags.get(art_type)
if image_tag_type:
image_tag = image_tag_type
elif parent is True:
if (item_type == "Episode" or item_type == "Season") and art_type == 'Primary':
tag_name = 'SeriesPrimaryImageTag'
id_name = 'SeriesId'
else:
tag_name = 'Parent%sImageTag' % art_type
id_name = 'Parent%sItemId' % art_type
parent_image_id = data.get(id_name)
parent_image_tag = data.get(tag_name)
if parent_image_id is not None and parent_image_tag is not None:
item_id = parent_image_id
image_tag = parent_image_tag
# ParentTag not passed for Banner and Art
if not image_tag and not ((art_type == 'Banner' or art_type == 'Art') and parent is True):
return ""
artwork = "{}/Items/{}/Images/{}/{}?Format=original&Tag={}".format(
server, item_id, art_type, index, image_tag)
return artwork
def image_url(item_id, art_type, index, width, height, image_tag, server):
# test imageTag e3ab56fe27d389446754d0fb04910a34
artwork = "{}/Items/{}/Images/{}/{}?Format=original&Tag={}".format(server, item_id, art_type, index, image_tag)
if int(width) > 0:
artwork += '&MaxWidth={}'.format(width)
if int(height) > 0:
artwork += '&MaxHeight={}'.format(height)
return artwork
def get_default_filters():
addon_settings = xbmcaddon.Addon()
include_media = addon_settings.getSetting("include_media") == "true"
include_people = addon_settings.getSetting("include_people") == "true"
include_overview = addon_settings.getSetting("include_overview") == "true"
filer_list = [
"DateCreated",
"EpisodeCount",
"SeasonCount",
"Path",
"Genres",
"Studios",
"Etag",
"Taglines",
"SortName",
"RecursiveItemCount",
"ChildCount",
"ProductionLocations",
"CriticRating",
"OfficialRating",
"CommunityRating",
"PremiereDate",
"ProductionYear",
"AirTime",
"Status",
"Tags"
]
if include_media:
filer_list.append("MediaStreams")
if include_people:
filer_list.append("People")
if include_overview:
filer_list.append("Overview")
return ','.join(filer_list)

View File

@@ -9,14 +9,14 @@ import websocket
import time
import xbmc
import xbmcaddon
import xbmcgui
from .functions import play_action
from .loghandler import LazyLogger
from . import downloadutils
from .jsonrpc import JsonRpc
from .kodi_utils import HomeWindow
from .utils import get_device_id
from .utils import get_device_id, load_user_details
log = LazyLogger(__name__)
@@ -237,16 +237,16 @@ class WebSocketClient(threading.Thread):
def run(self):
download_utils = downloadutils.DownloadUtils()
token = None
while token is None or token == "":
token = download_utils.authenticate()
user_details = load_user_details()
token = user_details.get('token')
if self.monitor.waitForAbort(10):
return
# Get the appropriate prefix for the websocket
server = download_utils.get_server()
settings = xbmcaddon.Addon()
server = settings.getSetting('server_address')
if "https://" in server:
server = server.replace('https://', 'wss://')
else:
@@ -290,5 +290,12 @@ class WebSocketClient(threading.Thread):
def post_capabilities(self):
download_utils = downloadutils.DownloadUtils()
download_utils.post_capabilities()
user_details = load_user_details()
api = API(
settings.getSetting('server_address'),
user_details.get('user_id'),
user_details.get('token')
)
api.post_capabilities()

View File

@@ -8,8 +8,8 @@ import hashlib
import random
import time
from .downloadutils import DownloadUtils
from .utils import get_jellyfin_url
from .api import API
from .utils import get_jellyfin_url, image_url, load_user_details, get_art_url, get_default_filters
from .datamanager import DataManager
from .loghandler import LazyLogger
from .kodi_utils import HomeWindow
@@ -17,7 +17,14 @@ from .dir_functions import process_directory
from .tracking import timer
log = LazyLogger(__name__)
downloadUtils = DownloadUtils()
settings = xbmcaddon.Addon()
user_details = load_user_details()
user_id = user_details.get('user_id')
api = API(
settings.getSetting('server_address'),
user_id,
user_details.get('token')
)
dataManager = DataManager()
kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
@@ -30,20 +37,21 @@ def set_random_movies():
log.debug("set_random_movies Called")
settings = xbmcaddon.Addon()
item_limit = settings.getSetting("show_x_filtered_items")
hide_watched = settings.getSetting("hide_watched") == "true"
url_params = {}
url_params["Recursive"] = True
url_params["limit"] = 20
url_params["limit"] = item_limit
if hide_watched:
url_params["IsPlayed"] = False
url_params["SortBy"] = "Random"
url_params["IncludeItemTypes"] = "Movie"
url_params["ImageTypeLimit"] = 0
url = get_jellyfin_url("{server}/Users/{userid}/Items", url_params)
url = get_jellyfin_url("/Users/{}/Items".format(user_id), url_params)
results = downloadUtils.download_url(url, suppress=True)
results = api.get(url)
randon_movies_list = []
if results is not None:
@@ -70,6 +78,8 @@ def set_background_image(force=False):
global background_current_item
global background_items
server = settings.getSetting('server_address')
if force:
background_current_item = 0
del background_items
@@ -86,17 +96,16 @@ def set_background_image(force=False):
url_params["IncludeItemTypes"] = "Movie,Series"
url_params["ImageTypeLimit"] = 1
url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params)
url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params)
server = downloadUtils.get_server()
results = downloadUtils.download_url(url, suppress=True)
results = api.get(url)
if results is not None:
items = results.get("Items", [])
background_current_item = 0
background_items = []
for item in items:
bg_image = downloadUtils.get_artwork(
bg_image = get_art_url(
item, "Backdrop", server=server)
if bg_image:
label = item.get("Name")
@@ -148,9 +157,9 @@ def check_for_new_content():
url_params["IncludeItemTypes"] = "Movie,Episode"
url_params["ImageTypeLimit"] = 0
added_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params)
added_url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params)
result = downloadUtils.download_url(added_url, suppress=True)
result = api.get(added_url)
log.debug("LATEST_ADDED_ITEM: {0}".format(result))
last_added_date = ""
@@ -170,9 +179,9 @@ def check_for_new_content():
url_params["IncludeItemTypes"] = "Movie,Episode"
url_params["ImageTypeLimit"] = 0
played_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params)
played_url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params)
result = downloadUtils.download_url(played_url, suppress=True)
result = api.get(played_url)
log.debug("LATEST_PLAYED_ITEM: {0}".format(result))
last_played_date = ""
@@ -202,12 +211,12 @@ def check_for_new_content():
@timer
def get_widget_content_cast(handle, params):
log.debug("getWigetContentCast Called: {0}".format(params))
server = downloadUtils.get_server()
server = settings.getSetting('server_address')
item_id = params["id"]
data_manager = DataManager()
result = data_manager.get_content(
"{server}/Users/{userid}/Items/" + item_id)
"/Users/{}/Items/{}".format(user_id, item_id))
log.debug("ItemInfo: {0}".format(result))
if not result:
@@ -233,7 +242,7 @@ def get_widget_content_cast(handle, params):
person_tag = person.get("PrimaryImageTag")
person_thumbnail = None
if person_tag:
person_thumbnail = downloadUtils.image_url(
person_thumbnail = image_url(
person_id, "Primary", 0, 400, 400, person_tag, server=server)
if kodi_version > 17:
@@ -269,6 +278,7 @@ def get_widget_content(handle, params):
log.debug("getWigetContent Called: {0}".format(params))
settings = xbmcaddon.Addon()
item_limit = settings.getSetting("show_x_filtered_items")
hide_watched = settings.getSetting("hide_watched") == "true"
use_cached_widget_data = settings.getSetting(
"use_cached_widget_data") == "true"
@@ -280,10 +290,10 @@ def get_widget_content(handle, params):
log.debug("widget_type: {0}".format(widget_type))
url_verb = "{server}/Users/{userid}/Items"
url_verb = "/Users/{}/Items".format(user_id)
url_params = {}
url_params["Limit"] = "{ItemLimit}"
url_params["Fields"] = "{field_filters}"
url_params["Limit"] = item_limit
url_params["Fields"] = get_default_filters()
url_params["ImageTypeLimit"] = 1
url_params["IsMissing"] = False
@@ -297,7 +307,7 @@ def get_widget_content(handle, params):
url_params["IsPlayed"] = False
url_params["IsVirtualUnaired"] = False
url_params["IncludeItemTypes"] = "Movie"
url_params["Limit"] = 20
url_params["Limit"] = item_limit
elif widget_type == "inprogress_movies":
xbmcplugin.setContent(handle, 'movies')
@@ -307,27 +317,28 @@ def get_widget_content(handle, params):
url_params["Filters"] = "IsResumable"
url_params["IsVirtualUnaired"] = False
url_params["IncludeItemTypes"] = "Movie"
url_params["Limit"] = 20
url_params["Limit"] = item_limit
elif widget_type == "random_movies":
home_window = HomeWindow()
xbmcplugin.setContent(handle, 'movies')
url_params["Ids"] = "{random_movies}"
url_params["Ids"] = home_window.get_property("random-movies")
elif widget_type == "recent_tvshows":
xbmcplugin.setContent(handle, 'episodes')
url_verb = '{server}/Users/{userid}/Items/Latest'
url_verb = '/Users/{}/Items/Latest'.format(user_id)
url_params["GroupItems"] = True
url_params["Limit"] = 45
url_params["Recursive"] = True
url_params["SortBy"] = "DateCreated"
url_params["SortOrder"] = "Descending"
url_params["Fields"] = "{field_filters}"
url_params["Fields"] = get_default_filters()
if hide_watched:
url_params["IsPlayed"] = False
url_params["IsVirtualUnaired"] = False
url_params["IncludeItemTypes"] = "Episode"
url_params["ImageTypeLimit"] = 1
url_params["Limit"] = 20
url_params["Limit"] = item_limit
elif widget_type == "recent_episodes":
xbmcplugin.setContent(handle, 'episodes')
@@ -339,7 +350,7 @@ def get_widget_content(handle, params):
url_params["IsPlayed"] = False
url_params["IsVirtualUnaired"] = False
url_params["IncludeItemTypes"] = "Episode"
url_params["Limit"] = 20
url_params["Limit"] = item_limit
elif widget_type == "inprogress_episodes":
xbmcplugin.setContent(handle, 'episodes')
@@ -349,18 +360,18 @@ def get_widget_content(handle, params):
url_params["Filters"] = "IsResumable"
url_params["IsVirtualUnaired"] = False
url_params["IncludeItemTypes"] = "Episode"
url_params["Limit"] = 20
url_params["Limit"] = item_limit
elif widget_type == "nextup_episodes":
xbmcplugin.setContent(handle, 'episodes')
url_verb = "{server}/Shows/NextUp"
url_verb = "/Shows/NextUp"
url_params = url_params.copy()
url_params["Limit"] = "{ItemLimit}"
url_params["userid"] = "{userid}"
url_params["Limit"] = item_limit
url_params["userid"] = user_id
url_params["Recursive"] = True
url_params["ImageTypeLimit"] = 1
# Collect InProgress items to be combined with NextUp
inprogress_url_verb = "{server}/Users/{userid}/Items"
inprogress_url_verb = "/Users/{}/Items".format(user_id)
inprogress_url_params = url_params.copy()
inprogress_url_params["Recursive"] = True
inprogress_url_params["SortBy"] = "DatePlayed"
@@ -368,13 +379,13 @@ def get_widget_content(handle, params):
inprogress_url_params["Filters"] = "IsResumable"
inprogress_url_params["IsVirtualUnaired"] = False
inprogress_url_params["IncludeItemTypes"] = "Episode"
inprogress_url_params["Limit"] = 20
inprogress_url_params["Limit"] = item_limit
elif widget_type == "movie_recommendations":
suggested_items_url_params = {}
suggested_items_url_params["userId"] = "{userid}"
suggested_items_url_params["userId"] = user_id
suggested_items_url_params["categoryLimit"] = 15
suggested_items_url_params["ItemLimit"] = 20
suggested_items_url_params["ItemLimit"] = item_limit
suggested_items_url_params["ImageTypeLimit"] = 0
suggested_items_url = get_jellyfin_url(
"{server}/Movies/Recommendations", suggested_items_url_params)
@@ -383,7 +394,7 @@ def get_widget_content(handle, params):
suggested_items = data_manager.get_content(suggested_items_url)
ids = []
set_id = 0
while len(ids) < 20 and suggested_items:
while len(ids) < item_limit and suggested_items:
items = suggested_items[set_id]
log.debug(
"BaselineItemName : {0} - {1}".format(set_id, items.get("BaselineItemName")))

View File

@@ -8,14 +8,13 @@ import xbmc
import xbmcaddon
import xbmcgui
from resources.lib.downloadutils import DownloadUtils
from resources.lib.loghandler import LazyLogger
from resources.lib.play_utils import Service, PlaybackService, send_progress
from resources.lib.kodi_utils import HomeWindow
from resources.lib.widgets import set_background_image, set_random_movies
from resources.lib.websocket_client import WebSocketClient
from resources.lib.menu_functions import set_library_window_values
from resources.lib.server_detect import check_server, check_safe_delete_available, check_connection_speed
from resources.lib.server_detect import check_server, check_connection_speed
from resources.lib.monitors import LibraryChangeMonitor, ContextMonitor
from resources.lib.datamanager import clear_old_cache_data
from resources.lib.tracking import set_timing_enabled
@@ -52,16 +51,6 @@ while not monitor.abortRequested():
check_server()
download_utils = DownloadUtils()
# auth the service
try:
download_utils.authenticate()
download_utils.get_user_id()
except Exception as error:
log.error("Error with initial service auth: {0}".format(error))
image_server = HttpImageServerThread()
image_server.start()
@@ -138,7 +127,8 @@ while home_window.get_property('exit') == 'False':
if user_changed or first_run:
settings = xbmcaddon.Addon()
server_speed_check_data = settings.getSetting("server_speed_check_data")
server_host = download_utils.get_server()
server_speed_check_data = settings.getSetting("server_speed_check_data")
server_host = settings.getSetting('server_address')
if server_host is not None and server_host != "" and server_host != "<none>" and server_host not in server_speed_check_data:
message = "This is the first time you have connected to this server.\nDo you want to run a connection speed test?"
response = xbmcgui.Dialog().yesno("First Connection", message)
@@ -167,10 +157,6 @@ while home_window.get_property('exit') == 'False':
websocket_client = WebSocketClient(library_change_monitor)
websocket_client.start()
if user_changed or not safe_delete_check:
check_safe_delete_available()
safe_delete_check = True
elif screen_saver_active:
last_random_movie_update = time.time() - (random_movie_list_interval - 15)
if background_interval != 0 and ((time.time() - last_background_update) > background_interval):