27 Commits

Author SHA1 Message Date
Matt
3f7816762e Version bump to 0.3.0 2020-12-11 20:33:51 -05:00
mcarlton00
8bade51eb5 Merge pull request #33 from mcarlton00/10.7-fixes
10.7 fixes
2020-12-11 20:30:49 -05:00
Matt
7c4398bfb5 When playback stops, only try to delete a transcode if we're transcoding 2020-12-05 18:30:40 -05:00
Matt
8f736e8bd3 We need the brackets for later 2020-12-05 18:28:20 -05:00
Matt
65a9b11dc5 Include URL when there's been an http error 2020-12-05 18:27:36 -05:00
Matt
7ffd16df4b Remove manually specifying return payload 2020-12-05 18:26:53 -05:00
mcarlton00
e2628d27dc Don't error on empty user list 2020-11-28 15:00:29 -05:00
mcarlton00
8799c2bb5e Use correct lookup URL 2020-11-28 14:59:55 -05:00
Matt
4ba0b64d2c Update auth for 10.7 2020-11-23 17:58:45 -05:00
mcarlton00
4b2f43e8a2 Merge pull request #32 from mcarlton00/json-payloads
Proper API json parsing
2020-11-15 14:44:11 -05:00
Matt
df774ca3c5 Simplify logic checks 2020-11-15 14:13:45 -05:00
Matt
084fab576e Remove debug statement 2020-11-15 11:04:13 -05:00
mcarlton00
1733e64403 Parse json payloads in centralized place 2020-11-11 22:50:26 -05:00
mcarlton00
1d0360c0c3 Merge pull request #29 from mcarlton00/words-r-hard
Connect to servers with special characters in the name
2020-10-06 22:22:34 -04:00
Matt
45823ccd96 Connect to servers with special characters in the name 2020-10-06 21:43:36 -04:00
mcarlton00
ef3b64cf51 Merge pull request #25 from mcarlton00/castaway
Fix casting from web UI
2020-09-10 17:41:24 -04:00
mcarlton00
a424fb8793 Merge pull request #24 from mcarlton00/when-is-a-string-not-a-string
Use future strings to fix unicode errors
2020-09-10 17:40:51 -04:00
mcarlton00
b6ae819d32 Merge pull request #23 from mcarlton00/i-know-my-abcs
Fix browsing libraries by letter
2020-09-10 17:39:41 -04:00
Matt
8711ae2452 Fix casting from web UI 2020-09-05 17:49:18 -04:00
Matt
a90c2c2fa8 Use future strings to fix unicode errors 2020-09-05 17:29:38 -04:00
Matt
03a89d4f43 Remove unnecessary log line 2020-09-05 16:15:54 -04:00
Matt
d48b2bdf2a Fix browsing libraries by letter 2020-09-05 16:11:15 -04:00
mcarlton00
6a6ca8c642 Merge pull request #22 from mcarlton00/noisy-logs-are-noisy
Fix log levels
2020-09-03 10:35:57 -04:00
Matt
d3ffecb866 Fix log levels 2020-09-02 23:04:13 -04:00
mcarlton00
083f91611a Merge pull request #16 from Shadowghost/websocket-url-fix
Fix websocket_url (fixes playing transcoded streams)
2020-08-18 08:29:05 -04:00
Shadowghost
01e9c45df6 Fix websocket_url (fixes playing transcoded streams) 2020-08-18 11:22:27 +02:00
Matt
b327ebc5bd Update authors field 2020-08-16 22:00:55 -04:00
31 changed files with 381 additions and 431 deletions

View File

@@ -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>

View File

@@ -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

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcgui

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcvfs
import xbmc
import base64

View File

@@ -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')))

View File

@@ -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,

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcgui
import xbmcplugin

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import threading
import time

View File

@@ -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]

View File

@@ -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"

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcaddon
import xbmcgui

View File

@@ -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

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import os
import threading

View File

@@ -1,4 +1,5 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcgui

View File

@@ -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

View File

@@ -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()

View File

@@ -1,5 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import json
import sys
import xbmcgui
import xbmcplugin

View File

@@ -1,4 +1,5 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import os

View File

@@ -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

View File

@@ -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

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcaddon
from .loghandler import LazyLogger

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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":