Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f7816762e | ||
|
|
8bade51eb5 | ||
|
|
7c4398bfb5 | ||
|
|
8f736e8bd3 | ||
|
|
65a9b11dc5 | ||
|
|
7ffd16df4b | ||
|
|
e2628d27dc | ||
|
|
8799c2bb5e | ||
|
|
4ba0b64d2c | ||
|
|
4b2f43e8a2 | ||
|
|
df774ca3c5 | ||
|
|
084fab576e | ||
|
|
1733e64403 | ||
|
|
1d0360c0c3 | ||
|
|
45823ccd96 | ||
|
|
ef3b64cf51 | ||
|
|
a424fb8793 | ||
|
|
b6ae819d32 | ||
|
|
8711ae2452 | ||
|
|
a90c2c2fa8 | ||
|
|
03a89d4f43 | ||
|
|
d48b2bdf2a | ||
|
|
6a6ca8c642 | ||
|
|
d3ffecb866 | ||
|
|
083f91611a | ||
|
|
01e9c45df6 | ||
|
|
b327ebc5bd |
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.jellycon"
|
||||
name="JellyCon"
|
||||
version="0.2.0"
|
||||
provider-name="Team B">
|
||||
version="0.3.0"
|
||||
provider-name="Jellyfin Contributors">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.25.0"/>
|
||||
<import addon="script.module.pil" version="1.1.7"/>
|
||||
@@ -23,7 +23,7 @@
|
||||
<website>https://github.com/jellyfin/jellycon/wiki</website>
|
||||
<source>https://github.com/jellyfin/jellycon</source>
|
||||
<summary lang="en_GB">Browse and play your Jellyfin server media library.</summary>
|
||||
<description lang="en_GB">An addon to allow you to browse and playback your Jellyfin (www.jellyfin.org) Movie, TV Show and Music collections.</description>
|
||||
<description lang="en_GB">An addon to allow you to browse and playback your Jellyfin (https://jellyfin.org) Movie, TV Show and Music collections.</description>
|
||||
<assets>
|
||||
<icon>icon.png</icon>
|
||||
<fanart>fanart.jpg</fanart>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import time
|
||||
import threading
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# coding=utf-8
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import urllib
|
||||
import requests
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
from uuid import uuid4 as uuid4
|
||||
import xbmcaddon
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
from collections import defaultdict
|
||||
import threading
|
||||
import hashlib
|
||||
@@ -46,15 +46,9 @@ class DataManager:
|
||||
# log.debug("DataManager __init__")
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def load_json_data(json_data):
|
||||
return json.loads(json_data, object_hook=lambda d: defaultdict(lambda: None, d))
|
||||
|
||||
@timer
|
||||
def get_content(self, url):
|
||||
json_data = DownloadUtils().download_url(url)
|
||||
result = self.load_json_data(json_data)
|
||||
return result
|
||||
return DownloadUtils().download_url(url)
|
||||
|
||||
@timer
|
||||
def get_items(self, url, gui_options, use_cache=False):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcaddon
|
||||
import xbmcplugin
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcgui
|
||||
import xbmcaddon
|
||||
@@ -15,6 +15,7 @@ import urllib
|
||||
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 .clientinfo import ClientInformation
|
||||
@@ -336,7 +337,6 @@ class DownloadUtils:
|
||||
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")
|
||||
play_info_result = json.loads(play_info_result)
|
||||
log.debug("PlaybackInfo : {0}".format(play_info_result))
|
||||
|
||||
return play_info_result
|
||||
@@ -392,25 +392,25 @@ class DownloadUtils:
|
||||
|
||||
# for episodes always use the parent BG
|
||||
if item_type == "Episode" and art_type == "Backdrop":
|
||||
item_id = data["ParentBackdropItemId"]
|
||||
bg_item_tags = data["ParentBackdropImageTags"]
|
||||
if bg_item_tags is not None and len(bg_item_tags) > 0:
|
||||
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["ParentBackdropItemId"]
|
||||
bg_item_tags = data["ParentBackdropImageTags"]
|
||||
if bg_item_tags is not None and len(bg_item_tags) > 0:
|
||||
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["BackdropImageTags"]
|
||||
if bg_tags is not None and len(bg_tags) > index:
|
||||
bg_tags = data.get("BackdropImageTags", [])
|
||||
if bg_tags:
|
||||
image_tag = bg_tags[index]
|
||||
# log.debug("Background Image Tag: {0}", imageTag)
|
||||
elif parent is False:
|
||||
image_tags = data["ImageTags"]
|
||||
if image_tags is not None:
|
||||
image_tag_type = image_tags[art_type]
|
||||
if image_tag_type is not None:
|
||||
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
|
||||
# log.debug("Image Tag: {0}", imageTag)
|
||||
elif parent is True:
|
||||
@@ -420,8 +420,8 @@ class DownloadUtils:
|
||||
else:
|
||||
tag_name = 'Parent%sImageTag' % art_type
|
||||
id_name = 'Parent%sItemId' % art_type
|
||||
parent_image_id = data[id_name]
|
||||
parent_image_tag = data[tag_name]
|
||||
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
|
||||
@@ -485,7 +485,7 @@ class DownloadUtils:
|
||||
userid = window.get_property("userid")
|
||||
user_image = window.get_property("userimage")
|
||||
|
||||
if userid and user_image:
|
||||
if userid:
|
||||
log.debug("JellyCon DownloadUtils -> Returning saved UserID: {0}".format(userid))
|
||||
return userid
|
||||
|
||||
@@ -498,20 +498,14 @@ class DownloadUtils:
|
||||
log.debug("Looking for user name: {0}".format(user_name))
|
||||
|
||||
try:
|
||||
json_data = self.download_url("{server}/Users/Public?format=json", suppress=True, authenticate=False)
|
||||
result = self.download_url("{server}/Users/Public?format=json", suppress=True, authenticate=False)
|
||||
except Exception as msg:
|
||||
log.error("Get User unable to connect: {0}".format(msg))
|
||||
return ""
|
||||
|
||||
log.debug("GETUSER_JSONDATA_01: {0}".format(json_data))
|
||||
log.debug("GETUSER_JSONDATA_01: {0}".format(py2_decode(result)))
|
||||
|
||||
try:
|
||||
result = json.loads(json_data)
|
||||
except Exception as e:
|
||||
log.debug("Could not load user data: {0}".format(e))
|
||||
return ""
|
||||
|
||||
if result is None:
|
||||
if not result:
|
||||
return ""
|
||||
|
||||
log.debug("GETUSER_JSONDATA_02: {0}".format(result))
|
||||
@@ -570,20 +564,15 @@ class DownloadUtils:
|
||||
user_name = urllib.quote(user_details.get("username", ""))
|
||||
pwd_text = urllib.quote(user_details.get("password", ""))
|
||||
|
||||
message_data = "username=" + user_name + "&pw=" + pwd_text
|
||||
message_data = {'username': user_name, 'pw': pwd_text}
|
||||
|
||||
resp = self.download_url(url, post_body=message_data, method="POST", suppress=True, authenticate=False)
|
||||
log.debug("AuthenticateByName: {0}".format(resp))
|
||||
result = self.download_url(url, post_body=message_data, method="POST", suppress=True, authenticate=False)
|
||||
log.debug("AuthenticateByName: {0}".format(result))
|
||||
|
||||
access_token = None
|
||||
userid = None
|
||||
try:
|
||||
result = json.loads(resp)
|
||||
access_token = result.get("AccessToken")
|
||||
# userid = result["SessionInfo"].get("UserId")
|
||||
userid = result["User"].get("Id")
|
||||
except:
|
||||
pass
|
||||
access_token = result.get("AccessToken")
|
||||
userid = result["User"].get("Id")
|
||||
|
||||
if access_token is not None:
|
||||
log.debug("User Authenticated: {0}".format(access_token))
|
||||
@@ -649,7 +638,7 @@ class DownloadUtils:
|
||||
http_timeout = int(settings.getSetting("http_timeout"))
|
||||
|
||||
if authenticate and username == "":
|
||||
return "null"
|
||||
return {}
|
||||
|
||||
if settings.getSetting("suppressErrors") == "true":
|
||||
suppress = True
|
||||
@@ -659,13 +648,13 @@ class DownloadUtils:
|
||||
if url.find("{server}") != -1:
|
||||
server = self.get_server()
|
||||
if server is None:
|
||||
return "null"
|
||||
return {}
|
||||
url = url.replace("{server}", server)
|
||||
|
||||
if url.find("{userid}") != -1:
|
||||
userid = self.get_user_id()
|
||||
if not userid:
|
||||
return "null"
|
||||
return {}
|
||||
url = url.replace("{userid}", userid)
|
||||
|
||||
if url.find("{ItemLimit}") != -1:
|
||||
@@ -680,7 +669,7 @@ class DownloadUtils:
|
||||
home_window = HomeWindow()
|
||||
random_movies = home_window.get_property("random-movies")
|
||||
if not random_movies:
|
||||
return "null"
|
||||
return {}
|
||||
url = url.replace("{random_movies}", random_movies)
|
||||
|
||||
log.debug("After: {0}".format(url))
|
||||
@@ -735,12 +724,16 @@ class DownloadUtils:
|
||||
settings.setSetting("saved_user_password_" + hashed_username, "")
|
||||
save_user_details(settings, "", "")
|
||||
|
||||
log.error("HTTP response error: {0} {1}".format(data.status_code, data.content))
|
||||
log.error("HTTP response error for {0}: {1} {2}".format(url, data.status_code, data.content))
|
||||
if suppress is False:
|
||||
xbmcgui.Dialog().notification(string_load(30316),
|
||||
string_load(30200) % str(data.content),
|
||||
icon="special://home/addons/plugin.video.jellycon/icon.png")
|
||||
return data.content or "null"
|
||||
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))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import urllib
|
||||
import sys
|
||||
@@ -280,8 +281,7 @@ def unmark_item_favorite(item_id):
|
||||
|
||||
def delete(item_id):
|
||||
|
||||
json_data = downloadUtils.download_url("{server}/Users/{userid}/Items/" + item_id + "?format=json")
|
||||
item = json.loads(json_data)
|
||||
item = downloadUtils.download_url("{server}/Users/{userid}/Items/" + item_id + "?format=json")
|
||||
|
||||
item_id = item.get("Id")
|
||||
item_name = item.get("Name", "")
|
||||
@@ -577,8 +577,7 @@ def show_menu(params):
|
||||
|
||||
elif selected_action == "safe_delete":
|
||||
url = "{server}/jellyfin_safe_delete/delete_item/" + item_id
|
||||
delete_action = downloadUtils.download_url(url)
|
||||
result = json.loads(delete_action)
|
||||
result = downloadUtils.download_url(url)
|
||||
dialog = xbmcgui.Dialog()
|
||||
if result:
|
||||
log.debug("Safe_Delete_Action: {0}".format(result))
|
||||
@@ -621,11 +620,10 @@ def show_menu(params):
|
||||
}
|
||||
delete_action = downloadUtils.download_url(url, method="POST", post_body=playback_info)
|
||||
log.debug("Delete result action: {0}".format(delete_action))
|
||||
delete_action_result = json.loads(delete_action)
|
||||
if not delete_action_result:
|
||||
if not delete_action:
|
||||
dialog.ok("Error", "Error deleting files", "Error in responce from server")
|
||||
elif not delete_action_result["result"]:
|
||||
dialog.ok("Error", "Error deleting files", delete_action_result["message"])
|
||||
elif not delete_action.get("result"):
|
||||
dialog.ok("Error", "Error deleting files", delete_action["message"])
|
||||
else:
|
||||
dialog.ok("Deleted", "Files deleted")
|
||||
else:
|
||||
@@ -689,8 +687,7 @@ def populate_listitem(item_id):
|
||||
log.debug("populate_listitem: {0}".format(item_id))
|
||||
|
||||
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
|
||||
json_data = downloadUtils.download_url(url)
|
||||
result = json.loads(json_data)
|
||||
result = downloadUtils.download_url(url)
|
||||
log.debug("populate_listitem item info: {0}".format(result))
|
||||
|
||||
item_title = result.get("Name", string_load(30280))
|
||||
@@ -941,7 +938,14 @@ def play_action(params):
|
||||
log.debug("PLAY ACTION PARAMS: {0}".format(params))
|
||||
item_id = params.get("item_id")
|
||||
|
||||
auto_resume = int(params.get("auto_resume", "-1"))
|
||||
auto_resume = params.get("auto_resume", "-1")
|
||||
if auto_resume == 'None':
|
||||
auto_resume = '-1'
|
||||
if auto_resume:
|
||||
auto_resume = int(auto_resume)
|
||||
else:
|
||||
auto_resume = -1
|
||||
|
||||
log.debug("AUTO_RESUME: {0}".format(auto_resume))
|
||||
|
||||
force_transcode = params.get("force_transcode", None) is not None
|
||||
@@ -981,8 +985,7 @@ def play_item_trailer(item_id):
|
||||
|
||||
url = ("{server}/Users/{userid}/Items/%s/LocalTrailers?format=json" % item_id)
|
||||
|
||||
json_data = downloadUtils.download_url(url)
|
||||
result = json.loads(json_data)
|
||||
result = downloadUtils.download_url(url)
|
||||
|
||||
if result is None:
|
||||
return
|
||||
@@ -1006,8 +1009,7 @@ def play_item_trailer(item_id):
|
||||
trailer_list.append(info)
|
||||
|
||||
url = ("{server}/Users/{userid}/Items/%s?format=json&Fields=RemoteTrailers" % item_id)
|
||||
json_data = downloadUtils.download_url(url)
|
||||
result = json.loads(json_data)
|
||||
result = downloadUtils.download_url(url)
|
||||
log.debug("RemoteTrailers: {0}".format(result))
|
||||
count = 1
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcvfs
|
||||
import xbmc
|
||||
import base64
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
import os
|
||||
@@ -100,27 +101,27 @@ def extract_item_info(item, gui_options):
|
||||
|
||||
item_details = ItemDetails()
|
||||
|
||||
item_details.id = item["Id"]
|
||||
item_details.etag = item["Etag"]
|
||||
item_details.is_folder = item["IsFolder"]
|
||||
item_details.item_type = item["Type"]
|
||||
item_details.location_type = item["LocationType"]
|
||||
item_details.name = item["Name"]
|
||||
item_details.sort_name = item["SortName"]
|
||||
item_details.id = item.get("Id")
|
||||
item_details.etag = item.get("Etag")
|
||||
item_details.is_folder = item.get("IsFolder")
|
||||
item_details.item_type = item.get("Type")
|
||||
item_details.location_type = item.get("LocationType")
|
||||
item_details.name = item.get("Name")
|
||||
item_details.sort_name = item.get("SortName")
|
||||
item_details.original_title = item_details.name
|
||||
|
||||
if item_details.item_type == "Episode":
|
||||
item_details.episode_number = item["IndexNumber"]
|
||||
item_details.season_number = item["ParentIndexNumber"]
|
||||
item_details.series_id = item["SeriesId"]
|
||||
item_details.episode_number = item.get("IndexNumber")
|
||||
item_details.season_number = item.get("ParentIndexNumber")
|
||||
item_details.series_id = item.get("SeriesId")
|
||||
|
||||
if item_details.season_number != 0:
|
||||
item_details.season_sort_number = item_details.season_number
|
||||
item_details.episode_sort_number = item_details.episode_number
|
||||
else:
|
||||
special_after_season = item["AirsAfterSeasonNumber"]
|
||||
special_before_season = item["AirsBeforeSeasonNumber"]
|
||||
special_before_episode = item["AirsBeforeEpisodeNumber"]
|
||||
special_after_season = item.get("AirsAfterSeasonNumber")
|
||||
special_before_season = item.get("AirsBeforeSeasonNumber")
|
||||
special_before_episode = item.get("AirsBeforeEpisodeNumber")
|
||||
|
||||
if special_after_season:
|
||||
item_details.season_sort_number = special_after_season + 1
|
||||
@@ -131,21 +132,21 @@ def extract_item_info(item, gui_options):
|
||||
item_details.episode_sort_number = special_before_episode - 1
|
||||
|
||||
elif item_details.item_type == "Season":
|
||||
item_details.season_number = item["IndexNumber"]
|
||||
item_details.series_id = item["SeriesId"]
|
||||
item_details.season_number = item.get("IndexNumber")
|
||||
item_details.series_id = item.get("SeriesId")
|
||||
|
||||
elif item_details.item_type == "Series":
|
||||
item_details.status = item["Status"]
|
||||
item_details.status = item.get("Status")
|
||||
|
||||
elif item_details.item_type == "Audio":
|
||||
item_details.track_number = item["IndexNumber"]
|
||||
item_details.album_name = item["Album"]
|
||||
artists = item["Artists"]
|
||||
if artists is not None and len(artists) > 0:
|
||||
item_details.track_number = item.get("IndexNumber")
|
||||
item_details.album_name = item.get("Album")
|
||||
artists = item.get("Artists", [])
|
||||
if artists:
|
||||
item_details.song_artist = artists[0] # get first artist
|
||||
|
||||
elif item_details.item_type == "MusicAlbum":
|
||||
item_details.album_artist = item["AlbumArtist"]
|
||||
item_details.album_artist = item.get("AlbumArtist")
|
||||
item_details.album_name = item_details.name
|
||||
|
||||
if item_details.season_number is None:
|
||||
@@ -153,23 +154,23 @@ def extract_item_info(item, gui_options):
|
||||
if item_details.episode_number is None:
|
||||
item_details.episode_number = 0
|
||||
|
||||
if item["Taglines"] is not None and len(item["Taglines"]) > 0:
|
||||
item_details.tagline = item["Taglines"][0]
|
||||
if item.get("Taglines", []):
|
||||
item_details.tagline = item.get("Taglines")[0]
|
||||
|
||||
item_details.tags = []
|
||||
if item["TagItems"] is not None and len(item["TagItems"]) > 0:
|
||||
for tag_info in item["TagItems"]:
|
||||
item_details.tags.append(tag_info["Name"])
|
||||
if item.get("TagItems", []):
|
||||
for tag_info in item.get("TagItems"):
|
||||
item_details.tags.append(tag_info.get("Name"))
|
||||
|
||||
# set the item name
|
||||
# override with name format string from request
|
||||
name_format = gui_options["name_format"]
|
||||
name_format_type = gui_options["name_format_type"]
|
||||
name_format = gui_options.get("name_format")
|
||||
name_format_type = gui_options.get("name_format_type")
|
||||
|
||||
if name_format is not None and item_details.item_type == name_format_type:
|
||||
name_info = {}
|
||||
name_info["ItemName"] = item["Name"]
|
||||
season_name = item["SeriesName"]
|
||||
name_info["ItemName"] = item.get("Name")
|
||||
season_name = item.get("SeriesName")
|
||||
if season_name:
|
||||
name_info["SeriesName"] = season_name
|
||||
else:
|
||||
@@ -179,8 +180,8 @@ def extract_item_info(item, gui_options):
|
||||
log.debug("FormatName: {0} | {1}".format(name_format, name_info))
|
||||
item_details.name = unicode(name_format).format(**name_info).strip()
|
||||
|
||||
year = item["ProductionYear"]
|
||||
prem_date = item["PremiereDate"]
|
||||
year = item.get("ProductionYear")
|
||||
prem_date = item.get("PremiereDate")
|
||||
|
||||
if year is not None:
|
||||
item_details.year = year
|
||||
@@ -191,35 +192,35 @@ def extract_item_info(item, gui_options):
|
||||
tokens = prem_date.split("T")
|
||||
item_details.premiere_date = tokens[0]
|
||||
|
||||
create_date = item["DateCreated"]
|
||||
if create_date is not None:
|
||||
create_date = item.get("DateCreated")
|
||||
if create_date:
|
||||
item_details.date_added = create_date.split('.')[0].replace('T', " ")
|
||||
|
||||
# add the premiered date for Upcoming TV
|
||||
if item_details.location_type == "Virtual":
|
||||
airtime = item["AirTime"]
|
||||
airtime = item.get("AirTime")
|
||||
item_details.name = item_details.name + ' - ' + item_details.premiere_date + ' - ' + str(airtime)
|
||||
|
||||
if item_details.item_type == "Program":
|
||||
item_details.program_channel_name = item["ChannelName"]
|
||||
item_details.program_start_date = item["StartDate"]
|
||||
item_details.program_end_date = item["EndDate"]
|
||||
item_details.program_channel_name = item.get("ChannelName")
|
||||
item_details.program_start_date = item.get("StartDate")
|
||||
item_details.program_end_date = item.get("EndDate")
|
||||
|
||||
# Process MediaStreams
|
||||
media_streams = item["MediaStreams"]
|
||||
if media_streams is not None:
|
||||
media_streams = item.get("MediaStreams", [])
|
||||
if media_streams:
|
||||
media_info_list = []
|
||||
for mediaStream in media_streams:
|
||||
stream_type = mediaStream["Type"]
|
||||
stream_type = mediaStream.get("Type")
|
||||
if stream_type == "Video":
|
||||
media_info = {}
|
||||
media_info["type"] = "video"
|
||||
media_info["codec"] = mediaStream["Codec"]
|
||||
media_info["height"] = mediaStream["Height"]
|
||||
media_info["width"] = mediaStream["Width"]
|
||||
aspect_ratio = mediaStream["AspectRatio"]
|
||||
media_info["codec"] = mediaStream.get("Codec")
|
||||
media_info["height"] = mediaStream.get("Height")
|
||||
media_info["width"] = mediaStream.get("Width")
|
||||
aspect_ratio = mediaStream.get("AspectRatio")
|
||||
media_info["apect"] = aspect_ratio
|
||||
if aspect_ratio is not None and len(aspect_ratio) >= 3:
|
||||
if aspect_ratio and len(aspect_ratio) >= 3:
|
||||
try:
|
||||
aspect_width, aspect_height = aspect_ratio.split(':')
|
||||
media_info["apect_ratio"] = float(aspect_width) / float(aspect_height)
|
||||
@@ -231,36 +232,36 @@ def extract_item_info(item, gui_options):
|
||||
if stream_type == "Audio":
|
||||
media_info = {}
|
||||
media_info["type"] = "audio"
|
||||
media_info["codec"] = mediaStream["Codec"]
|
||||
media_info["channels"] = mediaStream["Channels"]
|
||||
media_info["language"] = mediaStream["Language"]
|
||||
media_info["codec"] = mediaStream.get("Codec")
|
||||
media_info["channels"] = mediaStream.get("Channels")
|
||||
media_info["language"] = mediaStream.get("Language")
|
||||
media_info_list.append(media_info)
|
||||
if stream_type == "Subtitle":
|
||||
item_details.subtitle_available = True
|
||||
media_info = {}
|
||||
media_info["type"] = "sub"
|
||||
media_info["language"] = mediaStream["Language"]
|
||||
media_info["language"] = mediaStream.get("Language", '')
|
||||
media_info_list.append(media_info)
|
||||
|
||||
item_details.media_streams = media_info_list
|
||||
|
||||
# Process People
|
||||
people = item["People"]
|
||||
people = item.get("People", [])
|
||||
if people is not None:
|
||||
cast = []
|
||||
for person in people:
|
||||
person_type = person["Type"]
|
||||
person_type = person.get("Type")
|
||||
if person_type == "Director":
|
||||
item_details.director = item_details.director + person["Name"] + ' '
|
||||
item_details.director = item_details.director + person.get("Name") + ' '
|
||||
elif person_type == "Writing":
|
||||
item_details.writer = person["Name"]
|
||||
elif person_type == "Actor":
|
||||
# log.debug("Person: {0}", person)
|
||||
person_name = person["Name"]
|
||||
person_role = person["Role"]
|
||||
person_id = person["Id"]
|
||||
person_tag = person["PrimaryImageTag"]
|
||||
if person_tag is not None:
|
||||
person_name = person.get("Name")
|
||||
person_role = person.get("Role")
|
||||
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,
|
||||
@@ -272,64 +273,62 @@ def extract_item_info(item, gui_options):
|
||||
item_details.cast = cast
|
||||
|
||||
# Process Studios
|
||||
studios = item["Studios"]
|
||||
studios = item.get("Studios", [])
|
||||
if studios is not None:
|
||||
for studio in studios:
|
||||
if item_details.studio is None: # Just take the first one
|
||||
studio_name = studio["Name"]
|
||||
studio_name = studio.get("Name")
|
||||
item_details.studio = studio_name
|
||||
break
|
||||
|
||||
# production location
|
||||
prod_location = item["ProductionLocations"]
|
||||
prod_location = item.get("ProductionLocations", [])
|
||||
# log.debug("ProductionLocations : {0}", prod_location)
|
||||
if prod_location and len(prod_location) > 0:
|
||||
if prod_location:
|
||||
item_details.production_location = prod_location[0]
|
||||
|
||||
# Process Genres
|
||||
genres = item["Genres"]
|
||||
if genres is not None and len(genres) > 0:
|
||||
genres = item.get("Genres", [])
|
||||
if genres:
|
||||
item_details.genres = genres
|
||||
|
||||
# Process UserData
|
||||
user_data = item["UserData"]
|
||||
if user_data is None:
|
||||
user_data = defaultdict(lambda: None, {})
|
||||
user_data = item.get("UserData", {})
|
||||
|
||||
if user_data["Played"] is True:
|
||||
if user_data.get("Played"):
|
||||
item_details.overlay = "6"
|
||||
item_details.play_count = 1
|
||||
else:
|
||||
item_details.overlay = "7"
|
||||
item_details.play_count = 0
|
||||
|
||||
if user_data["IsFavorite"] is True:
|
||||
if user_data.get("IsFavorite"):
|
||||
item_details.overlay = "5"
|
||||
item_details.favorite = "true"
|
||||
else:
|
||||
item_details.favorite = "false"
|
||||
|
||||
reasonable_ticks = user_data["PlaybackPositionTicks"]
|
||||
if reasonable_ticks is not None:
|
||||
reasonable_ticks = user_data.get("PlaybackPositionTicks", 0)
|
||||
if reasonable_ticks:
|
||||
reasonable_ticks = int(reasonable_ticks) / 1000
|
||||
item_details.resume_time = int(reasonable_ticks / 10000)
|
||||
|
||||
item_details.series_name = item["SeriesName"]
|
||||
item_details.plot = item["Overview"]
|
||||
item_details.series_name = item.get("SeriesName", '')
|
||||
item_details.plot = item.get("Overview", '')
|
||||
|
||||
runtime = item["RunTimeTicks"]
|
||||
if item_details.is_folder is False and runtime is not None:
|
||||
runtime = item.get("RunTimeTicks")
|
||||
if item_details.is_folder is False and runtime:
|
||||
item_details.duration = long(runtime) / 10000000
|
||||
|
||||
child_count = item["ChildCount"]
|
||||
if child_count is not None:
|
||||
child_count = item.get("ChildCount")
|
||||
if child_count:
|
||||
item_details.total_seasons = child_count
|
||||
|
||||
recursive_item_count = item["RecursiveItemCount"]
|
||||
if recursive_item_count is not None:
|
||||
recursive_item_count = item.get("RecursiveItemCount")
|
||||
if recursive_item_count:
|
||||
item_details.total_episodes = recursive_item_count
|
||||
|
||||
unplayed_item_count = user_data["UnplayedItemCount"]
|
||||
unplayed_item_count = user_data.get("UnplayedItemCount")
|
||||
if unplayed_item_count is not None:
|
||||
item_details.unwatched_episodes = unplayed_item_count
|
||||
item_details.watched_episodes = item_details.total_episodes - unplayed_item_count
|
||||
@@ -337,20 +336,20 @@ def extract_item_info(item, gui_options):
|
||||
item_details.number_episodes = item_details.total_episodes
|
||||
|
||||
item_details.art = get_art(item, gui_options["server"])
|
||||
item_details.rating = item["OfficialRating"]
|
||||
item_details.mpaa = item["OfficialRating"]
|
||||
item_details.rating = item.get("OfficialRating")
|
||||
item_details.mpaa = item.get("OfficialRating")
|
||||
|
||||
item_details.community_rating = item["CommunityRating"]
|
||||
if item_details.community_rating is None:
|
||||
item_details.community_rating = item.get("CommunityRating")
|
||||
if not item_details.community_rating:
|
||||
item_details.community_rating = 0.0
|
||||
|
||||
item_details.critic_rating = item["CriticRating"]
|
||||
if item_details.critic_rating is None:
|
||||
item_details.critic_rating = item.get("CriticRating")
|
||||
if not item_details.critic_rating:
|
||||
item_details.critic_rating = 0.0
|
||||
|
||||
item_details.location_type = item["LocationType"]
|
||||
item_details.recursive_item_count = item["RecursiveItemCount"]
|
||||
item_details.recursive_unplayed_items_count = user_data["UnplayedItemCount"]
|
||||
item_details.location_type = item.get("LocationType")
|
||||
item_details.recursive_item_count = item.get("RecursiveItemCount")
|
||||
item_details.recursive_unplayed_items_count = user_data.get("UnplayedItemCount")
|
||||
|
||||
item_details.mode = "GET_CONTENT"
|
||||
|
||||
@@ -497,7 +496,7 @@ def add_gui_item(url, item_details, display_options, folder=True, default_sort=F
|
||||
info_labels["rating"] = item_details.rating
|
||||
info_labels["year"] = item_details.year
|
||||
|
||||
if item_details.genres is not None and len(item_details.genres) > 0:
|
||||
if item_details.genres:
|
||||
genres_list = []
|
||||
for genre in item_details.genres:
|
||||
genres_list.append(urllib.quote(genre.encode('utf8')))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import xbmc
|
||||
|
||||
@@ -9,7 +11,7 @@ class JsonRpc(object):
|
||||
params = None
|
||||
|
||||
def __init__(self, method, **kwargs):
|
||||
|
||||
|
||||
self.method = method
|
||||
|
||||
for arg in kwargs: # id_(int), jsonrpc(str)
|
||||
@@ -18,7 +20,7 @@ class JsonRpc(object):
|
||||
def _query(self):
|
||||
|
||||
query = {
|
||||
|
||||
|
||||
'jsonrpc': self.jsonrpc,
|
||||
'id': self.id_,
|
||||
'method': self.method,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@ from six import ensure_text
|
||||
from kodi_six import xbmc, xbmcaddon
|
||||
from urlparse import urlparse
|
||||
|
||||
#from .utils import get_filesystem_encoding
|
||||
|
||||
#from . import settings
|
||||
|
||||
##################################################################################################
|
||||
|
||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellycon')
|
||||
@@ -41,37 +37,20 @@ class LogHandler(logging.StreamHandler):
|
||||
self.sensitive = {'Token': [], 'Server': []}
|
||||
|
||||
settings = xbmcaddon.Addon()
|
||||
server = settings.getSetting('server_address')
|
||||
if server:
|
||||
url_bits = urlparse(server)
|
||||
server = url_bits.netloc
|
||||
self.sensitive['Server'].append(server)
|
||||
#for server in database.get_credentials()['Servers']:
|
||||
|
||||
# if server.get('AccessToken'):
|
||||
# self.sensitive['Token'].append(server['AccessToken'])
|
||||
|
||||
# if server.get('address'):
|
||||
# self.sensitive['Server'].append(server['address'].split('://')[1])
|
||||
|
||||
#self.mask_info = settings('maskInfo.bool')
|
||||
self.server = settings.getSetting('server_address')
|
||||
self.debug = settings.getSetting('log_debug')
|
||||
|
||||
def emit(self, record):
|
||||
|
||||
if self._get_log_level(record.levelno):
|
||||
string = self.format(record)
|
||||
|
||||
#if self.mask_info:
|
||||
for server in self.sensitive['Server']:
|
||||
string = string.replace(server or "{server}", "{jellyfin-server}")
|
||||
|
||||
# for token in self.sensitive['Token']:
|
||||
# string = string.replace(token or "{token}", "{jellyfin-token}")
|
||||
# Hide server URL in logs
|
||||
string = string.replace(self.server or "{server}", "{jellyfin-server}")
|
||||
|
||||
xbmc.log(string, level=xbmc.LOGNOTICE)
|
||||
|
||||
@classmethod
|
||||
def _get_log_level(cls, level):
|
||||
def _get_log_level(self, level):
|
||||
|
||||
levels = {
|
||||
logging.ERROR: 0,
|
||||
@@ -79,12 +58,10 @@ class LogHandler(logging.StreamHandler):
|
||||
logging.INFO: 1,
|
||||
logging.DEBUG: 2
|
||||
}
|
||||
### TODO
|
||||
log_level = 2
|
||||
#try:
|
||||
# log_level = int(settings('logLevel'))
|
||||
#except ValueError:
|
||||
# log_level = 2 # If getting settings fail, we probably want debug logging.
|
||||
if self.debug == 'true':
|
||||
log_level = 2
|
||||
else:
|
||||
log_level = 1
|
||||
|
||||
return log_level >= levels[level]
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# coding=utf-8
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
import json
|
||||
import urllib
|
||||
import base64
|
||||
import string
|
||||
|
||||
import xbmcplugin
|
||||
import xbmcaddon
|
||||
@@ -360,22 +361,12 @@ def show_movie_alpha_list(menu_params):
|
||||
if parent_id is not None:
|
||||
url_params["ParentId"] = parent_id
|
||||
|
||||
prefix_url = get_jellyfin_url("{server}/Items/Prefixes", url_params)
|
||||
|
||||
data_manager = DataManager()
|
||||
result = data_manager.get_content(prefix_url)
|
||||
|
||||
if not result:
|
||||
return
|
||||
|
||||
alpha_list = []
|
||||
for prefix in result:
|
||||
alpha_list.append(prefix.get("Name"))
|
||||
prefixes = '#' + string.ascii_uppercase
|
||||
|
||||
collections = []
|
||||
for alphaName in alpha_list:
|
||||
for alpha_name in prefixes:
|
||||
item_data = {}
|
||||
item_data['title'] = alphaName
|
||||
item_data['title'] = alpha_name
|
||||
item_data['media_type'] = "Movies"
|
||||
|
||||
params = {}
|
||||
@@ -391,10 +382,10 @@ def show_movie_alpha_list(menu_params):
|
||||
if parent_id is not None:
|
||||
params["ParentId"] = parent_id
|
||||
|
||||
if alphaName == "#":
|
||||
if alpha_name == "#":
|
||||
params["NameLessThan"] = "A"
|
||||
else:
|
||||
params["NameStartsWith"] = alphaName
|
||||
params["NameStartsWith"] = alpha_name
|
||||
|
||||
url = get_jellyfin_url("{server}/Users/{userid}/Items", params)
|
||||
item_data['path'] = url
|
||||
@@ -430,20 +421,11 @@ def show_tvshow_alpha_list(menu_params):
|
||||
url_params["SortOrder"] = "Ascending"
|
||||
if parent_id is not None:
|
||||
menu_params["ParentId"] = parent_id
|
||||
prefix_url = get_jellyfin_url("{server}/Items/Prefixes", url_params)
|
||||
|
||||
data_manager = DataManager()
|
||||
result = data_manager.get_content(prefix_url)
|
||||
|
||||
if not result:
|
||||
return
|
||||
|
||||
alpha_list = []
|
||||
for prefix in result:
|
||||
alpha_list.append(prefix.get("Name"))
|
||||
prefixes = '#' + string.ascii_uppercase
|
||||
|
||||
collections = []
|
||||
for alpha_name in alpha_list:
|
||||
for alpha_name in prefixes:
|
||||
item_data = {}
|
||||
item_data['title'] = alpha_name
|
||||
item_data['media_type'] = "tvshows"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
|
||||
@@ -1086,9 +1086,10 @@ def stop_all_playback(played_information):
|
||||
if data.get("play_action_type", "") == "play":
|
||||
prompt_for_stop_actions(jellyfin_item_id, data)
|
||||
|
||||
device_id = ClientInformation().get_device_id()
|
||||
url = "{server}/Videos/ActiveEncodings?DeviceId=%s" % device_id
|
||||
download_utils.download_url(url, method="DELETE")
|
||||
if data.get('playback_type') == 'Transcode':
|
||||
device_id = ClientInformation().get_device_id()
|
||||
url = "{server}/Videos/ActiveEncodings?DeviceId=%s" % device_id
|
||||
download_utils.download_url(url, method="DELETE")
|
||||
|
||||
|
||||
def get_playing_data(play_data_map):
|
||||
@@ -1202,8 +1203,6 @@ class PlaybackService(xbmc.Monitor):
|
||||
self.monitor = monitor
|
||||
|
||||
def onNotification(self, sender, method, data):
|
||||
log.debug("PlaybackService:onNotification:{0}:{1}:{2}".format(sender, method, data))
|
||||
|
||||
if method == 'GUI.OnScreensaverActivated':
|
||||
self.screensaver_activated()
|
||||
return
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcgui
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import socket
|
||||
import json
|
||||
@@ -12,6 +13,7 @@ from datetime import datetime
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmc
|
||||
from kodi_six.utils import py2_decode
|
||||
|
||||
from .kodi_utils import HomeWindow
|
||||
from .downloadutils import DownloadUtils, save_user_details, load_user_details
|
||||
@@ -98,9 +100,8 @@ def check_safe_delete_available():
|
||||
log.debug("check_safe_delete_available")
|
||||
|
||||
du = DownloadUtils()
|
||||
json_data = du.download_url("{server}/Plugins")
|
||||
result = json.loads(json_data)
|
||||
if result is not None:
|
||||
result = du.download_url("{server}/Plugins")
|
||||
if result:
|
||||
log.debug("Server Plugin List: {0}".format(result))
|
||||
|
||||
safe_delete_found = False
|
||||
@@ -226,17 +227,16 @@ def check_server(force=False, change_user=False, notify=False):
|
||||
xbmc.executebuiltin("ActivateWindow(Home)")
|
||||
return
|
||||
|
||||
public_lookup_url = "%s/Users/Public?format=json" % (server_url)
|
||||
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(__addon_name__ + " : " + string_load(30376))
|
||||
progress.update(0, string_load(30377))
|
||||
json_data = du.download_url(public_lookup_url, authenticate=False)
|
||||
result = du.download_url(public_lookup_url, authenticate=False)
|
||||
progress.close()
|
||||
|
||||
result = json.loads(json_data)
|
||||
if result is not None:
|
||||
if result:
|
||||
xbmcgui.Dialog().ok(__addon_name__ + " : " + string_load(30167),
|
||||
server_url)
|
||||
break
|
||||
@@ -266,168 +266,158 @@ def check_server(force=False, change_user=False, notify=False):
|
||||
|
||||
# get a list of users
|
||||
log.debug("Getting user list")
|
||||
json_data = du.download_url(server_url + "/Users/Public?format=json", authenticate=False)
|
||||
result = du.download_url(server_url + "/Users/Public?format=json", authenticate=False)
|
||||
|
||||
log.debug("jsonData: {0}".format(json_data))
|
||||
try:
|
||||
result = json.loads(json_data)
|
||||
except:
|
||||
result = None
|
||||
log.debug("jsonData: {0}".format(py2_decode(result)))
|
||||
|
||||
if result is None:
|
||||
xbmcgui.Dialog().ok(string_load(30135),
|
||||
string_load(30201),
|
||||
string_load(30169) + server_url)
|
||||
selected_id = -1
|
||||
users = []
|
||||
for user in result:
|
||||
config = user.get("Configuration")
|
||||
if config is not None:
|
||||
if config.get("IsHidden", False) is False:
|
||||
name = user.get("Name")
|
||||
admin = user.get("Policy", {}).get("IsAdministrator", False)
|
||||
|
||||
time_ago = ""
|
||||
last_active = user.get("LastActivityDate")
|
||||
if last_active:
|
||||
last_active_date = datetime_from_string(last_active)
|
||||
log.debug("LastActivityDate: {0}".format(last_active_date))
|
||||
ago = datetime.now() - last_active_date
|
||||
log.debug("LastActivityDate: {0}".format(ago))
|
||||
days = divmod(ago.seconds, 86400)
|
||||
hours = divmod(days[1], 3600)
|
||||
minutes = divmod(hours[1], 60)
|
||||
log.debug("LastActivityDate: {0} {1} {2}".format(days[0], hours[0], minutes[0]))
|
||||
if days[0]:
|
||||
time_ago += " %sd" % days[0]
|
||||
if hours[0]:
|
||||
time_ago += " %sh" % hours[0]
|
||||
if minutes[0]:
|
||||
time_ago += " %sm" % minutes[0]
|
||||
time_ago = time_ago.strip()
|
||||
if not time_ago:
|
||||
time_ago = "Active: now"
|
||||
else:
|
||||
time_ago = "Active: %s ago" % time_ago
|
||||
log.debug("LastActivityDate: {0}".format(time_ago))
|
||||
|
||||
user_item = xbmcgui.ListItem(name)
|
||||
user_image = du.get_user_artwork(user, 'Primary')
|
||||
if not user_image:
|
||||
user_image = "DefaultUser.png"
|
||||
art = {"Thumb": user_image}
|
||||
user_item.setArt(art)
|
||||
user_item.setLabel2("TEST")
|
||||
|
||||
sub_line = time_ago
|
||||
|
||||
if user.get("HasPassword", False) is True:
|
||||
sub_line += ", Password"
|
||||
user_item.setProperty("secure", "true")
|
||||
|
||||
m = hashlib.md5()
|
||||
m.update(name)
|
||||
hashed_username = m.hexdigest()
|
||||
saved_password = settings.getSetting("saved_user_password_" + hashed_username)
|
||||
if saved_password:
|
||||
sub_line += ": Saved"
|
||||
|
||||
else:
|
||||
user_item.setProperty("secure", "false")
|
||||
|
||||
if admin:
|
||||
sub_line += ", Admin"
|
||||
else:
|
||||
sub_line += ", User"
|
||||
|
||||
user_item.setProperty("manual", "false")
|
||||
user_item.setLabel2(sub_line)
|
||||
users.append(user_item)
|
||||
|
||||
if current_username == name:
|
||||
selected_id = len(users) - 1
|
||||
|
||||
if current_username:
|
||||
selection_title = string_load(30180) + " (" + current_username + ")"
|
||||
else:
|
||||
selected_id = -1
|
||||
users = []
|
||||
for user in result:
|
||||
config = user.get("Configuration")
|
||||
if config is not None:
|
||||
if config.get("IsHidden", False) is False:
|
||||
name = user.get("Name")
|
||||
admin = user.get("Policy", {}).get("IsAdministrator", False)
|
||||
selection_title = string_load(30180)
|
||||
|
||||
time_ago = ""
|
||||
last_active = user.get("LastActivityDate")
|
||||
if last_active:
|
||||
last_active_date = datetime_from_string(last_active)
|
||||
log.debug("LastActivityDate: {0}".format(last_active_date))
|
||||
ago = datetime.now() - last_active_date
|
||||
log.debug("LastActivityDate: {0}".format(ago))
|
||||
days = divmod(ago.seconds, 86400)
|
||||
hours = divmod(days[1], 3600)
|
||||
minutes = divmod(hours[1], 60)
|
||||
log.debug("LastActivityDate: {0} {1} {2}".format(days[0], hours[0], minutes[0]))
|
||||
if days[0]:
|
||||
time_ago += " %sd" % days[0]
|
||||
if hours[0]:
|
||||
time_ago += " %sh" % hours[0]
|
||||
if minutes[0]:
|
||||
time_ago += " %sm" % minutes[0]
|
||||
time_ago = time_ago.strip()
|
||||
if not time_ago:
|
||||
time_ago = "Active: now"
|
||||
else:
|
||||
time_ago = "Active: %s ago" % time_ago
|
||||
log.debug("LastActivityDate: {0}".format(time_ago))
|
||||
# add manual login
|
||||
user_item = xbmcgui.ListItem(string_load(30365))
|
||||
art = {"Thumb": "DefaultUser.png"}
|
||||
user_item.setArt(art)
|
||||
user_item.setLabel2(string_load(30366))
|
||||
user_item.setProperty("secure", "true")
|
||||
user_item.setProperty("manual", "true")
|
||||
users.append(user_item)
|
||||
|
||||
user_item = xbmcgui.ListItem(name)
|
||||
user_image = du.get_user_artwork(user, 'Primary')
|
||||
if not user_image:
|
||||
user_image = "DefaultUser.png"
|
||||
art = {"Thumb": user_image}
|
||||
user_item.setArt(art)
|
||||
user_item.setLabel2("TEST")
|
||||
return_value = xbmcgui.Dialog().select(selection_title,
|
||||
users,
|
||||
preselect=selected_id,
|
||||
autoclose=20000,
|
||||
useDetails=True)
|
||||
|
||||
sub_line = time_ago
|
||||
if return_value > -1 and return_value != selected_id:
|
||||
|
||||
if user.get("HasPassword", False) is True:
|
||||
sub_line += ", Password"
|
||||
user_item.setProperty("secure", "true")
|
||||
something_changed = True
|
||||
selected_user = users[return_value]
|
||||
secured = selected_user.getProperty("secure") == "true"
|
||||
manual = selected_user.getProperty("manual") == "true"
|
||||
selected_user_name = selected_user.getLabel()
|
||||
|
||||
m = hashlib.md5()
|
||||
m.update(name)
|
||||
hashed_username = m.hexdigest()
|
||||
saved_password = settings.getSetting("saved_user_password_" + hashed_username)
|
||||
if saved_password:
|
||||
sub_line += ": Saved"
|
||||
log.debug("Selected User Name: {0} : {1}".format(return_value, selected_user_name))
|
||||
|
||||
else:
|
||||
user_item.setProperty("secure", "false")
|
||||
if manual:
|
||||
kb = xbmc.Keyboard()
|
||||
kb.setHeading(string_load(30005))
|
||||
if current_username:
|
||||
kb.setDefault(current_username)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
selected_user_name = kb.getText()
|
||||
log.debug("Manual entered username: {0}".format(selected_user_name))
|
||||
else:
|
||||
return
|
||||
|
||||
if admin:
|
||||
sub_line += ", Admin"
|
||||
else:
|
||||
sub_line += ", User"
|
||||
if secured:
|
||||
# we need a password, check the settings first
|
||||
m = hashlib.md5()
|
||||
m.update(selected_user_name)
|
||||
hashed_username = m.hexdigest()
|
||||
saved_password = settings.getSetting("saved_user_password_" + hashed_username)
|
||||
allow_password_saving = settings.getSetting("allow_password_saving") == "true"
|
||||
|
||||
user_item.setProperty("manual", "false")
|
||||
user_item.setLabel2(sub_line)
|
||||
users.append(user_item)
|
||||
# if not saving passwords but have a saved ask to clear it
|
||||
if not allow_password_saving and saved_password:
|
||||
clear_password = xbmcgui.Dialog().yesno(string_load(30368), string_load(30369))
|
||||
if clear_password:
|
||||
settings.setSetting("saved_user_password_" + hashed_username, "")
|
||||
|
||||
if current_username == name:
|
||||
selected_id = len(users) - 1
|
||||
if saved_password:
|
||||
log.debug("Saving username and password: {0}".format(selected_user_name))
|
||||
log.debug("Using stored password for user: {0}".format(hashed_username))
|
||||
save_user_details(settings, selected_user_name, saved_password)
|
||||
|
||||
if current_username:
|
||||
selection_title = string_load(30180) + " (" + current_username + ")"
|
||||
else:
|
||||
selection_title = string_load(30180)
|
||||
|
||||
# add manual login
|
||||
user_item = xbmcgui.ListItem(string_load(30365))
|
||||
art = {"Thumb": "DefaultUser.png"}
|
||||
user_item.setArt(art)
|
||||
user_item.setLabel2(string_load(30366))
|
||||
user_item.setProperty("secure", "true")
|
||||
user_item.setProperty("manual", "true")
|
||||
users.append(user_item)
|
||||
|
||||
return_value = xbmcgui.Dialog().select(selection_title,
|
||||
users,
|
||||
preselect=selected_id,
|
||||
autoclose=20000,
|
||||
useDetails=True)
|
||||
|
||||
if return_value > -1 and return_value != selected_id:
|
||||
|
||||
something_changed = True
|
||||
selected_user = users[return_value]
|
||||
secured = selected_user.getProperty("secure") == "true"
|
||||
manual = selected_user.getProperty("manual") == "true"
|
||||
selected_user_name = selected_user.getLabel()
|
||||
|
||||
log.debug("Selected User Name: {0} : {1}".format(return_value, selected_user_name))
|
||||
|
||||
if manual:
|
||||
else:
|
||||
kb = xbmc.Keyboard()
|
||||
kb.setHeading(string_load(30005))
|
||||
if current_username:
|
||||
kb.setDefault(current_username)
|
||||
kb.setHeading(string_load(30006))
|
||||
kb.setHiddenInput(True)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
selected_user_name = kb.getText()
|
||||
log.debug("Manual entered username: {0}".format(selected_user_name))
|
||||
else:
|
||||
return
|
||||
|
||||
if secured:
|
||||
# we need a password, check the settings first
|
||||
m = hashlib.md5()
|
||||
m.update(selected_user_name)
|
||||
hashed_username = m.hexdigest()
|
||||
saved_password = settings.getSetting("saved_user_password_" + hashed_username)
|
||||
allow_password_saving = settings.getSetting("allow_password_saving") == "true"
|
||||
|
||||
# if not saving passwords but have a saved ask to clear it
|
||||
if not allow_password_saving and saved_password:
|
||||
clear_password = xbmcgui.Dialog().yesno(string_load(30368), string_load(30369))
|
||||
if clear_password:
|
||||
settings.setSetting("saved_user_password_" + hashed_username, "")
|
||||
|
||||
if saved_password:
|
||||
log.debug("Saving username and password: {0}".format(selected_user_name))
|
||||
log.debug("Using stored password for user: {0}".format(hashed_username))
|
||||
save_user_details(settings, selected_user_name, saved_password)
|
||||
save_user_details(settings, selected_user_name, kb.getText())
|
||||
|
||||
else:
|
||||
kb = xbmc.Keyboard()
|
||||
kb.setHeading(string_load(30006))
|
||||
kb.setHiddenInput(True)
|
||||
kb.doModal()
|
||||
if kb.isConfirmed():
|
||||
log.debug("Saving username and password: {0}".format(selected_user_name))
|
||||
save_user_details(settings, selected_user_name, kb.getText())
|
||||
|
||||
# should we save the password
|
||||
if allow_password_saving:
|
||||
save_password = xbmcgui.Dialog().yesno(string_load(30363), string_load(30364))
|
||||
if save_password:
|
||||
log.debug("Saving password for fast user switching: {0}".format(hashed_username))
|
||||
settings.setSetting("saved_user_password_" + hashed_username, kb.getText())
|
||||
else:
|
||||
log.debug("Saving username with no password: {0}".format(selected_user_name))
|
||||
save_user_details(settings, selected_user_name, "")
|
||||
# should we save the password
|
||||
if allow_password_saving:
|
||||
save_password = xbmcgui.Dialog().yesno(string_load(30363), string_load(30364))
|
||||
if save_password:
|
||||
log.debug("Saving password for fast user switching: {0}".format(hashed_username))
|
||||
settings.setSetting("saved_user_password_" + hashed_username, kb.getText())
|
||||
else:
|
||||
log.debug("Saving username with no password: {0}".format(selected_user_name))
|
||||
save_user_details(settings, selected_user_name, "")
|
||||
|
||||
if something_changed:
|
||||
home_window = HomeWindow()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import sys
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
import functools
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import urllib
|
||||
import encodings
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcaddon
|
||||
from .loghandler import LazyLogger
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Gnu General Public License - see LICENSE.TXT
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcaddon
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
@@ -13,6 +15,7 @@ import math
|
||||
from datetime import datetime
|
||||
import calendar
|
||||
import re
|
||||
from urllib import urlencode
|
||||
|
||||
from .downloadutils import DownloadUtils
|
||||
from .loghandler import LazyLogger
|
||||
@@ -28,17 +31,11 @@ log = LazyLogger(__name__)
|
||||
|
||||
def get_jellyfin_url(base_url, params):
|
||||
params["format"] = "json"
|
||||
param_list = []
|
||||
for key in params:
|
||||
if params[key] is not None:
|
||||
value = params[key]
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode("utf8")
|
||||
else:
|
||||
value = str(value)
|
||||
param_list.append(key + "=" + urllib.quote_plus(value, safe="{}"))
|
||||
param_string = "&".join(param_list)
|
||||
return base_url + "?" + param_string
|
||||
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
|
||||
|
||||
|
||||
###########################################################################
|
||||
@@ -218,8 +215,8 @@ def get_art(item, server):
|
||||
'tvshow.landscape': ''
|
||||
}
|
||||
|
||||
image_tags = item["ImageTags"]
|
||||
if image_tags is not None and image_tags["Primary"] is not None:
|
||||
image_tags = item.get("ImageTags", {})
|
||||
if image_tags and image_tags.get("Primary"):
|
||||
# image_tag = image_tags["Primary"]
|
||||
art['thumb'] = downloadUtils.get_artwork(item, "Primary", server=server)
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ STATUS_INVALID_EXTENSION = 1010
|
||||
STATUS_UNEXPECTED_CONDITION = 1011
|
||||
STATUS_TLS_HANDSHAKE_ERROR = 1015
|
||||
|
||||
logger = logging.getLogger()
|
||||
#logger = logging.getLogger()
|
||||
|
||||
|
||||
class WebSocketException(Exception):
|
||||
@@ -108,10 +108,10 @@ def enableTrace(tracable):
|
||||
"""
|
||||
global traceEnabled
|
||||
traceEnabled = tracable
|
||||
if tracable:
|
||||
if not logger.handlers:
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
logger.setLevel(logging.DEBUG)
|
||||
#if tracable:
|
||||
# if not logger.handlers:
|
||||
# logger.addHandler(logging.StreamHandler())
|
||||
# logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
def setdefaulttimeout(timeout):
|
||||
@@ -512,10 +512,10 @@ class WebSocket(object):
|
||||
|
||||
header_str = "\r\n".join(headers)
|
||||
self._send(header_str)
|
||||
if traceEnabled:
|
||||
logger.debug("--- request header ---")
|
||||
logger.debug(header_str)
|
||||
logger.debug("-----------------------")
|
||||
#if traceEnabled:
|
||||
# logger.debug("--- request header ---")
|
||||
# logger.debug(header_str)
|
||||
# logger.debug("-----------------------")
|
||||
|
||||
status, resp_headers = self._read_headers()
|
||||
if status != 101:
|
||||
@@ -550,16 +550,16 @@ class WebSocket(object):
|
||||
def _read_headers(self):
|
||||
status = None
|
||||
headers = {}
|
||||
if traceEnabled:
|
||||
logger.debug("--- response header ---")
|
||||
#if traceEnabled:
|
||||
# logger.debug("--- response header ---")
|
||||
|
||||
while True:
|
||||
line = self._recv_line()
|
||||
if line == "\r\n":
|
||||
break
|
||||
line = line.strip()
|
||||
if traceEnabled:
|
||||
logger.debug(line)
|
||||
#if traceEnabled:
|
||||
# logger.debug(line)
|
||||
if not status:
|
||||
status_info = line.split(" ", 2)
|
||||
status = int(status_info[1])
|
||||
@@ -571,8 +571,8 @@ class WebSocket(object):
|
||||
else:
|
||||
raise WebSocketException("Invalid header")
|
||||
|
||||
if traceEnabled:
|
||||
logger.debug("-----------------------")
|
||||
#if traceEnabled:
|
||||
# logger.debug("-----------------------")
|
||||
|
||||
return status, headers
|
||||
|
||||
@@ -591,8 +591,8 @@ class WebSocket(object):
|
||||
frame.get_mask_key = self.get_mask_key
|
||||
data = frame.format()
|
||||
length = len(data)
|
||||
if traceEnabled:
|
||||
logger.debug("send: " + repr(data))
|
||||
#if traceEnabled:
|
||||
# logger.debug("send: " + repr(data))
|
||||
while data:
|
||||
l = self._send(data)
|
||||
data = data[l:]
|
||||
@@ -645,7 +645,7 @@ class WebSocket(object):
|
||||
self._cont_data[1] += frame.data
|
||||
else:
|
||||
self._cont_data = [frame.opcode, frame.data]
|
||||
|
||||
|
||||
if frame.fin:
|
||||
data = self._cont_data
|
||||
self._cont_data = None
|
||||
@@ -718,12 +718,12 @@ class WebSocket(object):
|
||||
|
||||
reason: the reason to close. This must be string.
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
'''
|
||||
if self.connected:
|
||||
if status < 0 or status >= ABNF.LENGTH_16:
|
||||
@@ -735,10 +735,10 @@ class WebSocket(object):
|
||||
self.sock.settimeout(3)
|
||||
try:
|
||||
frame = self.recv_frame()
|
||||
if logger.isEnabledFor(logging.ERROR):
|
||||
if #logger.isEnabledFor(logging.ERROR):
|
||||
recv_status = struct.unpack("!H", frame.data)[0]
|
||||
if recv_status != STATUS_NORMAL:
|
||||
logger.error("close status: " + repr(recv_status))
|
||||
#logger.error("close status: " + repr(recv_status))
|
||||
except:
|
||||
pass
|
||||
self.sock.settimeout(timeout)
|
||||
@@ -856,7 +856,7 @@ class WebSocketApp(object):
|
||||
"""
|
||||
self.keep_running = False
|
||||
if(self.sock != None):
|
||||
self.sock.close()
|
||||
self.sock.close()
|
||||
|
||||
def _send_ping(self, interval):
|
||||
while True:
|
||||
@@ -897,14 +897,14 @@ class WebSocketApp(object):
|
||||
thread.start()
|
||||
|
||||
while self.keep_running:
|
||||
|
||||
|
||||
try:
|
||||
data = self.sock.recv()
|
||||
|
||||
|
||||
if data is None or self.keep_running == False:
|
||||
break
|
||||
self._callback(self.on_message, data)
|
||||
|
||||
self._callback(self.on_message, data)
|
||||
|
||||
except Exception as e:
|
||||
found_timeout = False
|
||||
for arg in e.args:
|
||||
@@ -928,7 +928,7 @@ class WebSocketApp(object):
|
||||
try:
|
||||
callback(self, *args)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
#logger.error(e)
|
||||
if True:#logger.isEnabledFor(logging.DEBUG):
|
||||
_, _, tb = sys.exc_info()
|
||||
traceback.print_tb(tb)
|
||||
|
||||
@@ -254,7 +254,7 @@ class WebSocketClient(threading.Thread):
|
||||
else:
|
||||
server = server.replace('http', "ws")
|
||||
|
||||
websocket_url = "%s/websocket?api_key=%s&deviceId=%s" % (server, token, self.device_id)
|
||||
websocket_url = "%s/socket?api_key=%s&deviceId=%s" % (server, token, self.device_id)
|
||||
log.debug("websocket url: {0}".format(websocket_url))
|
||||
|
||||
self._client = websocket.WebSocketApp(websocket_url,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
import xbmcaddon
|
||||
import xbmcplugin
|
||||
import xbmcgui
|
||||
import xbmc
|
||||
import json
|
||||
import hashlib
|
||||
import random
|
||||
import time
|
||||
@@ -43,7 +44,6 @@ def set_random_movies():
|
||||
url = get_jellyfin_url("{server}/Users/{userid}/Items", url_params)
|
||||
|
||||
results = downloadUtils.download_url(url, suppress=True)
|
||||
results = json.loads(results)
|
||||
|
||||
randon_movies_list = []
|
||||
if results is not None:
|
||||
@@ -81,7 +81,7 @@ def set_background_image(force=False):
|
||||
|
||||
url_params = {}
|
||||
url_params["Recursive"] = True
|
||||
# url_params["limit"] = 60
|
||||
url_params["limit"] = 100
|
||||
url_params["SortBy"] = "Random"
|
||||
url_params["IncludeItemTypes"] = "Movie,Series"
|
||||
url_params["ImageTypeLimit"] = 1
|
||||
@@ -90,7 +90,6 @@ def set_background_image(force=False):
|
||||
|
||||
server = downloadUtils.get_server()
|
||||
results = downloadUtils.download_url(url, suppress=True)
|
||||
results = json.loads(results)
|
||||
|
||||
if results is not None:
|
||||
items = results.get("Items", [])
|
||||
@@ -144,12 +143,10 @@ def check_for_new_content():
|
||||
url_params["SortOrder"] = "Descending"
|
||||
url_params["IncludeItemTypes"] = "Movie,Episode"
|
||||
url_params["ImageTypeLimit"] = 0
|
||||
url_params["format"] = "json"
|
||||
|
||||
added_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params)
|
||||
|
||||
added_result = downloadUtils.download_url(added_url, suppress=True)
|
||||
result = json.loads(added_result)
|
||||
result = downloadUtils.download_url(added_url, suppress=True)
|
||||
log.debug("LATEST_ADDED_ITEM: {0}".format(result))
|
||||
|
||||
last_added_date = ""
|
||||
@@ -168,12 +165,10 @@ def check_for_new_content():
|
||||
url_params["SortOrder"] = "Descending"
|
||||
url_params["IncludeItemTypes"] = "Movie,Episode"
|
||||
url_params["ImageTypeLimit"] = 0
|
||||
url_params["format"] = "json"
|
||||
|
||||
played_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params)
|
||||
|
||||
played_result = downloadUtils.download_url(played_url, suppress=True)
|
||||
result = json.loads(played_result)
|
||||
result = downloadUtils.download_url(played_url, suppress=True)
|
||||
log.debug("LATEST_PLAYED_ITEM: {0}".format(result))
|
||||
|
||||
last_played_date = ""
|
||||
@@ -208,7 +203,7 @@ def get_widget_content_cast(handle, params):
|
||||
|
||||
item_id = params["id"]
|
||||
data_manager = DataManager()
|
||||
result = data_manager.get_content("{server}/Users/{userid}/Items/" + item_id + "?format=json")
|
||||
result = data_manager.get_content("{server}/Users/{userid}/Items/" + item_id)
|
||||
log.debug("ItemInfo: {0}".format(result))
|
||||
|
||||
if not result:
|
||||
@@ -288,7 +283,6 @@ def get_widget_content(handle, params):
|
||||
url_verb = "{server}/Users/{userid}/Items"
|
||||
url_params = {}
|
||||
url_params["Limit"] = "{ItemLimit}"
|
||||
url_params["format"] = "json"
|
||||
url_params["Fields"] = "{field_filters}"
|
||||
url_params["ImageTypeLimit"] = 1
|
||||
url_params["IsMissing"] = False
|
||||
@@ -331,7 +325,6 @@ def get_widget_content(handle, params):
|
||||
url_params["IsVirtualUnaired"] = False
|
||||
url_params["IncludeItemTypes"] = "Episode"
|
||||
url_params["ImageTypeLimit"] = 1
|
||||
url_params["format"] = "json"
|
||||
|
||||
elif widget_type == "recent_episodes":
|
||||
xbmcplugin.setContent(handle, 'episodes')
|
||||
@@ -360,7 +353,6 @@ def get_widget_content(handle, params):
|
||||
url_params["userid"] = "{userid}"
|
||||
url_params["Recursive"] = True
|
||||
url_params["Fields"] = "{field_filters}"
|
||||
url_params["format"] = "json"
|
||||
url_params["ImageTypeLimit"] = 1
|
||||
|
||||
elif widget_type == "movie_recommendations":
|
||||
|
||||
Reference in New Issue
Block a user