47 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
Matt
ec1a5add73 version bump 2020-08-16 21:19:12 -04:00
mcarlton00
b7110a7222 Merge pull request #15 from ltGuillaume/patch-1
Fix annoying typo "defalt"
2020-08-16 19:13:45 -04:00
Guillaume
2e19d2eac1 Fix annoying typo "defalt" 2020-08-16 23:51:56 +02:00
mcarlton00
ad7f388d68 Merge pull request #11 from mcarlton00/improve-logging
Improve logging
2020-07-25 16:01:58 -04:00
Matt
f28c1e7fae Cleanup less helpful logging 2020-07-25 13:36:21 -04:00
Matt
6ce342c0b3 Remove debug statement 2020-07-25 13:36:03 -04:00
Matt
622bdf613c Sanitize server url from logs 2020-07-25 13:35:29 -04:00
Matt
a0efd1087f Copy log handler from JF for Kodi, modify for strings 2020-07-25 01:01:30 -04:00
mcarlton00
9813295fd3 Merge pull request #8 from TrueTechy/server_address_storing_fix
Store full URL instead of component parts. Fixes #1 and Fixes #2
2020-07-19 18:27:32 -04:00
Abby Gourlay
303fdfc9ad Merge branch 'master' of github.com:mcarlton00/jellycon into server_address_storing_fix 2020-07-19 23:19:28 +01:00
mcarlton00
90d88a998c Merge pull request #9 from TrueTechy/issue-template
Create issue templates
2020-07-19 17:59:13 -04:00
Abby
1999e1daf2 Update issue templates 2020-07-19 17:17:45 +01:00
mcarlton00
433f39dd38 Merge pull request #7 from TrueTechy/httplib_replacement
Replace httplib with requests
2020-07-18 23:24:46 -04:00
Abby
110746e859 Update resources/lib/image_server.py spelling error
Co-authored-by: mcarlton00 <mcarlton00@gmail.com>
2020-07-19 04:21:37 +01:00
Abby Gourlay
640860a3af Store full URL instead of component parts. Fixes #1 and Fixes #2 2020-07-19 03:54:15 +01:00
Abby Gourlay
b21fa807db Fixed auth bug 2020-07-19 02:05:34 +01:00
Abby Gourlay
446dd921bf Replace httplib with requests 2020-07-19 01:31:42 +01:00
mcarlton00
594fccd602 Merge pull request #6 from TrueTechy/fix_connection_test
Lowered the maximum connection test size to comply with API limits
2020-07-18 11:31:33 -04:00
Abby Gourlay
47993b612f Removed debugging code 2020-07-18 15:55:24 +01:00
Abby Gourlay
2302f2dbef Lowered the maximum connection test size to comply with API limits 2020-07-18 15:53:21 +01:00
38 changed files with 962 additions and 1094 deletions

36
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
<!-- Steps to reproduce the behavior: -->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Logs**
<!-- Please paste any log errors. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**System (please complete the following information):**
- OS: [e.g. Android, Debian, Windows]
- Jellyfin Version: [e.g. 10.0.1]
- Kodi Version: [e.g. 18.3]
- Addon Version: [e.g. 0.1.1]
**Additional context**
<!-- Add any other context about the problem here. -->

View File

@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.jellycon"
name="JellyCon"
version="0.1.1"
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"/>
<import addon="script.module.requests" version="2.22.0"/>
<import addon="script.module.six" version="1.13.0"/>
<import addon="script.module.kodi-six" />
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>video audio</provides>
@@ -20,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

@@ -2,11 +2,11 @@
import xbmcaddon
from resources.lib.simple_logging import SimpleLogging
from resources.lib.loghandler import LazyLogger
from resources.lib.functions import main_entry_point
from resources.lib.tracking import set_timing_enabled
log = SimpleLogging('default')
log = LazyLogger('default')
settings = xbmcaddon.Addon()
log_timing_data = settings.getSetting('log_timing') == "true"

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
@@ -6,9 +7,9 @@ import threading
import xbmc
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class ActionAutoClose(threading.Thread):
@@ -27,7 +28,7 @@ class ActionAutoClose(threading.Thread):
log.debug("ActionAutoClose Running")
while not xbmc.abortRequested and not self.stop_thread:
time_since_last = time.time() - self.last_interaction
log.debug("ActionAutoClose time_since_last : {0}", time_since_last)
log.debug("ActionAutoClose time_since_last : {0}".format(time_since_last))
if time_since_last > 20:
log.debug("ActionAutoClose Closing Parent")
@@ -40,7 +41,7 @@ class ActionAutoClose(threading.Thread):
def set_last(self):
self.last_interaction = time.time()
log.debug("ActionAutoClose set_last : {0}", self.last_interaction)
log.debug("ActionAutoClose set_last : {0}".format(self.last_interaction))
def stop(self):
log.debug("ActionAutoClose stop_thread called")
@@ -79,7 +80,7 @@ class ActionMenu(xbmcgui.WindowXMLDialog):
pass
def onMessage(self, message):
log.debug("ActionMenu: onMessage: {0}", message)
log.debug("ActionMenu: onMessage: {0}".format(message))
def onAction(self, action):
@@ -91,12 +92,12 @@ class ActionMenu(xbmcgui.WindowXMLDialog):
self.close()
else:
self.auto_close_thread.set_last()
log.debug("ActionMenu: onAction: {0}", action.getId())
log.debug("ActionMenu: onAction: {0}".format(action.getId()))
def onClick(self, control_id):
if control_id == 3000:
self.selected_action = self.listControl.getSelectedItem()
log.debug("ActionMenu: Selected Item: {0}", self.selected_action)
log.debug("ActionMenu: Selected Item: {0}".format(self.selected_action))
self.auto_close_thread.stop()
self.close()

View File

@@ -1,9 +1,11 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class BitrateDialog(xbmcgui.WindowXMLDialog):
@@ -35,7 +37,7 @@ class BitrateDialog(xbmcgui.WindowXMLDialog):
pass
def onMessage(self, message):
log.debug("ActionMenu: onMessage: {0}", message)
log.debug("ActionMenu: onMessage: {0}".format(message))
def onAction(self, action):
@@ -54,5 +56,5 @@ class BitrateDialog(xbmcgui.WindowXMLDialog):
def onClick(self, control_id):
if control_id == 3000:
log.debug("ActionMenu: Selected Item: {0}", control_id)
#self.close()
log.debug("ActionMenu: Selected Item: {0}".format(control_id))
#self.close()

View File

@@ -1,8 +1,9 @@
# coding=utf-8
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import urllib
import httplib
import requests
import base64
import sys
import threading
@@ -14,15 +15,15 @@ import xbmc
import xbmcaddon
from .downloadutils import DownloadUtils
from .simple_logging import SimpleLogging
from .jsonrpc import JsonRpc
from .loghandler import LazyLogger
from .jsonrpc import JsonRpc, get_value
from .translation import string_load
from .datamanager import DataManager
from .utils import get_art, double_urlencode
from .kodi_utils import HomeWindow
downloadUtils = DownloadUtils()
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class CacheArtwork(threading.Thread):
@@ -62,7 +63,7 @@ class CacheArtwork(threading.Thread):
monitor.waitForAbort(5)
log.debug("CacheArtwork background thread exited : stop_all_activity : {0}", self.stop_all_activity)
log.debug("CacheArtwork background thread exited : stop_all_activity : {0}".format(self.stop_all_activity))
@staticmethod
def delete_cached_images(item_id):
@@ -74,7 +75,7 @@ class CacheArtwork(threading.Thread):
item_image_url_part = "Items/%s/Images/" % item_id
item_image_url_part = item_image_url_part.replace("/", "%2f")
log.debug("texture ids: {0}", item_image_url_part)
log.debug("texture ids: {0}".format(item_image_url_part))
# is the web server enabled
web_query = {"setting": "services.webserver"}
@@ -87,7 +88,7 @@ class CacheArtwork(threading.Thread):
params = {"properties": ["url"]}
json_result = JsonRpc('Textures.GetTextures').execute(params)
textures = json_result.get("result", {}).get("textures", [])
log.debug("texture ids: {0}", textures)
log.debug("texture ids: {0}".format(textures))
progress.update(70, string_load(30346))
@@ -97,7 +98,7 @@ class CacheArtwork(threading.Thread):
texture_url = texture["url"]
if item_image_url_part in texture_url:
delete_count += 1
log.debug("removing texture id: {0}", texture_id)
log.debug("removing texture id: {0}".format(texture_id))
params = {"textureid": int(texture_id)}
JsonRpc('Textures.RemoveTexture').execute(params)
@@ -141,8 +142,8 @@ class CacheArtwork(threading.Thread):
jellyfin_texture_urls = self.get_jellyfin_artwork(delete_pdialog)
log.debug("kodi textures: {0}", textures)
log.debug("jellyfin texture urls: {0}", jellyfin_texture_urls)
log.debug("kodi textures: {0}".format(textures))
log.debug("jellyfin texture urls: {0}".format(jellyfin_texture_urls))
if jellyfin_texture_urls is not None:
@@ -157,7 +158,7 @@ class CacheArtwork(threading.Thread):
unused_texture_ids.add(texture["textureid"])
total = len(unused_texture_ids)
log.debug("unused texture ids: {0}", unused_texture_ids)
log.debug("unused texture ids: {0}".format(unused_texture_ids))
for texture_id in unused_texture_ids:
params = {"textureid": int(texture_id)}
@@ -206,11 +207,11 @@ class CacheArtwork(threading.Thread):
try:
result_text = self.cache_artwork(dp)
except Exception as err:
log.error("Cache Images Failed : {0}", err)
log.error("Cache Images Failed : {0}".format(err))
dp.close()
del dp
if result_text is not None:
log.debug("Cache Images reuslt : {0}", " - ".join(result_text))
log.debug("Cache Images reuslt : {0}".format(" - ".join(result_text)))
def get_jellyfin_artwork(self, progress):
log.debug("get_jellyfin_artwork")
@@ -233,7 +234,7 @@ class CacheArtwork(threading.Thread):
results = results.get("Items")
server = downloadUtils.get_server()
log.debug("Jellyfin Item Count Count: {0}", len(results))
log.debug("Jellyfin Item Count Count: {0}".format(len(results)))
if self.stop_all_activity:
return None
@@ -254,36 +255,27 @@ class CacheArtwork(threading.Thread):
log.debug("cache_artwork")
# is the web server enabled
web_query = {"setting": "services.webserver"}
result = JsonRpc('Settings.GetSettingValue').execute(web_query)
xbmc_webserver_enabled = result['result']['value']
if not xbmc_webserver_enabled:
if not get_value("services.webserver"):
log.error("Kodi web server not enabled, can not cache images")
return
# get the port
web_port = {"setting": "services.webserverport"}
result = JsonRpc('Settings.GetSettingValue').execute(web_port)
xbmc_port = result['result']['value']
log.debug("xbmc_port: {0}", xbmc_port)
xbmc_port = get_value("services.webserverport")
log.debug("xbmc_port: {0}".format(xbmc_port))
# get the user
web_user = {"setting": "services.webserverusername"}
result = JsonRpc('Settings.GetSettingValue').execute(web_user)
xbmc_username = result['result']['value']
log.debug("xbmc_username: {0}", xbmc_username)
xbmc_username = get_value("services.webserverusername")
log.debug("xbmc_username: {0}".format(xbmc_username))
# get the password
web_pass = {"setting": "services.webserverpassword"}
result = JsonRpc('Settings.GetSettingValue').execute(web_pass)
xbmc_password = result['result']['value']
xbmc_password = get_value("services.webserverpassword")
progress.update(0, string_load(30356))
params = {"properties": ["url"]}
json_result = JsonRpc('Textures.GetTextures').execute(params)
textures = json_result.get("result", {}).get("textures", [])
log.debug("Textures.GetTextures Count: {0}", len(textures))
log.debug("Textures.GetTextures Count: {0}".format(len(textures)))
if self.stop_all_activity:
return
@@ -301,7 +293,7 @@ class CacheArtwork(threading.Thread):
del textures
del json_result
log.debug("texture_urls Count: {0}", len(texture_urls))
log.debug("texture_urls Count: {0}".format(len(texture_urls)))
if self.stop_all_activity:
return
@@ -313,6 +305,7 @@ class CacheArtwork(threading.Thread):
return
missing_texture_urls = set()
# image_types = ["thumb", "poster", "banner", "clearlogo", "tvshow.poster", "tvshow.banner", "tvshow.landscape"]
for image_url in jellyfin_texture_urls:
if image_url not in texture_urls and not image_url.endswith("&Tag=") and len(image_url) > 0:
@@ -321,10 +314,10 @@ class CacheArtwork(threading.Thread):
if self.stop_all_activity:
return
log.debug("texture_urls: {0}", texture_urls)
log.debug("missing_texture_urls: {0}", missing_texture_urls)
log.debug("Number of existing textures: {0}", len(texture_urls))
log.debug("Number of missing textures: {0}", len(missing_texture_urls))
log.debug("texture_urls: {0}".format(texture_urls))
log.debug("missing_texture_urls: {0}".format(missing_texture_urls))
log.debug("Number of existing textures: {0}".format(len(texture_urls)))
log.debug("Number of missing textures: {0}".format(len(missing_texture_urls)))
kodi_http_server = "localhost:" + str(xbmc_port)
headers = {}
@@ -333,27 +326,25 @@ class CacheArtwork(threading.Thread):
headers = {'Authorization': 'Basic %s' % base64.b64encode(auth)}
total = len(missing_texture_urls)
index = 1
count_done = 0
for get_url in missing_texture_urls:
for index, get_url in enumerate(missing_texture_urls, 1):
# log.debug("texture_url: {0}", get_url)
url = double_urlencode(get_url)
kodi_texture_url = ("/image/image://%s" % url)
log.debug("kodi_texture_url: {0}", kodi_texture_url)
log.debug("kodi_texture_url: {0}".format(kodi_texture_url))
percentage = int((float(index) / float(total)) * 100)
message = "%s of %s" % (index, total)
progress.update(percentage, message)
conn = httplib.HTTPConnection(kodi_http_server, timeout=20)
conn.request(method="GET", url=kodi_texture_url, headers=headers)
data = conn.getresponse()
if data.status == 200:
count_done += 1
log.debug("Get Image Result: {0}", data.status)
cache_url = "http://%s%s" % (kodi_http_server, kodi_texture_url)
data = requests.get(cache_url, timeout=20, headers=headers)
if data.status_code == 200:
count_done += 1
log.debug("Get Image Result: {0}".format(data.status_code))
index += 1
# if progress.iscanceled():
# if "iscanceled" in dir(progress) and progress.iscanceled():
if isinstance(progress, xbmcgui.DialogProgress) and progress.iscanceled():

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
@@ -6,9 +7,9 @@ import xbmc
import xbmcvfs
from .kodi_utils import HomeWindow
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class ClientInformation:
@@ -23,20 +24,20 @@ class ClientInformation:
return client_id
jellyfin_guid_path = xbmc.translatePath("special://temp/jellycon_guid").decode('utf-8')
log.debug("jellyfin_guid_path: {0}", jellyfin_guid_path)
log.debug("jellyfin_guid_path: {0}".format(jellyfin_guid_path))
guid = xbmcvfs.File(jellyfin_guid_path)
client_id = guid.read()
guid.close()
if not client_id:
client_id = str("%012X" % uuid4())
log.debug("Generating a new guid: {0}", client_id)
log.debug("Generating a new guid: {0}".format(client_id))
guid = xbmcvfs.File(jellyfin_guid_path, 'w')
guid.write(client_id)
guid.close()
log.debug("jellyfin_client_id (NEW): {0}", client_id)
log.debug("jellyfin_client_id (NEW): {0}".format(client_id))
else:
log.debug("jellyfin_client_id: {0}", client_id)
log.debug("jellyfin_client_id: {0}".format(client_id))
window.set_property("client_id", client_id)
return client_id

View File

@@ -1,10 +1,10 @@
import threading
import xbmc
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from resources.lib.functions import show_menu
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class ContextMonitor(threading.Thread):
@@ -33,41 +33,6 @@ class ContextMonitor(threading.Thread):
xbmc.sleep(100)
'''
context_up = False
is_jellycon_item = False
while not xbmc.abortRequested and not self.stop_thread:
if xbmc.getCondVisibility("Window.IsActive(fullscreenvideo) | Window.IsActive(visualisation)"):
xbmc.sleep(1000)
else:
if xbmc.getCondVisibility("Window.IsVisible(contextmenu)"):
context_up = True
if is_jellycon_item:
xbmc.executebuiltin("Dialog.Close(contextmenu,true)")
else:
if context_up: # context now down, do something
context_up = False
container_id = xbmc.getInfoLabel("System.CurrentControlID")
log.debug("ContextMonitor Container ID: {0}", container_id)
item_id = xbmc.getInfoLabel("Container(" + str(container_id) + ").ListItem.Property(id)")
log.debug("ContextMonitor Item ID: {0}", item_id)
if item_id:
params = {}
params["item_id"] = item_id
show_menu(params)
container_id = xbmc.getInfoLabel("System.CurrentControlID")
condition = ("String.StartsWith(Container(" + str(container_id) +
").ListItem.Path,plugin://plugin.video.jellycon) + !String.IsEmpty(Container(" +
str(container_id) + ").ListItem.Property(id))")
is_jellycon_item = xbmc.getCondVisibility(condition)
xbmc.sleep(200)
'''
log.debug("ContextMonitor Thread Exited")
def stop_monitor(self):

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
@@ -9,7 +9,7 @@ import cPickle
import time
from .downloadutils import DownloadUtils
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .item_functions import extract_item_info
from .kodi_utils import HomeWindow
from .translation import string_load
@@ -21,7 +21,7 @@ import xbmcaddon
import xbmcvfs
import xbmcgui
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class CacheItem:
@@ -46,21 +46,15 @@ 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):
home_window = HomeWindow()
log.debug("last_content_url : use_cache={0} url={1}", use_cache, url)
log.debug("last_content_url : use_cache={0} url={1}".format(use_cache, url))
home_window.set_property("last_content_url", url)
download_utils = DownloadUtils()
@@ -102,7 +96,7 @@ class DataManager:
item_list = cache_item.item_list
total_records = cache_item.total_records
except Exception as err:
log.error("Pickle Data Load Failed : {0}", err)
log.error("Pickle Data Load Failed : {0}".format(err))
item_list = None
# we need to load the list item data form the server
@@ -206,7 +200,7 @@ class CacheManagerThread(threading.Thread):
else:
log.debug("CacheManagerThread : Reloading to recheck data hashes")
cached_hash = self.cached_item.item_list_hash
log.debug("CacheManagerThread : Cache Hash : {0}", cached_hash)
log.debug("CacheManagerThread : Cache Hash : {0}".format(cached_hash))
data_manager = DataManager()
results = data_manager.get_content(self.cached_item.items_url)
@@ -232,7 +226,7 @@ class CacheManagerThread(threading.Thread):
return
loaded_hash = self.get_data_hash(loaded_items)
log.debug("CacheManagerThread : Loaded Hash : {0}", loaded_hash)
log.debug("CacheManagerThread : Loaded Hash : {0}".format(loaded_hash))
# if they dont match then save the data and trigger a content reload
if cached_hash != loaded_hash:
@@ -252,7 +246,7 @@ class CacheManagerThread(threading.Thread):
# TODO: probably should only set this in simple check mode
current_time_stamp = str(time.time())
home_window.set_property("jellycon_widget_reload", current_time_stamp)
log.debug("Setting New Widget Hash: {0}", current_time_stamp)
log.debug("Setting New Widget Hash: {0}".format(current_time_stamp))
log.debug("CacheManagerThread : Sending container refresh")
xbmc.executebuiltin("Container.Refresh")
@@ -276,7 +270,7 @@ def clear_cached_server_data():
del_count = 0
for filename in files:
if filename.startswith("cache_") and filename.endswith(".pickle"):
log.debug("Deleteing CacheFile: {0}", filename)
log.debug("Deleteing CacheFile: {0}".format(filename))
xbmcvfs.delete(os.path.join(addon_dir, filename))
del_count += 1
@@ -293,7 +287,7 @@ def clear_old_cache_data():
del_count = 0
for filename in files:
if filename.startswith("cache_") and filename.endswith(".pickle"):
log.debug("clear_old_cache_data() : Checking CacheFile : {0}", filename)
log.debug("clear_old_cache_data() : Checking CacheFile : {0}".format(filename))
cache_item = None
for x in range(0, 5):
@@ -304,7 +298,7 @@ def clear_old_cache_data():
cache_item = cPickle.load(handle)
break
except Exception as error:
log.debug("clear_old_cache_data() : Pickle load error : {0}", error)
log.debug("clear_old_cache_data() : Pickle load error : {0}".format(error))
cache_item = None
xbmc.sleep(1000)
@@ -313,9 +307,9 @@ def clear_old_cache_data():
if cache_item.date_last_used is not None:
item_last_used = time.time() - cache_item.date_last_used
log.debug("clear_old_cache_data() : Cache item last used : {0} sec ago", item_last_used)
log.debug("clear_old_cache_data() : Cache item last used : {0} sec ago".format(item_last_used))
if item_last_used == -1 or item_last_used > (3600 * 24 * 7):
log.debug("clear_old_cache_data() : Deleting cache item age : {0}", item_last_used)
log.debug("clear_old_cache_data() : Deleting cache item age : {0}".format(item_last_used))
data_file = os.path.join(addon_dir, filename)
with FileLock(data_file + ".locked", timeout=5):
xbmcvfs.delete(data_file)
@@ -326,4 +320,4 @@ def clear_old_cache_data():
with FileLock(data_file + ".locked", timeout=5):
xbmcvfs.delete(data_file)
log.debug("clear_old_cache_data() : Cache items deleted : {0}", del_count)
log.debug("clear_old_cache_data() : Cache items deleted : {0}".format(del_count))

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
@@ -12,12 +13,12 @@ from .datamanager import DataManager
from .kodi_utils import HomeWindow
from .downloadutils import DownloadUtils
from .translation import string_load
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .item_functions import add_gui_item, ItemDetails
from .utils import send_event_notification
from .tracking import timer
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
@timer
@@ -29,8 +30,8 @@ def get_content(url, params):
if not media_type:
xbmcgui.Dialog().ok(string_load(30135), string_load(30139))
log.debug("URL: {0}", url)
log.debug("MediaType: {0}", media_type)
log.debug("URL: {0}".format(url))
log.debug("MediaType: {0}".format(media_type))
pluginhandle = int(sys.argv[1])
settings = xbmcaddon.Addon()
@@ -71,7 +72,7 @@ def get_content(url, params):
elif media_type == "playlists":
view_type = "Playlists"
log.debug("media_type:{0} content_type:{1} view_type:{2} ", media_type, content_type, view_type)
log.debug("media_type:{0} content_type:{1} view_type:{2} ".format(media_type, content_type, view_type))
# show a progress indicator if needed
progress = None
@@ -88,22 +89,22 @@ def get_content(url, params):
if page_limit > 0 and media_type.startswith("movie"):
m = re.search('StartIndex=([0-9]{1,4})', url)
if m and m.group(1):
log.debug("UPDATING NEXT URL: {0}", url)
log.debug("UPDATING NEXT URL: {0}".format(url))
start_index = int(m.group(1))
log.debug("current_start : {0}", start_index)
log.debug("current_start : {0}".format(start_index))
if start_index > 0:
prev_index = start_index - page_limit
if prev_index < 0:
prev_index = 0
url_prev = re.sub('StartIndex=([0-9]{1,4})', 'StartIndex=' + str(prev_index), url)
url_next = re.sub('StartIndex=([0-9]{1,4})', 'StartIndex=' + str(start_index + page_limit), url)
log.debug("UPDATING NEXT URL: {0}", url_next)
log.debug("UPDATING NEXT URL: {0}".format(url_next))
else:
log.debug("ADDING NEXT URL: {0}", url)
log.debug("ADDING NEXT URL: {0}".format(url))
url_next = url + "&StartIndex=" + str(start_index + page_limit) + "&Limit=" + str(page_limit)
url = url + "&StartIndex=" + str(start_index) + "&Limit=" + str(page_limit)
log.debug("ADDING NEXT URL: {0}", url_next)
log.debug("ADDING NEXT URL: {0}".format(url_next))
# use the data manager to get the data
# result = dataManager.GetContent(url)
@@ -118,7 +119,7 @@ def get_content(url, params):
if dir_items is None:
return
log.debug("total_records: {0}", total_records)
log.debug("total_records: {0}".format(total_records))
# add paging items
if page_limit > 0 and media_type.startswith("movie"):
@@ -126,7 +127,7 @@ def get_content(url, params):
list_item = xbmcgui.ListItem("Prev Page (" + str(start_index - page_limit + 1) + "-" + str(start_index) +
" of " + str(total_records) + ")")
u = sys.argv[0] + "?url=" + urllib.quote(url_prev) + "&mode=GET_CONTENT&media_type=movies"
log.debug("ADDING PREV ListItem: {0} - {1}", u, list_item)
log.debug("ADDING PREV ListItem: {0} - {1}".format(u, list_item))
dir_items.insert(0, (u, list_item, True))
if start_index + page_limit < total_records:
@@ -136,7 +137,7 @@ def get_content(url, params):
list_item = xbmcgui.ListItem("Next Page (" + str(start_index + page_limit + 1) + "-" +
str(upper_count) + " of " + str(total_records) + ")")
u = sys.argv[0] + "?url=" + urllib.quote(url_next) + "&mode=GET_CONTENT&media_type=movies"
log.debug("ADDING NEXT ListItem: {0} - {1}", u, list_item)
log.debug("ADDING NEXT ListItem: {0} - {1}".format(u, list_item))
dir_items.append((u, list_item, True))
# set the Kodi content type
@@ -144,7 +145,7 @@ def get_content(url, params):
xbmcplugin.setContent(pluginhandle, content_type)
elif detected_type is not None:
# if the media type is not set then try to use the detected type
log.debug("Detected content type: {0}", detected_type)
log.debug("Detected content type: {0}".format(detected_type))
if detected_type == "Movie":
view_type = "Movies"
content_type = 'movies'
@@ -166,11 +167,11 @@ def get_content(url, params):
view_key = "view-" + content_type
view_id = settings.getSetting(view_key)
if view_id:
log.debug("Setting view for type:{0} to id:{1}", view_key, view_id)
log.debug("Setting view for type:{0} to id:{1}".format(view_key, view_id))
display_items_notification = {"view_id": view_id}
send_event_notification("set_view", display_items_notification)
else:
log.debug("No view id for view type:{0}", view_key)
log.debug("No view id for view type:{0}".format(view_key))
# send display items event
# display_items_notification = {"view_type": view_type}
@@ -185,7 +186,7 @@ def get_content(url, params):
def set_sort(pluginhandle, view_type, default_sort):
log.debug("SETTING_SORT for media type: {0}", view_type)
log.debug("SETTING_SORT for media type: {0}".format(view_type))
if default_sort == "none":
xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_UNSORTED)
@@ -202,7 +203,7 @@ def set_sort(pluginhandle, view_type, default_sort):
settings = xbmcaddon.Addon()
preset_sort_order = settings.getSetting("sort-" + view_type)
log.debug("SETTING_SORT preset_sort_order: {0}", preset_sort_order)
log.debug("SETTING_SORT preset_sort_order: {0}".format(preset_sort_order))
if preset_sort_order in sorting_order_mapping:
xbmcplugin.addSortMethod(pluginhandle, sorting_order_mapping[preset_sort_order])
@@ -311,7 +312,7 @@ def process_directory(url, progress, params, use_cache_data=False):
detected_type = item_details.item_type
if item_details.item_type == "Season" and first_season_item is None:
log.debug("Setting First Season to : {0}", item_details.__dict__)
log.debug("Setting First Season to : {0}".format(item_details.__dict__))
first_season_item = item_details
total_unwatched += item_details.unwatched_episodes
@@ -357,7 +358,7 @@ def process_directory(url, progress, params, use_cache_data=False):
if gui_item:
dir_items.append(gui_item)
else:
log.debug("Dropping empty folder item : {0}", item_details.__dict__)
log.debug("Dropping empty folder item : {0}".format(item_details.__dict__))
elif item_details.item_type == "MusicArtist":
u = ('{server}/Users/{userid}/items' +

View File

@@ -1,9 +1,10 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcgui
import xbmcaddon
import httplib
import requests
import hashlib
import ssl
import StringIO
@@ -13,14 +14,16 @@ from urlparse import urlparse
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
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .translation import string_load
from .tracking import timer
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
def save_user_details(settings, user_name, user_password):
@@ -104,16 +107,15 @@ class DownloadUtils:
self.use_https = False
if settings.getSetting('protocol') == "1":
self.use_https = True
log.debug("use_https: {0}", self.use_https)
log.debug("use_https: {0}".format(self.use_https))
self.verify_cert = settings.getSetting('verify_cert') == 'true'
log.debug("verify_cert: {0}", self.verify_cert)
log.debug("verify_cert: {0}".format(self.verify_cert))
def post_capabilities(self):
url = "{server}/Sessions/Capabilities/Full?format=json"
data = {
'IconUrl': "https://raw.githubusercontent.com/faush01/plugin.video.jellycon/develop/kodi.png",
'SupportsMediaControl': True,
'PlayableMediaTypes': ["Video", "Audio"],
'SupportedCommands': ["MoveUp",
@@ -151,7 +153,7 @@ class DownloadUtils:
}
self.download_url(url, post_body=data, method="POST")
log.debug("Posted Capabilities: {0}", data)
log.debug("Posted Capabilities: {0}".format(data))
def get_item_playback_info(self, item_id, force_transcode):
@@ -332,60 +334,24 @@ class DownloadUtils:
else:
url = "{server}/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate)
log.debug("PlaybackInfo : {0}", url)
log.debug("PlaybackInfo : {0}", profile)
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}", play_info_result)
log.debug("PlaybackInfo : {0}".format(play_info_result))
return play_info_result
def get_server(self):
settings = xbmcaddon.Addon()
host = settings.getSetting('ipaddress')
if len(host) == 0 or host == "<none>":
return None
#For migration from storing URL parts to just one URL
if settings.getSetting('ipaddress') != "" and settings.getSetting('ipaddress') != "&lt;none&gt;":
log.info("Migrating to new server url storage")
url = ("http://" if settings.getSetting('protocol') == "0" else "https://") + settings.getSetting('ipaddress') + ":" + settings.getSetting('port')
settings.setSetting('server_address', url)
settings.setSetting('ipaddress', "")
port = settings.getSetting('port')
if not port and self.use_https:
port = "443"
settings.setSetting("port", port)
elif not port:
port = "80"
settings.setSetting("port", port)
# if user entered a full path i.e. http://some_host:port
if host.lower().strip().startswith("http://") or host.lower().strip().startswith("https://"):
log.debug("Extracting host info from url: {0}", host)
url_bits = urlparse(host.strip())
if host.lower().strip().startswith("http://"):
settings.setSetting('protocol', '0')
self.use_https = False
elif host.lower().strip().startswith("https://"):
settings.setSetting('protocol', '1')
self.use_https = True
if url_bits.hostname is not None and len(url_bits.hostname) > 0:
host = url_bits.hostname
if url_bits.username and url_bits.password:
host = "%s:%s@" % (url_bits.username, url_bits.password) + host
settings.setSetting("ipaddress", host)
if url_bits.port is not None and url_bits.port > 0:
port = str(url_bits.port)
settings.setSetting("port", port)
if self.use_https:
server = "https://" + host + ":" + port
else:
server = "http://" + host + ":" + port
return server
return settings.getSetting('server_address')
@staticmethod
def get_all_artwork(item, server):
@@ -426,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:
@@ -454,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
@@ -519,8 +485,8 @@ class DownloadUtils:
userid = window.get_property("userid")
user_image = window.get_property("userimage")
if userid and user_image:
log.debug("JellyCon DownloadUtils -> Returning saved UserID: {0}", userid)
if userid:
log.debug("JellyCon DownloadUtils -> Returning saved UserID: {0}".format(userid))
return userid
settings = xbmcaddon.Addon()
@@ -529,33 +495,27 @@ class DownloadUtils:
if not user_name:
return ""
log.debug("Looking for user name: {0}", user_name)
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}", msg)
log.error("Get User unable to connect: {0}".format(msg))
return ""
log.debug("GETUSER_JSONDATA_01: {0}", 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}", e)
if not result:
return ""
if result is None:
return ""
log.debug("GETUSER_JSONDATA_02: {0}", result)
log.debug("GETUSER_JSONDATA_02: {0}".format(result))
secure = False
for user in result:
if user.get("Name") == unicode(user_name, "utf-8"):
userid = user.get("Id")
user_image = self.get_user_artwork(user, 'Primary')
log.debug("Username Found: {0}", user.get("Name"))
log.debug("Username Found: {0}".format(user.get("Name")))
if user.get("HasPassword", False):
secure = True
log.debug("Username Is Secure (HasPassword=True)")
@@ -579,7 +539,7 @@ class DownloadUtils:
string_load(30045),
icon="special://home/addons/plugin.video.jellycon/icon.png")
log.debug("userid: {0}", userid)
log.debug("userid: {0}".format(userid))
window.set_property("userid", userid)
window.set_property("userimage", user_image)
@@ -592,14 +552,11 @@ class DownloadUtils:
token = window.get_property("AccessToken")
if token is not None and token != "":
log.debug("JellyCon DownloadUtils -> Returning saved AccessToken: {0}", token)
log.debug("JellyCon DownloadUtils -> Returning saved AccessToken: {0}".format(token))
return token
settings = xbmcaddon.Addon()
port = settings.getSetting("port")
host = settings.getSetting("ipaddress")
if host is None or host == "" or port is None or port == "":
return ""
server_address = settings.getSetting("server_address")
url = "{server}/Users/AuthenticateByName?format=json"
@@ -607,24 +564,19 @@ 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}", 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}", access_token)
log.debug("User Id: {0}", userid)
log.debug("User Authenticated: {0}".format(access_token))
log.debug("User Id: {0}".format(userid))
window.set_property("AccessToken", access_token)
window.set_property("userid", userid)
# WINDOW.setProperty("userimage", "")
@@ -660,27 +612,24 @@ class DownloadUtils:
if authenticate is False:
auth_string = "MediaBrowser Client=\"" + client + "\",Device=\"" + device_name + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
# headers["Authorization"] = authString
headers['X-Emby-Authorization'] = auth_string
return headers
else:
userid = self.get_user_id()
auth_string = "MediaBrowser UserId=\"" + userid + "\",Client=\"" + client + "\",Device=\"" + device_name + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
# headers["Authorization"] = authString
headers['X-Emby-Authorization'] = auth_string
auth_token = self.authenticate()
if auth_token != "":
headers["X-MediaBrowser-Token"] = auth_token
log.debug("JellyCon Authentication Header: {0}", headers)
log.debug("JellyCon Authentication Header: {0}".format(headers))
return headers
@timer
def download_url(self, url, suppress=False, post_body=None, method="GET", authenticate=True, headers=None):
log.debug("downloadUrl")
return_data = "null"
settings = xbmcaddon.Addon()
user_details = load_user_details(settings)
username = user_details.get("username", "")
@@ -689,23 +638,23 @@ class DownloadUtils:
http_timeout = int(settings.getSetting("http_timeout"))
if authenticate and username == "":
return return_data
return {}
if settings.getSetting("suppressErrors") == "true":
suppress = True
log.debug("Before: {0}", url)
log.debug("Before: {0}".format(url))
if url.find("{server}") != -1:
server = self.get_server()
if server is None:
return return_data
return {}
url = url.replace("{server}", server)
if url.find("{userid}") != -1:
userid = self.get_user_id()
if not userid:
return return_data
return {}
url = url.replace("{userid}", userid)
if url.find("{ItemLimit}") != -1:
@@ -720,120 +669,75 @@ class DownloadUtils:
home_window = HomeWindow()
random_movies = home_window.get_property("random-movies")
if not random_movies:
return return_data
return {}
url = url.replace("{random_movies}", random_movies)
log.debug("After: {0}", url)
conn = None
log.debug("After: {0}".format(url))
try:
url_bits = urlparse(url.strip())
protocol = url_bits.scheme
host_name = url_bits.hostname
port = url_bits.port
user_name = url_bits.username
user_password = url_bits.password
url_path = url_bits.path
url_puery = url_bits.query
if not host_name or host_name == "<none>":
return return_data
local_use_https = False
if protocol.lower() == "https":
local_use_https = True
server = "%s:%s" % (host_name, port)
url_path = url_path + "?" + url_puery
if local_use_https and self.verify_cert:
log.debug("Connection: HTTPS, Cert checked")
conn = httplib.HTTPSConnection(server, timeout=http_timeout)
elif local_use_https and not self.verify_cert:
log.debug("Connection: HTTPS, Cert NOT checked")
conn = httplib.HTTPSConnection(server, timeout=http_timeout, context=ssl._create_unverified_context())
else:
log.debug("Connection: HTTP")
conn = httplib.HTTPConnection(server, timeout=http_timeout)
head = self.get_auth_header(authenticate)
if user_name and user_password:
log.info("Replacing username & Password info")
# add basic auth headers
user_and_pass = b64encode(b"%s:%s" % (user_name, user_password)).decode("ascii")
head["Authorization"] = 'Basic %s' % user_and_pass
head["User-Agent"] = "JellyCon-" + ClientInformation().get_version()
log.debug("HEADERS: {0}", head)
if post_body is not None:
http_request = getattr(requests, method.lower())
if post_body:
if isinstance(post_body, dict):
content_type = "application/json"
head["Content-Type"] = "application/json"
post_body = json.dumps(post_body)
else:
content_type = "application/x-www-form-urlencoded"
head["Content-Type"] = "application/x-www-form-urlencoded"
head["Content-Type"] = content_type
log.debug("Content-Type: {0}", content_type)
log.debug("Content-Type: {0}".format(head["Content-Type"]))
log.debug("POST DATA: {0}".format(post_body))
log.debug("POST DATA: {0}", post_body)
conn.request(method=method, url=url_path, body=post_body, headers=head)
data = http_request(url, data=post_body, headers=head)
else:
conn.request(method=method, url=url_path, headers=head)
data = http_request(url, headers=head)
data = conn.getresponse()
log.debug("HTTP response: {0} {1}", data.status, data.reason)
log.debug("GET URL HEADERS: {0}", data.getheaders())
if int(data.status) == 200:
ret_data = data.read()
content_type = data.getheader('content-encoding')
log.debug("Data Len Before: {0}", len(ret_data))
if content_type == "gzip":
ret_data = StringIO.StringIO(ret_data)
gzipper = gzip.GzipFile(fileobj=ret_data)
return_data = gzipper.read()
else:
return_data = ret_data
if data.status_code == 200:
if headers is not None and isinstance(headers, dict):
headers.update(data.getheaders())
log.debug("Data Len After: {0}", len(return_data))
log.debug("====== 200 returned =======")
log.debug("Content-Type: {0}", content_type)
log.debug("{0}", return_data)
log.debug("====== 200 finished ======")
headers.update(data.headers)
log.debug("{0}".format(data.json()))
elif int(data.status) >= 400:
elif data.status_code >= 400:
if int(data.status) == 401:
if data.status_code == 401:
# remove any saved password
m = hashlib.md5()
m.update(username)
hashed_username = m.hexdigest()
log.error("HTTP response error 401 auth error, removing any saved passwords for user: {0}", hashed_username)
log.error("HTTP response error 401 auth error, removing any saved passwords for user: {0}".format(hashed_username))
settings.setSetting("saved_user_password_" + hashed_username, "")
save_user_details(settings, "", "")
log.error("HTTP response error: {0} {1}", data.status, data.reason)
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.reason),
string_load(30200) % str(data.content),
icon="special://home/addons/plugin.video.jellycon/icon.png")
try:
result = data.json()
except:
result = {}
return result
except Exception as msg:
log.error("Unable to connect to {0} : {1}", server, msg)
if suppress is False:
log.error("{0}".format(format_exc()))
log.error("Unable to connect to {0} : {1}".format(server, msg))
if not suppress:
xbmcgui.Dialog().notification(string_load(30316),
str(msg),
icon="special://home/addons/plugin.video.jellycon/icon.png")
finally:
try:
log.debug("Closing HTTP connection: {0}", conn)
conn.close()
except:
pass
return return_data

View File

@@ -90,10 +90,6 @@ import sys
import time
import errno
# from .simple_logging import SimpleLogging
# log = SimpleLogging(__name__)
class FileLock(object):
""" A file locking mechanism that has context-manager support so
you can use it in a ``with`` statement. This should be relatively cross
@@ -201,7 +197,7 @@ if __name__ == "__main__":
import threading
import tempfile
from builtins import range
temp_dir = tempfile.mkdtemp()
protected_filepath = os.path.join(temp_dir, "somefile.txt")
@@ -236,4 +232,4 @@ if __name__ == "__main__":
# Please manually inspect the output. Does it look like the operations were atomic?
with open(protected_filepath, "r") as f:
sys.stdout.write(f.read())
"""
"""

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
@@ -20,7 +21,7 @@ from .kodi_utils import HomeWindow
from .clientinfo import ClientInformation
from .datamanager import DataManager, clear_cached_server_data
from .server_detect import check_server, check_connection_speed
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .menu_functions import display_main_menu, display_menu, show_movie_alpha_list, show_tvshow_alpha_list, show_genre_list, show_search, show_movie_pages
from .translation import string_load
from .server_sessions import show_server_sessions
@@ -39,7 +40,7 @@ __addondir__ = xbmc.translatePath(__addon__.getAddonInfo('profile'))
__cwd__ = __addon__.getAddonInfo('path')
PLUGINPATH = xbmc.translatePath(os.path.join(__cwd__))
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
@@ -60,14 +61,14 @@ def main_entry_point():
pr = cProfile.Profile()
pr.enable()
log.debug("Running Python: {0}", sys.version_info)
log.debug("Running JellyCon: {0}", ClientInformation().get_version())
log.debug("Kodi BuildVersion: {0}", xbmc.getInfoLabel("System.BuildVersion"))
log.debug("Kodi Version: {0}", kodi_version)
log.debug("Script argument data: {0}", sys.argv)
log.debug("Running Python: {0}".format(sys.version_info))
log.debug("Running JellyCon: {0}".format(ClientInformation().get_version()))
log.debug("Kodi BuildVersion: {0}".format(xbmc.getInfoLabel("System.BuildVersion")))
log.debug("Kodi Version: {0}".format(kodi_version))
log.debug("Script argument data: {0}".format(sys.argv))
params = get_params()
log.debug("Script params: {0}", params)
log.debug("Script params: {0}".format(params))
request_path = params.get("request_path", None)
param_url = params.get('url', None)
@@ -148,8 +149,8 @@ def main_entry_point():
else:
log.info("Unable to find TV show parent ID.")
else:
log.debug("JellyCon -> Mode: {0}", mode)
log.debug("JellyCon -> URL: {0}", param_url)
log.debug("JellyCon -> Mode: {0}".format(mode))
log.debug("JellyCon -> URL: {0}".format(param_url))
if mode == "GET_CONTENT":
get_content(param_url, params)
@@ -189,7 +190,7 @@ def __get_parent_id_from(params):
result = None
show_provider_ids = params.get("show_ids")
if show_provider_ids is not None:
log.debug("TV show providers IDs: {}", show_provider_ids)
log.debug("TV show providers IDs: {}".format(show_provider_ids))
get_show_url = "{server}/Users/{userid}/Items?fields=MediaStreams&Recursive=true" \
"&IncludeItemTypes=series&IncludeMedia=true&ImageTypeLimit=1&Limit=16" \
"&AnyProviderIdEquals=" + show_provider_ids
@@ -198,21 +199,21 @@ def __get_parent_id_from(params):
if len(show) == 1:
result = content.get("Items")[0].get("Id")
else:
log.debug("TV show not found for ids: {}", show_provider_ids)
log.debug("TV show not found for ids: {}".format(show_provider_ids))
else:
log.error("TV show parameter not found in request.")
return result
def toggle_watched(params):
log.debug("toggle_watched: {0}", params)
log.debug("toggle_watched: {0}".format(params))
item_id = params.get("item_id", None)
if item_id is None:
return
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("toggle_watched item info: {0}", result)
log.debug("toggle_watched item info: {0}".format(result))
user_data = result.get("UserData", None)
if user_data is None:
@@ -225,35 +226,35 @@ def toggle_watched(params):
def mark_item_watched(item_id):
log.debug("Mark Item Watched: {0}", item_id)
log.debug("Mark Item Watched: {0}".format(item_id))
url = "{server}/Users/{userid}/PlayedItems/" + item_id
downloadUtils.download_url(url, post_body="", method="POST")
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
if last_url:
log.debug("markWatched_lastUrl: {0}", last_url)
log.debug("markWatched_lastUrl: {0}".format(last_url))
home_window.set_property("skip_cache_for_" + last_url, "true")
xbmc.executebuiltin("Container.Refresh")
def mark_item_unwatched(item_id):
log.debug("Mark Item UnWatched: {0}", item_id)
log.debug("Mark Item UnWatched: {0}".format(item_id))
url = "{server}/Users/{userid}/PlayedItems/" + item_id
downloadUtils.download_url(url, method="DELETE")
check_for_new_content()
home_window = HomeWindow()
last_url = home_window.get_property("last_content_url")
if last_url:
log.debug("markUnwatched_lastUrl: {0}", last_url)
log.debug("markUnwatched_lastUrl: {0}".format(last_url))
home_window.set_property("skip_cache_for_" + last_url, "true")
xbmc.executebuiltin("Container.Refresh")
def mark_item_favorite(item_id):
log.debug("Add item to favourites: {0}", item_id)
log.debug("Add item to favourites: {0}".format(item_id))
url = "{server}/Users/{userid}/FavoriteItems/" + item_id
downloadUtils.download_url(url, post_body="", method="POST")
check_for_new_content()
@@ -266,7 +267,7 @@ def mark_item_favorite(item_id):
def unmark_item_favorite(item_id):
log.debug("Remove item from favourites: {0}", item_id)
log.debug("Remove item from favourites: {0}".format(item_id))
url = "{server}/Users/{userid}/FavoriteItems/" + item_id
downloadUtils.download_url(url, method="DELETE")
check_for_new_content()
@@ -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", "")
@@ -304,7 +304,7 @@ def delete(item_id):
return_value = xbmcgui.Dialog().yesno(string_load(30091), final_name, string_load(30092))
if return_value:
log.debug('Deleting Item: {0}', item_id)
log.debug('Deleting Item: {0}'.format(item_id))
url = '{server}/Items/' + item_id
progress = xbmcgui.DialogProgress()
progress.create(string_load(30052), string_load(30053))
@@ -324,8 +324,8 @@ def get_params():
plugin_path = sys.argv[0]
paramstring = sys.argv[2]
log.debug("Parameter string: {0}", paramstring)
log.debug("Plugin Path string: {0}", plugin_path)
log.debug("Parameter string: {0}".format(paramstring))
log.debug("Plugin Path string: {0}".format(plugin_path))
param = {}
@@ -348,12 +348,12 @@ def get_params():
elif (len(splitparams)) == 3:
param[splitparams[0]] = splitparams[1] + "=" + splitparams[2]
log.debug("JellyCon -> Detected parameters: {0}", param)
log.debug("JellyCon -> Detected parameters: {0}".format(param))
return param
def show_menu(params):
log.debug("showMenu(): {0}", params)
log.debug("showMenu(): {0}".format(params))
home_window = HomeWindow()
settings = xbmcaddon.Addon()
@@ -362,7 +362,7 @@ def show_menu(params):
url = "{server}/Users/{userid}/Items/" + item_id + "?format=json"
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("Menu item info: {0}", result)
log.debug("Menu item info: {0}".format(result))
if result is None:
return
@@ -466,15 +466,15 @@ def show_menu(params):
view_key = "view-" + container_content_type
current_default_view = settings.getSetting(view_key)
view_match = container_view_id == current_default_view
log.debug("View ID:{0} Content type:{1}", container_view_id, container_content_type)
log.debug("View ID:{0} Content type:{1}".format(container_view_id, container_content_type))
if container_content_type in ["movies", "tvshows", "seasons", "episodes", "sets"]:
if view_match:
li = xbmcgui.ListItem("Unset as defalt view")
li = xbmcgui.ListItem("Unset as default view")
li.setProperty('menu_id', 'unset_view')
action_items.append(li)
else:
li = xbmcgui.ListItem("Set as defalt view")
li = xbmcgui.ListItem("Set as default view")
li.setProperty('menu_id', 'set_view')
action_items.append(li)
@@ -487,7 +487,7 @@ def show_menu(params):
selected_action = ""
if selected_action_item is not None:
selected_action = selected_action_item.getProperty('menu_id')
log.debug("Menu Action Selected: {0}", selected_action)
log.debug("Menu Action Selected: {0}".format(selected_action))
del action_menu
if selected_action == "play":
@@ -498,11 +498,11 @@ def show_menu(params):
play_action(params)
elif selected_action == "set_view":
log.debug("Settign view type for {0} to {1}", view_key, container_view_id)
log.debug("Settign view type for {0} to {1}".format(view_key, container_view_id))
settings.setSetting(view_key, container_view_id)
elif selected_action == "unset_view":
log.debug("Un-Settign view type for {0} to {1}", view_key, container_view_id)
log.debug("Un-Settign view type for {0} to {1}".format(view_key, container_view_id))
settings.setSetting(view_key, "")
elif selected_action == "refresh_server":
@@ -513,7 +513,7 @@ def show_menu(params):
"&ReplaceAllImages=true" +
"&ReplaceAllMetadata=true")
res = downloadUtils.download_url(url, post_body="", method="POST")
log.debug("Refresh Server Responce: {0}", res)
log.debug("Refresh Server Responce: {0}".format(res))
elif selected_action == "hide":
user_details = load_user_details(settings)
@@ -522,13 +522,13 @@ def show_menu(params):
url = "{server}/Items/" + item_id + "/Tags/Add"
post_tag_data = {"Tags": [{"Name": hide_tag_string}]}
res = downloadUtils.download_url(url, post_body=post_tag_data, method="POST")
log.debug("Add Tag Responce: {0}", res)
log.debug("Add Tag Responce: {0}".format(res))
check_for_new_content()
last_url = home_window.get_property("last_content_url")
if last_url:
log.debug("markUnwatched_lastUrl: {0}", last_url)
log.debug("markUnwatched_lastUrl: {0}".format(last_url))
home_window.set_property("skip_cache_for_" + last_url, "true")
xbmc.executebuiltin("Container.Refresh")
@@ -549,7 +549,7 @@ def show_menu(params):
bitrate_dialog.doModal()
selected_transcode_value = bitrate_dialog.selected_transcode_value
del bitrate_dialog
log.debug("selected_transcode_value: {0}", selected_transcode_value)
log.debug("selected_transcode_value: {0}".format(selected_transcode_value))
if selected_transcode_value > 0:
settings.setSetting("force_max_stream_bitrate", str(selected_transcode_value))
@@ -577,11 +577,10 @@ 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}", result)
log.debug("Safe_Delete_Action: {0}".format(result))
action_token = result["action_token"]
message = "You are about to delete the following item[CR][CR]"
@@ -611,7 +610,7 @@ def show_menu(params):
confirm_dialog.message = message
confirm_dialog.heading = "Confirm delete files?"
confirm_dialog.doModal()
log.debug("safe_delete_confirm_dialog: {0}", confirm_dialog.confirm)
log.debug("safe_delete_confirm_dialog: {0}".format(confirm_dialog.confirm))
if confirm_dialog.confirm:
url = "{server}/jellyfin_safe_delete/delete_item_action"
@@ -620,12 +619,11 @@ def show_menu(params):
'action_token': action_token
}
delete_action = downloadUtils.download_url(url, method="POST", post_body=playback_info)
log.debug("Delete result action: {0}", delete_action)
delete_action_result = json.loads(delete_action)
if not delete_action_result:
dialog.ok("Error", "Error deleteing files", "Error in responce from server")
elif not delete_action_result["result"]:
dialog.ok("Error", "Error deleteing files", delete_action_result["message"])
log.debug("Delete result action: {0}".format(delete_action))
if not delete_action:
dialog.ok("Error", "Error deleting files", "Error in responce from server")
elif not delete_action.get("result"):
dialog.ok("Error", "Error deleting files", delete_action["message"])
else:
dialog.ok("Deleted", "Files deleted")
else:
@@ -686,29 +684,11 @@ def show_menu(params):
def populate_listitem(item_id):
log.debug("populate_listitem: {0}", 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)
log.debug("populate_listitem item info: {0}", result)
'''
server = downloadUtils.getServer()
gui_options = {}
gui_options["server"] = server
gui_options["name_format"] = None
gui_options["name_format_type"] = None
details, extraData = extract_item_info(result,gui_options )
u, list_item, folder = add_gui_item(result["Id"], details, extraData, {}, folder=False)
log.debug("list_item path: {0}", u)
#list_item.setProperty('IsPlayable', 'false')
#list_item.setPath(u)
'''
result = downloadUtils.download_url(url)
log.debug("populate_listitem item info: {0}".format(result))
item_title = result.get("Name", string_load(30280))
@@ -738,7 +718,7 @@ def populate_listitem(item_id):
def show_content(params):
log.debug("showContent Called: {0}", params)
log.debug("showContent Called: {0}".format(params))
item_type = params.get("item_type")
settings = xbmcaddon.Addon()
@@ -760,7 +740,7 @@ def show_content(params):
"&IsVirtualUnaired=false" +
"&IncludeItemTypes=" + item_type)
log.debug("showContent Content Url: {0}", content_url)
log.debug("showContent Content Url: {0}".format(content_url))
get_content(content_url, params)
@@ -776,28 +756,16 @@ def search_results_person(params):
'&Fields={field_filters}' +
'&format=json')
'''
details_result = dataManager.GetContent(details_url)
log.debug("Search Results Details: {0}", details_result)
if details_result:
items = details_result.get("Items")
found_types = set()
for item in items:
found_types.add(item.get("Type"))
log.debug("search_results_person found_types: {0}", found_types)
'''
params["name_format"] = "Episode|episode_name_format"
dir_items, detected_type, total_records = process_directory(details_url, None, params)
log.debug('search_results_person results: {0}', dir_items)
log.debug('search_results_person detect_type: {0}', detected_type)
log.debug('search_results_person results: {0}'.format(dir_items))
log.debug('search_results_person detect_type: {0}'.format(detected_type))
if detected_type is not None:
# if the media type is not set then try to use the detected type
log.debug("Detected content type: {0}", detected_type)
log.debug("Detected content type: {0}".format(detected_type))
content_type = None
if detected_type == "Movie":
@@ -825,9 +793,9 @@ def search_results(params):
item_type = params.get('item_type')
query_string = params.get('query')
if query_string:
log.debug("query_string : {0}", query_string)
log.debug("query_string : {0}".format(query_string))
query_string = urllib.unquote(query_string)
log.debug("query_string : {0}", query_string)
log.debug("query_string : {0}".format(query_string))
item_type = item_type.lower()
@@ -867,14 +835,14 @@ def search_results(params):
return
home_window.set_property("last_search", user_input)
log.debug('searchResults Called: {0}', params)
log.debug('searchResults Called: {0}'.format(params))
query = user_input
else:
query = query_string
query = urllib.quote(query)
log.debug("query : {0}", query)
log.debug("query : {0}".format(query))
if (not item_type) or (not query):
return
@@ -904,7 +872,7 @@ def search_results(params):
"&userId={userid}")
person_search_results = dataManager.get_content(search_url)
log.debug("Person Search Result : {0}", person_search_results)
log.debug("Person Search Result : {0}".format(person_search_results))
if person_search_results is None:
return
@@ -967,23 +935,30 @@ def search_results(params):
def play_action(params):
log.debug("== ENTER: PLAY ==")
log.debug("PLAY ACTION PARAMS: {0}", params)
log.debug("PLAY ACTION PARAMS: {0}".format(params))
item_id = params.get("item_id")
auto_resume = int(params.get("auto_resume", "-1"))
log.debug("AUTO_RESUME: {0}", auto_resume)
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
log.debug("FORCE_TRANSCODE: {0}", force_transcode)
log.debug("FORCE_TRANSCODE: {0}".format(force_transcode))
media_source_id = params.get("media_source_id", "")
log.debug("media_source_id: {0}", media_source_id)
log.debug("media_source_id: {0}".format(media_source_id))
subtitle_stream_index = params.get("subtitle_stream_index")
log.debug("subtitle_stream_index: {0}", subtitle_stream_index)
log.debug("subtitle_stream_index: {0}".format(subtitle_stream_index))
audio_stream_index = params.get("audio_stream_index")
log.debug("audio_stream_index: {0}", audio_stream_index)
log.debug("audio_stream_index: {0}".format(audio_stream_index))
action = params.get("action", "play")
@@ -1001,7 +976,7 @@ def play_action(params):
play_info["media_source_id"] = media_source_id
play_info["subtitle_stream_index"] = subtitle_stream_index
play_info["audio_stream_index"] = audio_stream_index
log.info("Sending jellycon_play_action : {0}", play_info)
log.info("Sending jellycon_play_action : {0}".format(play_info))
send_event_notification("jellycon_play_action", play_info)
@@ -1010,13 +985,12 @@ 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
log.debug("LocalTrailers {0}", result)
log.debug("LocalTrailers {0}".format(result))
count = 1
trailer_names = []
@@ -1035,9 +1009,8 @@ 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)
log.debug("RemoteTrailers: {0}", result)
result = downloadUtils.download_url(url)
log.debug("RemoteTrailers: {0}".format(result))
count = 1
if result is None:
@@ -1058,7 +1031,7 @@ def play_item_trailer(item_id):
trailer_names.append(name)
trailer_list.append(info)
log.debug("TrailerList: {0}", trailer_list)
log.debug("TrailerList: {0}".format(trailer_list))
trailer_text = []
for trailer in trailer_list:
@@ -1069,7 +1042,7 @@ def play_item_trailer(item_id):
resp = dialog.select(string_load(30308), trailer_text)
if resp > -1:
trailer = trailer_list[resp]
log.debug("SelectedTrailer: {0}", trailer)
log.debug("SelectedTrailer: {0}".format(trailer))
if trailer.get("type") == "local":
params = {}
@@ -1079,7 +1052,7 @@ def play_item_trailer(item_id):
elif trailer.get("type") == "remote":
youtube_id = trailer.get("url").rsplit('=', 1)[1]
youtube_plugin = "RunPlugin(plugin://plugin.video.youtube/play/?video_id=%s)" % youtube_id
log.debug("youtube_plugin: {0}", youtube_plugin)
log.debug("youtube_plugin: {0}".format(youtube_plugin))
# play_info = {}
# play_info["url"] = youtube_plugin

View File

@@ -1,3 +1,4 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcvfs
import xbmc
@@ -7,11 +8,11 @@ from urlparse import urlparse
from random import shuffle
import threading
import httplib
import requests
import io
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .datamanager import DataManager
from .downloadutils import DownloadUtils
from .utils import get_art
@@ -24,7 +25,7 @@ except Exception as err:
pil_loaded = False
PORT_NUMBER = 24276
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
def get_image_links(url):
@@ -78,7 +79,7 @@ def get_image_links(url):
def build_image(path):
log.debug("build_image()")
log.debug("Request Path : {0}", path)
log.debug("Request Path : {0}".format(path))
request_path = path[1:]
@@ -86,7 +87,7 @@ def build_image(path):
return []
decoded_url = base64.b64decode(request_path)
log.debug("decoded_url : {0}", decoded_url)
log.debug("decoded_url : {0}".format(decoded_url))
image_urls = get_image_links(decoded_url)
@@ -116,13 +117,11 @@ def build_image(path):
server = "%s:%s" % (host_name, port)
url_full_path = url_path + "?" + url_query
log.debug("Loading image from : {0} {1} {2}", image_count, server, url_full_path)
log.debug("Loading image from : {0} {1} {2}".format(image_count, server, url_full_path))
try:
conn = httplib.HTTPConnection(server)
conn.request("GET", url_full_path)
image_responce = conn.getresponse()
image_data = image_responce.read()
image_response = requests.get(thumb_url)
image_data = image_response.content
loaded_image = Image.open(io.BytesIO(image_data))
image = ImageOps.fit(loaded_image, size, method=Image.ANTIALIAS, bleed=0.0, centering=(0.5, 0.5))
@@ -136,7 +135,7 @@ def build_image(path):
del image_data
except Exception as con_err:
log.debug("Error loading image : {0}", str(con_err))
log.debug("Error loading image : {0}".format(con_err))
image_count += 1
@@ -170,12 +169,6 @@ class HttpImageHandler(BaseHTTPRequestHandler):
self.end_headers()
return
def do_QUIT(self):
log.debug("HttpImageHandler:do_QUIT()")
self.send_response(200)
self.end_headers()
return
def serve_image(self):
if pil_loaded:
@@ -211,14 +204,8 @@ class HttpImageServerThread(threading.Thread):
threading.Thread.__init__(self)
def stop(self):
self.keep_running = False
log.debug("HttpImageServerThread:stop called")
try:
conn = httplib.HTTPConnection("localhost:%d" % PORT_NUMBER)
conn.request("QUIT", "/")
conn.getresponse()
except:
pass
self.keep_running = False
def run(self):
log.debug("HttpImageServerThread:started")

View File

@@ -1,3 +1,4 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import sys
import os
@@ -12,11 +13,11 @@ import xbmcaddon
import xbmcgui
from .utils import get_art, datetime_from_string
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .downloadutils import DownloadUtils
from .kodi_utils import HomeWindow
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
addon_instance = xbmcaddon.Addon()
@@ -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,34 +154,34 @@ 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:
name_info["SeriesName"] = ""
name_info["SeasonIndex"] = u"%02d" % item_details.season_number
name_info["EpisodeIndex"] = u"%02d" % item_details.episode_number
log.debug("FormatName: {0} | {1}", name_format, name_info)
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
@@ -6,9 +8,9 @@ import xbmcaddon
import sys
import json
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
addon = xbmcaddon.Addon()
@@ -59,9 +61,9 @@ def get_kodi_version():
result = result.get("result")
version_data = result.get("version")
version = float(str(version_data.get("major")) + "." + str(version_data.get("minor")))
log.debug("Version: {0} - {1}", version, version_data)
log.debug("Version: {0} - {1}".format(version, version_data))
except:
version = 0.0
log.error("Version Error : RAW Version Data: {0}", result)
log.error("Version Error : RAW Version Data: {0}".format(result))
return version

View File

@@ -1,13 +1,15 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import threading
import time
import xbmc
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .widgets import check_for_new_content
from .tracking import timer
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class LibraryChangeMonitor(threading.Thread):

141
resources/lib/loghandler.py Normal file
View File

@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals
##################################################################################################
import os
import logging
import sys
import traceback
from six import ensure_text
from kodi_six import xbmc, xbmcaddon
from urlparse import urlparse
##################################################################################################
__addon__ = xbmcaddon.Addon(id='plugin.video.jellycon')
__pluginpath__ = xbmc.translatePath(__addon__.getAddonInfo('path'))
##################################################################################################
def getLogger(name=None):
if name is None:
return __LOGGER
return __LOGGER.getChild(name)
class LogHandler(logging.StreamHandler):
def __init__(self):
logging.StreamHandler.__init__(self)
self.setFormatter(MyFormatter())
self.sensitive = {'Token': [], 'Server': []}
settings = xbmcaddon.Addon()
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)
# Hide server URL in logs
string = string.replace(self.server or "{server}", "{jellyfin-server}")
xbmc.log(string, level=xbmc.LOGNOTICE)
def _get_log_level(self, level):
levels = {
logging.ERROR: 0,
logging.WARNING: 0,
logging.INFO: 1,
logging.DEBUG: 2
}
if self.debug == 'true':
log_level = 2
else:
log_level = 1
return log_level >= levels[level]
class MyFormatter(logging.Formatter):
def __init__(self, fmt='%(name)s -> %(levelname)s::%(relpath)s:%(lineno)s %(message)s'):
logging.Formatter.__init__(self, fmt)
def format(self, record):
if record.pathname:
record.pathname = ensure_text(record.pathname, get_filesystem_encoding())
self._gen_rel_path(record)
# Call the original formatter class to do the grunt work
result = logging.Formatter.format(self, record)
return result
def formatException(self, exc_info):
_pluginpath_real = os.path.realpath(__pluginpath__)
res = []
for o in traceback.format_exception(*exc_info):
o = ensure_text(o, get_filesystem_encoding())
if o.startswith(' File "'):
# If this split can't handle your file names, you should seriously consider renaming your files.
fn = o.split(' File "', 2)[1].split('", line ', 1)[0]
rfn = os.path.realpath(fn)
if rfn.startswith(_pluginpath_real):
o = o.replace(fn, os.path.relpath(rfn, _pluginpath_real))
res.append(o)
return ''.join(res)
def _gen_rel_path(self, record):
if record.pathname:
record.relpath = os.path.relpath(record.pathname, __pluginpath__)
class LazyLogger(object):
"""`helper.loghandler.getLogger()` is used everywhere.
This class helps avoiding import errors.
"""
__logger = None
__logger_name = None
def __init__(self, logger_name=None):
self.__logger_name = logger_name
def __getattr__(self, name):
if self.__logger is None:
self.__logger = getLogger(self.__logger_name)
return getattr(self.__logger, name)
def get_filesystem_encoding():
enc = sys.getfilesystemencoding()
if not enc:
enc = sys.getdefaultencoding()
if not enc or enc == 'ascii':
enc = 'utf-8'
return enc
__LOGGER = logging.getLogger('JELLYFIN')
for handler in __LOGGER.handlers:
__LOGGER.removeHandler(handler)
__LOGGER.addHandler(LogHandler())
__LOGGER.setLevel(logging.DEBUG)

View File

@@ -1,29 +1,30 @@
# 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
from .downloadutils import DownloadUtils
from .kodi_utils import add_menu_directory_item, HomeWindow
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .translation import string_load
from .datamanager import DataManager
from .utils import get_art, get_jellyfin_url
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
downloadUtils = DownloadUtils()
__addon__ = xbmcaddon.Addon()
def show_movie_tags(menu_params):
log.debug("show_movie_tags: {0}", menu_params)
log.debug("show_movie_tags: {0}".format(menu_params))
parent_id = menu_params.get("parent_id")
url_params = {}
@@ -50,7 +51,7 @@ def show_movie_tags(menu_params):
tags = result.get("Items")
log.debug("Tags : {0}", result)
log.debug("Tags : {0}".format(result))
for tag in tags:
name = tag["Name"]
@@ -80,14 +81,14 @@ def show_movie_tags(menu_params):
content_url +
"&mode=GET_CONTENT" +
"&media_type=movies")
log.debug("addMenuDirectoryItem: {0} - {1}", name, url)
log.debug("addMenuDirectoryItem: {0} - {1}".format(name, url))
add_menu_directory_item(name, url, art=art)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def show_movie_years(menu_params):
log.debug("show_movie_years: {0}", menu_params)
log.debug("show_movie_years: {0}".format(menu_params))
parent_id = menu_params.get("parent_id")
group_into_decades = menu_params.get("group") == "true"
@@ -166,14 +167,14 @@ def show_movie_years(menu_params):
content_url +
"&mode=GET_CONTENT" +
"&media_type=movies")
log.debug("addMenuDirectoryItem: {0} - {1}", name, url)
log.debug("addMenuDirectoryItem: {0} - {1}".format(name, url))
add_menu_directory_item(name, url, art=art)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def show_movie_pages(menu_params):
log.debug("showMoviePages: {0}", menu_params)
log.debug("showMoviePages: {0}".format(menu_params))
parent_id = menu_params.get("parent_id")
settings = xbmcaddon.Addon()
@@ -199,7 +200,7 @@ def show_movie_pages(menu_params):
return
total_results = result.get("TotalRecordCount", 0)
log.debug("showMoviePages TotalRecordCount {0}", total_results)
log.debug("showMoviePages TotalRecordCount {0}".format(total_results))
if result == 0:
return
@@ -250,14 +251,14 @@ def show_movie_pages(menu_params):
url = sys.argv[0] + ("?url=" + content_url +
"&mode=GET_CONTENT" +
"&media_type=" + collection["media_type"])
log.debug("addMenuDirectoryItem: {0} - {1} - {2}", collection.get('title'), url, collection.get("art"))
log.debug("addMenuDirectoryItem: {0} - {1} - {2}".format(collection.get('title'), url, collection.get("art")))
add_menu_directory_item(collection.get('title', string_load(30250)), url, art=collection.get("art"))
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def show_genre_list(menu_params):
log.debug("showGenreList: {0}", menu_params)
log.debug("showGenreList: {0}".format(menu_params))
server = downloadUtils.get_server()
if server is None:
@@ -331,7 +332,7 @@ def show_genre_list(menu_params):
url = sys.argv[0] + ("?url=" + urllib.quote(collection['path']) +
"&mode=GET_CONTENT" +
"&media_type=" + collection["media_type"])
log.debug("addMenuDirectoryItem: {0} - {1} - {2}", collection.get('title'), url, collection.get("art"))
log.debug("addMenuDirectoryItem: {0} - {1} - {2}".format(collection.get('title'), url, collection.get("art")))
add_menu_directory_item(collection.get('title', string_load(30250)), url, art=collection.get("art"))
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@@ -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
@@ -407,7 +398,7 @@ def show_movie_alpha_list(menu_params):
for collection in collections:
url = (sys.argv[0] + "?url=" + urllib.quote(collection['path']) +
"&mode=GET_CONTENT&media_type=" + collection["media_type"])
log.debug("addMenuDirectoryItem: {0} ({1})", collection.get('title'), url)
log.debug("addMenuDirectoryItem: {0} ({1})".format(collection.get('title'), url))
add_menu_directory_item(collection.get('title', string_load(30250)), url, art=collection.get("art"))
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@@ -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"
@@ -477,7 +459,7 @@ def show_tvshow_alpha_list(menu_params):
for collection in collections:
url = (sys.argv[0] + "?url=" + urllib.quote(collection['path']) +
"&mode=GET_CONTENT&media_type=" + collection["media_type"])
log.debug("addMenuDirectoryItem: {0} ({1})", collection.get('title'), url)
log.debug("addMenuDirectoryItem: {0} ({1})".format(collection.get('title'), url))
add_menu_directory_item(collection.get('title', string_load(30250)), url, art=collection.get("art"))
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@@ -1053,7 +1035,7 @@ def display_library_view(params):
data_manager = DataManager()
view_info = data_manager.get_content(view_info_url)
log.debug("VIEW_INFO : {0}", view_info)
log.debug("VIEW_INFO : {0}".format(view_info))
collection_type = view_info.get("CollectionType", None)
@@ -1111,7 +1093,7 @@ def show_search():
def set_library_window_values(force=False):
log.debug("set_library_window_values Called forced={0}", force)
log.debug("set_library_window_values Called forced={0}".format(force))
home_window = HomeWindow()
already_set = home_window.get_property("view_item.0.name")
@@ -1145,19 +1127,19 @@ def set_library_window_values(force=False):
# plugin.video.jellycon-
prop_name = "view_item.%i.name" % index
home_window.set_property(prop_name, name)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}", prop_name, name)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, name))
prop_name = "view_item.%i.id" % index
home_window.set_property(prop_name, item_id)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}", prop_name, item_id)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, item_id))
prop_name = "view_item.%i.type" % index
home_window.set_property(prop_name, collection_type)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}", prop_name, collection_type)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, collection_type))
thumb = downloadUtils.get_artwork(item, "Primary", server=server)
prop_name = "view_item.%i.thumb" % index
home_window.set_property(prop_name, thumb)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}", prop_name, thumb)
log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, thumb))
index += 1

View File

@@ -1,10 +1,12 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcaddon
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class PictureViewer(xbmcgui.WindowXMLDialog):
picture_url = None

View File

@@ -9,7 +9,7 @@ import json
import os
import base64
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .downloadutils import DownloadUtils
from .resume_dialog import ResumeDialog
from .utils import PlayUtils, get_art, send_event_notification, convert_size
@@ -24,7 +24,7 @@ from .picture_viewer import PictureViewer
from .tracking import timer
from .playnext import PlayNextDialog
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
download_utils = DownloadUtils()
@@ -60,7 +60,7 @@ def play_all_files(items, monitor, play_items=True):
source_id = selected_media_source.get("Id")
playurl, playback_type, listitem_props = PlayUtils().get_play_url(selected_media_source, play_session_id)
log.info("Play URL: {0} PlaybackType: {1} ListItem Properties: {2}", playurl, playback_type, listitem_props)
log.info("Play URL: {0} PlaybackType: {1} ListItem Properties: {2}".format(playurl, playback_type, listitem_props))
if playurl is None:
return
@@ -89,7 +89,7 @@ def play_all_files(items, monitor, play_items=True):
data["play_session_id"] = play_session_id
data["play_action_type"] = "play_all"
monitor.played_information[playurl] = data
log.debug("Add to played_information: {0}", monitor.played_information)
log.debug("Add to played_information: {0}".format(monitor.played_information))
list_item.setPath(playurl)
list_item = set_list_item_props(item_id, list_item, item, server, listitem_props, item_title)
@@ -121,7 +121,7 @@ def play_list_of_items(id_list, monitor):
def add_to_playlist(play_info, monitor):
log.debug("Adding item to playlist : {0}", play_info)
log.debug("Adding item to playlist : {0}".format(play_info))
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
server = download_utils.get_server()
@@ -159,7 +159,7 @@ def add_to_playlist(play_info, monitor):
source_id = selected_media_source.get("Id")
playurl, playback_type, listitem_props = PlayUtils().get_play_url(selected_media_source, play_session_id)
log.info("Play URL: {0} PlaybackType: {1} ListItem Properties: {2}", playurl, playback_type, listitem_props)
log.info("Play URL: {0} PlaybackType: {1} ListItem Properties: {2}".format(playurl, playback_type, listitem_props))
if playurl is None:
return
@@ -188,7 +188,7 @@ def add_to_playlist(play_info, monitor):
data["play_session_id"] = play_session_id
data["play_action_type"] = "play_all"
monitor.played_information[playurl] = data
log.debug("Add to played_information: {0}", monitor.played_information)
log.debug("Add to played_information: {0}".format(monitor.played_information))
list_item.setPath(playurl)
list_item = set_list_item_props(item_id, list_item, item, server, listitem_props, item_title)
@@ -238,7 +238,7 @@ def play_file(play_info, monitor):
subtitle_stream_index = play_info.get("subtitle_stream_index", None)
audio_stream_index = play_info.get("audio_stream_index", None)
log.debug("playFile id({0}) resume({1}) force_transcode({2})", item_id, auto_resume, force_transcode)
log.debug("playFile id({0}) resume({1}) force_transcode({2})".format(item_id, auto_resume, force_transcode))
settings = xbmcaddon.Addon()
addon_path = settings.getAddonInfo('path')
@@ -251,7 +251,7 @@ def play_file(play_info, monitor):
url = "{server}/Users/{userid}/Items/%s?format=json" % (item_id,)
data_manager = DataManager()
result = data_manager.get_content(url)
log.debug("Playfile item: {0}", result)
log.debug("Playfile item: {0}".format(result))
if result is None:
log.debug("Playfile item was None, so can not play!")
@@ -259,14 +259,14 @@ def play_file(play_info, monitor):
# if this is a season, playlist or album then play all items in that parent
if result.get("Type") in ["Season", "MusicAlbum", "Playlist"]:
log.debug("PlayAllFiles for parent item id: {0}", item_id)
log.debug("PlayAllFiles for parent item id: {0}".format(item_id))
url = ('{server}/Users/{userid}/items' +
'?ParentId=%s' +
'&Fields=MediaSources' +
'&format=json')
url = url % (item_id,)
result = data_manager.get_content(url)
log.debug("PlayAllFiles items: {0}", result)
log.debug("PlayAllFiles items: {0}".format(result))
# process each item
items = result["Items"]
@@ -366,7 +366,7 @@ def play_file(play_info, monitor):
resume_dialog.doModal()
resume_result = resume_dialog.getResumeAction()
del resume_dialog
log.debug("Resume Dialog Result: {0}", resume_result)
log.debug("Resume Dialog Result: {0}".format(resume_result))
# check system settings for play action
# if prompt is set ask to set it to auto resume
@@ -389,9 +389,9 @@ def play_file(play_info, monitor):
elif resume_result == -1:
return
log.debug("play_session_id: {0}", play_session_id)
log.debug("play_session_id: {0}".format(play_session_id))
playurl, playback_type, listitem_props = PlayUtils().get_play_url(selected_media_source, play_session_id)
log.info("Play URL: {0} Playback Type: {1} ListItem Properties: {2}", playurl, playback_type, listitem_props)
log.info("Play URL: {0} Playback Type: {1} ListItem Properties: {2}".format(playurl, playback_type, listitem_props))
if playurl is None:
return
@@ -431,7 +431,7 @@ def play_file(play_info, monitor):
if playback_type == "2": # if transcoding then prompt for audio and subtitle
playurl = audio_subs_pref(playurl, list_item, selected_media_source, item_id, audio_stream_index,
subtitle_stream_index)
log.debug("New playurl for transcoding: {0}", playurl)
log.debug("New playurl for transcoding: {0}".format(playurl))
elif playback_type == "1": # for direct stream add any streamable subtitles
external_subs(selected_media_source, list_item, item_id)
@@ -446,7 +446,7 @@ def play_file(play_info, monitor):
data["item_type"] = result.get("Type", None)
data["can_delete"] = result.get("CanDelete", False)
monitor.played_information[playurl] = data
log.debug("Add to played_information: {0}", monitor.played_information)
log.debug("Add to played_information: {0}".format(monitor.played_information))
list_item.setPath(playurl)
list_item = set_list_item_props(item_id, list_item, result, server, listitem_props, item_title)
@@ -488,12 +488,12 @@ def play_file(play_info, monitor):
count = 0
max_loops = 2 * 120
while not monitor.abortRequested() and player.isPlaying() and count < max_loops:
log.info("PlaybackResumrAction : Seeking to : {0}", seek_to_time)
log.info("PlaybackResumrAction : Seeking to : {0}".format(seek_to_time))
player.seekTime(seek_to_time)
current_position = player.getTime()
if current_position >= target_seek:
break
log.info("PlaybackResumrAction : target:{0} current:{1}", target_seek, current_position)
log.info("PlaybackResumrAction : target:{0} current:{1}".format(target_seek, current_position))
count = count + 1
xbmc.sleep(500)
@@ -571,7 +571,7 @@ def get_next_episode(item):
data_manager = DataManager()
items_result = data_manager.get_content(url)
log.debug("get_next_episode, sibling list: {0}", items_result)
log.debug("get_next_episode, sibling list: {0}".format(items_result))
if items_result is None:
log.debug("get_next_episode no results")
@@ -583,7 +583,7 @@ def get_next_episode(item):
index = item.get("IndexNumber", -1)
# find the very next episode in the season
if index == item_index + 1:
log.debug("get_next_episode, found next episode: {0}", item)
log.debug("get_next_episode, found next episode: {0}".format(item))
return item
return None
@@ -795,7 +795,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s
if select_subs_index in downloadable_streams:
subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index)
log.debug("Streaming subtitles url: {0} {1}", select_subs_index, subtitle_url)
log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url))
list_item.setSubtitles([subtitle_url])
else:
# Burn subtitles
@@ -815,7 +815,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s
if select_subs_index in downloadable_streams:
subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index)
log.debug("Streaming subtitles url: {0} {1}", select_subs_index, subtitle_url)
log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url))
list_item.setSubtitles([subtitle_url])
else:
# Burn subtitles
@@ -884,7 +884,7 @@ def external_subs(media_source, list_item, item_id):
resp = xbmcgui.Dialog().select(string_load(30292), sub_names)
if resp > -1:
selected_sub = externalsubs[resp]
log.debug("External Subtitle Selected: {0}", selected_sub)
log.debug("External Subtitle Selected: {0}".format(selected_sub))
list_item.setSubtitles([selected_sub])
@@ -937,7 +937,7 @@ def send_progress(monitor):
'VolumeLevel': volume
}
log.debug("Sending POST progress started: {0}", postdata)
log.debug("Sending POST progress started: {0}".format(postdata))
url = "{server}/Sessions/Playing/Progress"
download_utils.download_url(url, post_body=postdata, method="POST")
@@ -955,7 +955,7 @@ def get_volume():
def prompt_for_stop_actions(item_id, data):
log.debug("prompt_for_stop_actions Called : {0}", data)
log.debug("prompt_for_stop_actions Called : {0}".format(data))
settings = xbmcaddon.Addon()
current_position = data.get("currentPossition", 0)
@@ -986,7 +986,7 @@ def prompt_for_stop_actions(item_id, data):
# item percentage complete
# percenatge_complete = int(((current_position * 10000000) / runtime) * 100)
percenatge_complete = int((current_position / duration) * 100)
log.debug("Episode Percentage Complete: {0}", percenatge_complete)
log.debug("Episode Percentage Complete: {0}".format(percenatge_complete))
if (can_delete and
prompt_delete_episode_percentage < 100 and
@@ -1047,12 +1047,12 @@ def prompt_for_stop_actions(item_id, data):
def stop_all_playback(played_information):
log.debug("stop_all_playback : {0}", played_information)
log.debug("stop_all_playback : {0}".format(played_information))
if len(played_information) == 0:
return
log.debug("played_information: {0}", played_information)
log.debug("played_information: {0}".format(played_information))
home_screen = HomeWindow()
home_screen.clear_property("currently_playing_id")
@@ -1060,8 +1060,8 @@ def stop_all_playback(played_information):
for item_url in played_information:
data = played_information.get(item_url)
if data.get("currently_playing", False) is True:
log.debug("item_url: {0}", item_url)
log.debug("item_data: {0}", data)
log.debug("item_url: {0}".format(item_url))
log.debug("item_data: {0}".format(data))
current_position = data.get("currentPossition", 0)
duration = data.get("duration", 0)
@@ -1070,7 +1070,7 @@ def stop_all_playback(played_information):
play_session_id = data.get("play_session_id")
if jellyfin_item_id is not None and current_position >= 0:
log.debug("Playback Stopped at: {0}", current_position)
log.debug("Playback Stopped at: {0}".format(current_position))
url = "{server}/Sessions/Playing/Stopped"
postdata = {
@@ -1086,21 +1086,22 @@ 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):
try:
playing_file = xbmc.Player().getPlayingFile()
except Exception as e:
log.error("get_playing_data : getPlayingFile() : {0}", e)
log.error("get_playing_data : getPlayingFile() : {0}".format(e))
return None
log.debug("get_playing_data : getPlayingFile() : {0}", playing_file)
log.debug("get_playing_data : getPlayingFile() : {0}".format(playing_file))
if playing_file not in play_data_map:
infolabel_path_and_file = xbmc.getInfoLabel("Player.Filenameandpath")
log.debug("get_playing_data : Filenameandpath : {0}", infolabel_path_and_file)
log.debug("get_playing_data : Filenameandpath : {0}".format(infolabel_path_and_file))
if infolabel_path_and_file not in play_data_map:
log.debug("get_playing_data : play data not found")
return None
@@ -1113,7 +1114,7 @@ def get_playing_data(play_data_map):
class Service(xbmc.Player):
def __init__(self, *args):
log.debug("Starting monitor service: {0}", args)
log.debug("Starting monitor service: {0}".format(args))
self.played_information = {}
def onPlayBackStarted(self):
@@ -1151,7 +1152,7 @@ class Service(xbmc.Player):
'PlaySessionId': play_session_id
}
log.debug("Sending POST play started: {0}", postdata)
log.debug("Sending POST play started: {0}".format(postdata))
url = "{server}/Sessions/Playing"
download_utils.download_url(url, post_body=postdata, method="POST")
@@ -1202,8 +1203,6 @@ class PlaybackService(xbmc.Monitor):
self.monitor = monitor
def onNotification(self, sender, method, data):
log.debug("PlaybackService:onNotification:{0}:{1}:{2}", sender, method, data)
if method == 'GUI.OnScreensaverActivated':
self.screensaver_activated()
return
@@ -1221,20 +1220,20 @@ class PlaybackService(xbmc.Monitor):
data_json = json.loads(data)
message_data = data_json[0]
log.debug("PlaybackService:onNotification:{0}", message_data)
log.debug("PlaybackService:onNotification:{0}".format(message_data))
decoded_data = base64.b64decode(message_data)
play_info = json.loads(decoded_data)
if signal == "jellycon_play_action":
log.info("Received jellycon_play_action : {0}", play_info)
log.info("Received jellycon_play_action : {0}".format(play_info))
play_file(play_info, self.monitor)
elif signal == "jellycon_play_youtube_trailer_action":
log.info("Received jellycon_play_trailer_action : {0}", play_info)
log.info("Received jellycon_play_trailer_action : {0}".format(play_info))
trailer_link = play_info["url"]
xbmc.executebuiltin(trailer_link)
elif signal == "set_view":
view_id = play_info["view_id"]
log.debug("Setting view id: {0}", view_id)
log.debug("Setting view id: {0}".format(view_id))
xbmc.executebuiltin("Container.SetViewMode(%s)" % int(view_id))
def screensaver_activated(self):

View File

@@ -1,3 +1,5 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import os
import threading
@@ -5,10 +7,10 @@ import xbmc
import xbmcgui
import xbmcaddon
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .play_utils import send_event_notification
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class PlayNextService(threading.Thread):
@@ -38,7 +40,7 @@ class PlayNextService(threading.Thread):
if not is_playing:
settings = xbmcaddon.Addon()
play_next_trigger_time = int(settings.getSetting('play_next_trigger_time'))
log.debug("New play_next_trigger_time value: {0}", play_next_trigger_time)
log.debug("New play_next_trigger_time value: {0}".format(play_next_trigger_time))
duration = player.getTotalTime()
position = player.getTime()
@@ -47,10 +49,10 @@ class PlayNextService(threading.Thread):
if not play_next_triggered and (trigger_time > time_to_end) and play_next_dialog is None:
play_next_triggered = True
log.debug("play_next_triggered hit at {0} seconds from end", time_to_end)
log.debug("play_next_triggered hit at {0} seconds from end".format(time_to_end))
play_data = get_playing_data(self.monitor.played_information)
log.debug("play_next_triggered play_data : {0}", play_data)
log.debug("play_next_triggered play_data : {0}".format(play_data))
next_episode = play_data.get("next_episode")
item_type = play_data.get("item_type")
@@ -116,7 +118,7 @@ class PlayNextDialog(xbmcgui.WindowXMLDialog):
pass
def onMessage(self, message):
log.debug("PlayNextDialog: onMessage: {0}", message)
log.debug("PlayNextDialog: onMessage: {0}".format(message))
def onAction(self, action):
@@ -125,7 +127,7 @@ class PlayNextDialog(xbmcgui.WindowXMLDialog):
elif action.getId() == 92: # ACTION_NAV_BACK
self.close()
else:
log.debug("PlayNextDialog: onAction: {0}", action.getId())
log.debug("PlayNextDialog: onAction: {0}".format(action.getId()))
def onClick(self, control_id):
if control_id == 3013:
@@ -133,7 +135,7 @@ class PlayNextDialog(xbmcgui.WindowXMLDialog):
self.play_called
self.close()
next_item_id = self.episode_info.get("Id")
log.debug("Playing Next Episode: {0}", next_item_id)
log.debug("Playing Next Episode: {0}".format(next_item_id))
play_info = {}
play_info["item_id"] = next_item_id
play_info["auto_resume"] = "-1"

View File

@@ -1,11 +1,12 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .translation import string_load
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class ResumeDialog(xbmcgui.WindowXMLDialog):

View File

@@ -1,11 +1,12 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import xbmc
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class SafeDeleteDialog(xbmcgui.WindowXMLDialog):
@@ -36,7 +37,7 @@ class SafeDeleteDialog(xbmcgui.WindowXMLDialog):
pass
def onMessage(self, message):
log.debug("SafeDeleteDialog: onMessage: {0}", message)
log.debug("SafeDeleteDialog: onMessage: {0}".format(message))
def onAction(self, action):
@@ -45,7 +46,7 @@ class SafeDeleteDialog(xbmcgui.WindowXMLDialog):
elif action.getId() == 92: # ACTION_NAV_BACK
self.close()
else:
log.debug("SafeDeleteDialog: onAction: {0}", action.getId())
log.debug("SafeDeleteDialog: onAction: {0}".format(action.getId()))
def onClick(self, controlID):
if controlID == 1:

View File

@@ -1,9 +1,10 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import socket
import json
from urlparse import urlparse
import httplib
import requests
import ssl
import time
import hashlib
@@ -12,15 +13,16 @@ 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
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .translation import string_load
from .utils import datetime_from_string
from .clientinfo import ClientInformation
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
__addon__ = xbmcaddon.Addon()
__addon_name__ = __addon__.getAddonInfo('name')
@@ -40,66 +42,45 @@ def check_connection_speed():
url = server + "/playback/bitratetest?size=%s" % test_data_size
url_bits = urlparse(url.strip())
protocol = url_bits.scheme
host_name = url_bits.hostname
port = url_bits.port
# user_name = url_bits.username
# user_password = url_bits.password
url_path = url_bits.path
url_puery = url_bits.query
server = "%s:%s" % (host_name, port)
url_path = url_path + "?" + url_puery
local_use_https = False
if protocol.lower() == "https":
local_use_https = True
if local_use_https and verify_cert:
log.debug("Connection: HTTPS, Cert checked")
conn = httplib.HTTPSConnection(server, timeout=http_timeout)
elif local_use_https and not verify_cert:
log.debug("Connection: HTTPS, Cert NOT checked")
conn = httplib.HTTPSConnection(server, timeout=http_timeout, context=ssl._create_unverified_context())
else:
log.debug("Connection: HTTP")
conn = httplib.HTTPConnection(server, timeout=http_timeout)
head = du.get_auth_header(True)
head["User-Agent"] = "JellyCon-" + ClientInformation().get_version()
conn.request(method="GET", url=url_path, headers=head)
request_details = {
"stream": True,
"headers": head
}
if not verify_cert:
request_details["verify"] = False
progress_dialog = xbmcgui.DialogProgress()
message = 'Testing with {0} MB of data'.format(speed_test_data_size)
progress_dialog.create("JellyCon connection speed test", message)
total_data_read = 0
total_time = time.time()
start_time = time.time()
log.debug("Starting Connection Speed Test")
response = conn.getresponse()
response = requests.get(url, **request_details)
last_percentage_done = 0
if int(response.status) == 200:
data = response.read(10240)
while len(data) > 0:
total_data_read = 0
if response.status_code == 200:
for data in response.iter_content(chunk_size=10240):
total_data_read += len(data)
percentage_done = int(float(total_data_read) / float(test_data_size) * 100.0)
if last_percentage_done != percentage_done:
progress_dialog.update(percentage_done)
last_percentage_done = percentage_done
data = response.read(10240)
else:
log.error("HTTP response error: {0} {1}", response.status, response.reason)
error_message = "HTTP response error: %s\n%s" % (response.status, response.reason)
log.error("HTTP response error: {0} {1}".format(response.status_code, response.content))
error_message = "HTTP response error: %s\n%s" % (response.status_code, response.content)
xbmcgui.Dialog().ok("Speed Test Error", error_message)
return -1
total_data_read_kbits = (total_data_read * 8) / 1000
total_time = time.time() - total_time
total_time = time.time() - start_time
speed = int(total_data_read_kbits / total_time)
log.debug("Finished Connection Speed Test, speed: {0} total_data: {1}, total_time: {2}", speed, total_data_read, total_time)
log.debug("Finished Connection Speed Test, speed: {0} total_data: {1}, total_time: {2}".format(speed, total_data_read, total_time))
progress_dialog.close()
del progress_dialog
@@ -119,10 +100,9 @@ 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:
log.debug("Server Plugin List: {0}", result)
result = du.download_url("{server}/Plugins")
if result:
log.debug("Server Plugin List: {0}".format(result))
safe_delete_found = False
for plugin in result:
@@ -130,7 +110,7 @@ def check_safe_delete_available():
safe_delete_found = True
break
log.debug("Safe Delete Plugin Available: {0}", safe_delete_found)
log.debug("Safe Delete Plugin Available: {0}".format(safe_delete_found))
home_window = HomeWindow()
if safe_delete_found:
home_window.set_property("safe_delete_plugin_available", "true")
@@ -157,8 +137,8 @@ def get_server_details():
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
log.debug("MutliGroup: {0}", multi_group)
log.debug("Sending UDP Data: {0}", message)
log.debug("MutliGroup: {0}".format(multi_group))
log.debug("Sending UDP Data: {0}".format(message))
progress = xbmcgui.DialogProgress()
progress.create(__addon_name__ + " : " + string_load(30373))
@@ -179,11 +159,11 @@ def get_server_details():
except:
break
except Exception as e:
log.error("UPD Discovery Error: {0}", e)
log.error("UPD Discovery Error: {0}".format(e))
progress.close()
log.debug("Found Servers: {0}", servers)
log.debug("Found Servers: {0}".format(servers))
return servers
@@ -247,29 +227,18 @@ def check_server(force=False, change_user=False, notify=False):
xbmc.executebuiltin("ActivateWindow(Home)")
return
url_bits = urlparse(server_url)
server_address = url_bits.hostname
server_port = str(url_bits.port)
server_protocol = url_bits.scheme
user_name = url_bits.username
user_password = url_bits.password
public_lookup_url = "%s/System/Info/Public?format=json" % (server_url)
if user_name and user_password:
temp_url = "%s://%s:%s@%s:%s/Users/Public?format=json" % (server_protocol, user_name, user_password, server_address, server_port)
else:
temp_url = "%s://%s:%s/Users/Public?format=json" % (server_protocol, server_address, server_port)
log.debug("Testing_Url: {0}", temp_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(temp_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),
"%s://%s:%s/" % (server_protocol, server_address, server_port))
server_url)
break
else:
return_index = xbmcgui.Dialog().yesno(__addon_name__ + " : " + string_load(30135),
@@ -279,30 +248,8 @@ def check_server(force=False, change_user=False, notify=False):
xbmc.executebuiltin("ActivateWindow(Home)")
return
log.debug("Selected server: {0}", server_url)
# parse the url
url_bits = urlparse(server_url)
server_address = url_bits.hostname
server_port = str(url_bits.port)
server_protocol = url_bits.scheme
user_name = url_bits.username
user_password = url_bits.password
log.debug("Detected server info {0} - {1} - {2}", server_protocol, server_address, server_port)
# save the server info
settings.setSetting("port", server_port)
if user_name and user_password:
server_address = "%s:%s@%s" % (url_bits.username, url_bits.password, server_address)
settings.setSetting("ipaddress", server_address)
if server_protocol == "https":
settings.setSetting("protocol", "1")
else:
settings.setSetting("protocol", "0")
log.debug("Selected server: {0}".format(server_url))
settings.setSetting("server_address", server_url)
something_changed = True
# do we need to change the user
@@ -319,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}", 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}", last_active_date)
ago = datetime.now() - last_active_date
log.debug("LastActivityDate: {0}", ago)
days = divmod(ago.seconds, 86400)
hours = divmod(days[1], 3600)
minutes = divmod(hours[1], 60)
log.debug("LastActivityDate: {0} {1} {2}", 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}", 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}", 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}", selected_user_name)
else:
return
log.debug("Saving username and password: {0}".format(selected_user_name))
save_user_details(settings, selected_user_name, kb.getText())
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}", selected_user_name)
log.debug("Using stored password for user: {0}", hashed_username)
save_user_details(settings, selected_user_name, saved_password)
else:
kb = xbmc.Keyboard()
kb.setHeading(string_load(30006))
kb.setHiddenInput(True)
kb.doModal()
if kb.isConfirmed():
log.debug("Saving username and password: {0}", 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}", hashed_username)
settings.setSetting("saved_user_password_" + hashed_username, kb.getText())
else:
log.debug("Saving username with no password: {0}", 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,15 +1,15 @@
from __future__ import division, absolute_import, print_function, unicode_literals
import json
import sys
import xbmcgui
import xbmcplugin
from .downloadutils import DownloadUtils
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .utils import get_art
from .datamanager import DataManager
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
def show_server_sessions():
@@ -29,7 +29,7 @@ def show_server_sessions():
url = "{server}/Sessions"
results = data_manager.get_content(url)
log.debug("session_info: {0}", results)
log.debug("session_info: {0}".format(results))
if results is None:
return

View File

@@ -1,51 +0,0 @@
# Gnu General Public License - see LICENSE.TXT
import xbmc
import xbmcaddon
from .jsonrpc import JsonRpc
class SimpleLogging:
name = ""
enable_logging = False
def __init__(self, name):
settings = xbmcaddon.Addon()
prefix = settings.getAddonInfo('name')
self.name = prefix + '.' + name
self.enable_logging = settings.getSetting('log_debug') == "true"
# params = {"setting": "debug.showloginfo"}
# setting_result = json_rpc('Settings.getSettingValue').execute(params)
# current_value = setting_result.get("result", None)
# if current_value is not None:
# self.enable_logging = current_value.get("value", False)
# xbmc.log("LOGGING_ENABLED %s : %s" % (self.name, str(self.enable_logging)), level=xbmc.LOGDEBUG)
def __str__(self):
return "LoggingEnabled: " + str(self.enable_logging)
def info(self, fmt, *args, **kwargs):
log_line = self.name + "|INFO|" + self.log_line(fmt, *args)
xbmc.log(log_line, level=xbmc.LOGNOTICE)
def error(self, fmt, *args, **kwargs):
log_line = self.name + "|ERROR|" + self.log_line(fmt, *args)
xbmc.log(log_line, level=xbmc.LOGERROR)
def debug(self, fmt, *args, **kwargs):
if self.enable_logging:
log_line = self.name + "|DEBUG|" + self.log_line(fmt, *args)
xbmc.log(log_line, level=xbmc.LOGNOTICE)
@staticmethod
def log_line(fmt, *args):
new_args = []
# convert any unicode to utf-8 strings
for arg in args:
if isinstance(arg, unicode):
new_args.append(arg.encode("utf-8"))
else:
new_args.append(arg)
log_line = fmt.format(*new_args)
return log_line

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
@@ -7,9 +8,9 @@ import xbmcgui
import xbmcvfs
from .jsonrpc import JsonRpc, get_value, set_value
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
ver = xbmc.getInfoLabel('System.BuildVersion')[:2]
@@ -52,7 +53,7 @@ def clone_skin():
kodi_path = xbmc.translatePath("special://xbmc")
kodi_skin_source = os.path.join(kodi_path, "addons", "skin.estuary")
log.debug("Kodi Skin Source: {0}", kodi_skin_source)
log.debug("Kodi Skin Source: {0}".format(kodi_skin_source))
pdialog = xbmcgui.DialogProgress()
pdialog.create("JellyCon Skin Cloner", "")
@@ -60,11 +61,11 @@ def clone_skin():
all_files = []
walk_path(kodi_skin_source, "", all_files)
for found in all_files:
log.debug("Found Path: {0}", found)
log.debug("Found Path: {0}".format(found))
kodi_home_path = xbmc.translatePath("special://home")
kodi_skin_destination = os.path.join(kodi_home_path, "addons", "skin.estuary_jellycon")
log.debug("Kodi Skin Destination: {0}", kodi_skin_destination)
log.debug("Kodi Skin Destination: {0}".format(kodi_skin_destination))
# copy all skin files (clone)
count = 0
@@ -96,7 +97,7 @@ def clone_skin():
# get jellycon path
jellycon_path = os.path.join(kodi_home_path, "addons", "plugin.video.jellycon")
log.debug("Major Version: {0}", ver)
log.debug("Major Version: {0}".format(ver))
file_list = ["Home.xml",
"Includes_Home.xml",
@@ -123,11 +124,11 @@ def clone_skin():
'enabled': True
}
result = JsonRpc('Addons.SetAddonEnabled').execute(params)
log.debug("Addons.SetAddonEnabled : {0}", result)
log.debug("Addons.SetAddonEnabled : {0}".format(result))
log.debug("SkinCloner : Current Skin : " + get_value("lookandfeel.skin"))
set_result = set_value("lookandfeel.skin", "skin.estuary_jellycon")
log.debug("Save Setting : lookandfeel.skin : {0}", set_result)
log.debug("Save Setting : lookandfeel.skin : {0}".format(set_result))
log.debug("SkinCloner : Current Skin : " + get_value("lookandfeel.skin"))

View File

@@ -1,11 +1,12 @@
# Gnu General Public License - see LICENSE.TXT
from __future__ import division, absolute_import, print_function, unicode_literals
import sys
import functools
import time
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
enabled = False
@@ -27,6 +28,6 @@ def timer(func):
data = args[1]
elif func.__name__ == "main_entry_point" and len(sys.argv) > 2:
data = sys.argv[2]
log.info("timing_data|{0}|{1}|{2}|{3}", func.__name__, started, ended, data)
log.info("timing_data|{0}|{1}|{2}|{3}".format(func.__name__, started, ended, data))
return value
return wrapper

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
@@ -6,12 +7,12 @@ import encodings
import xbmc
import xbmcgui
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .datamanager import DataManager
from .translation import string_load
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
dataManager = DataManager()
details_string = 'EpisodeCount,SeasonCount,Path,Etag,MediaStreams'
@@ -111,7 +112,7 @@ def get_match(item_type, title, year, imdb_id):
results = results.get('SearchHints')
if results is None:
results = []
log.debug('SearchHints jsonData: {0}', results)
log.debug('SearchHints jsonData: {0}'.format(results))
potential_matches = []
@@ -121,12 +122,12 @@ def get_match(item_type, title, year, imdb_id):
if (name == title and int(year) == production_year) or (int(year) == production_year):
potential_matches.append(item)
log.debug('Potential matches: {0}', potential_matches)
log.debug('Potential matches: {0}'.format(potential_matches))
for item in potential_matches:
item_imdb_id = get_imdb_id(item.get('ItemId'))
if item_imdb_id == imdb_id:
log.debug('Found match: {0}', item)
log.debug('Found match: {0}'.format(item))
return item
return None

View File

@@ -1,7 +1,9 @@
import xbmcaddon
from .simple_logging import SimpleLogging
from __future__ import division, absolute_import, print_function, unicode_literals
log = SimpleLogging(__name__)
import xbmcaddon
from .loghandler import LazyLogger
log = LazyLogger(__name__)
addon = xbmcaddon.Addon()

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,9 +15,10 @@ import math
from datetime import datetime
import calendar
import re
from urllib import urlencode
from .downloadutils import DownloadUtils
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .clientinfo import ClientInformation
# hack to get datetime strptime loaded
@@ -23,22 +26,16 @@ throwaway = time.strptime('20110101', '%Y%m%d')
# define our global download utils
downloadUtils = DownloadUtils()
log = SimpleLogging(__name__)
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
###########################################################################
@@ -88,7 +85,7 @@ class PlayUtils:
if direct_path.startswith("//"):
direct_path = "smb://" + direct_path[2:]
log.debug("playback_direct_path: {0}", direct_path)
log.debug("playback_direct_path: {0}".format(direct_path))
if xbmcvfs.exists(direct_path):
playurl = direct_path
@@ -164,13 +161,13 @@ class PlayUtils:
lines = contents.split(line_break)
for line in lines:
line = line.strip()
log.debug("STRM Line: {0}", line)
log.debug("STRM Line: {0}".format(line))
if line.startswith('#KODIPROP:'):
match = re.search('#KODIPROP:(?P<item_property>[^=]+?)=(?P<property_value>.+)', line)
if match:
item_property = match.group('item_property')
property_value = match.group('property_value')
log.debug("STRM property found: {0} value: {1}", item_property, property_value)
log.debug("STRM property found: {0} value: {1}".format(item_property, property_value))
listitem_props.append((item_property, property_value))
else:
log.debug("STRM #KODIPROP incorrect format")
@@ -181,7 +178,7 @@ class PlayUtils:
playurl = line
log.debug("STRM playback url found")
log.debug("Playback URL: {0} ListItem Properties: {1}", playurl, listitem_props)
log.debug("Playback URL: {0} ListItem Properties: {1}".format(playurl, listitem_props))
return playurl, listitem_props
@@ -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)
@@ -307,7 +304,7 @@ def send_event_notification(method, data):
base64_data = base64.b64encode(message_data)
escaped_data = '\\"[\\"{0}\\"]\\"'.format(base64_data)
command = 'XBMC.NotifyAll({0}.SIGNAL,{1},{2})'.format(source_id, method, escaped_data)
log.debug("Sending notification event data: {0}", command)
log.debug("Sending notification event data: {0}".format(command))
xbmc.executebuiltin(command)
@@ -317,7 +314,7 @@ def datetime_from_string(time_string):
time_string = re.sub("[0-9]{1}Z", " UTC", time_string)
elif time_string[-6:] == "+00:00":
time_string = re.sub("[0-9]{1}\+00:00", " UTC", time_string)
log.debug("New Time String : {0}", time_string)
log.debug("New Time String : {0}".format(time_string))
start_time = time.strptime(time_string, "%Y-%m-%dT%H:%M:%S.%f %Z")
dt = datetime(*(start_time[0:6]))

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

@@ -10,13 +10,13 @@ import xbmc
import xbmcgui
from .functions import play_action
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from . import clientinfo
from . import downloadutils
from .jsonrpc import JsonRpc
from .kodi_utils import HomeWindow
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
class WebSocketClient(threading.Thread):
@@ -65,10 +65,10 @@ class WebSocketClient(threading.Thread):
self._general_commands(data)
else:
log.debug("WebSocket Message Type: {0}", message)
log.debug("WebSocket Message Type: {0}".format(message))
def _library_changed(self, data):
log.debug("Library_Changed: {0}", data)
log.debug("Library_Changed: {0}".format(data))
self._library_monitor.check_for_updates()
def _play(self, data):
@@ -81,7 +81,7 @@ class WebSocketClient(threading.Thread):
home_screen.set_property("skip_select_user", "true")
startat = data.get('StartPositionTicks', -1)
log.debug("WebSocket Message PlayNow: {0}", data)
log.debug("WebSocket Message PlayNow: {0}".format(data))
media_source_id = data.get("MediaSourceId", "")
subtitle_stream_index = data.get("SubtitleStreamIndex", None)
@@ -124,14 +124,14 @@ class WebSocketClient(threading.Thread):
seek_to = data['SeekPositionTicks']
seek_time = seek_to / 10000000.0
player.seekTime(seek_time)
log.debug("Seek to {0}", seek_time)
log.debug("Seek to {0}".format(seek_time))
elif command in actions:
actions[command]()
log.debug("Command: {0} completed", command)
log.debug("Command: {0} completed".format(command))
else:
log.debug("Unknown command: {0}", command)
log.debug("Unknown command: {0}".format(command))
return
def _general_commands(self, data):
@@ -175,7 +175,7 @@ class WebSocketClient(threading.Thread):
# header = arguments['Header']
text = arguments['Text']
# show notification here
log.debug("WebSocket DisplayMessage: {0}", text)
log.debug("WebSocket DisplayMessage: {0}".format(text))
xbmcgui.Dialog().notification("JellyCon", text)
elif command == 'SendString':
@@ -234,7 +234,7 @@ class WebSocketClient(threading.Thread):
self.post_capabilities()
def on_error(self, ws, error):
log.debug("Error: {0}", error)
log.debug("Error: {0}".format(error))
def run(self):
@@ -254,8 +254,8 @@ class WebSocketClient(threading.Thread):
else:
server = server.replace('http', "ws")
websocket_url = "%s/websocket?api_key=%s&deviceId=%s" % (server, token, self.device_id)
log.debug("websocket url: {0}", websocket_url)
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,
on_open=self.on_open,

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
@@ -10,12 +11,12 @@ import time
from .downloadutils import DownloadUtils
from .utils import get_jellyfin_url
from .datamanager import DataManager
from .simple_logging import SimpleLogging
from .loghandler import LazyLogger
from .kodi_utils import HomeWindow
from .dir_functions import process_directory
from .tracking import timer
log = SimpleLogging(__name__)
log = LazyLogger(__name__)
downloadUtils = DownloadUtils()
dataManager = DataManager()
kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
@@ -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:
@@ -58,14 +58,14 @@ def set_random_movies():
m.update(movies_list_string)
new_widget_hash = m.hexdigest()
log.debug("set_random_movies : {0}", movies_list_string)
log.debug("set_random_movies : {0}", new_widget_hash)
log.debug("set_random_movies : {0}".format(movies_list_string))
log.debug("set_random_movies : {0}".format(new_widget_hash))
home_window.set_property("random-movies", movies_list_string)
home_window.set_property("random-movies-changed", new_widget_hash)
def set_background_image(force=False):
log.debug("set_background_image Called forced={0}", force)
log.debug("set_background_image Called forced={0}".format(force))
global background_current_item
global background_items
@@ -76,12 +76,12 @@ def set_background_image(force=False):
background_items = []
if len(background_items) == 0:
log.debug("set_background_image: Need to load more backgrounds {0} - {1}",
len(background_items), background_current_item)
log.debug("set_background_image: Need to load more backgrounds {0} - {1}".format(
len(background_items), background_current_item))
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", [])
@@ -105,12 +104,12 @@ def set_background_image(force=False):
item_background["name"] = label
background_items.append(item_background)
log.debug("set_background_image: Loaded {0} more backgrounds", len(background_items))
log.debug("set_background_image: Loaded {0} more backgrounds".format(len(background_items)))
if len(background_items) > 0:
bg_image = background_items[background_current_item].get("image")
label = background_items[background_current_item].get("name")
log.debug("set_background_image: {0} - {1} - {2}", background_current_item, label, bg_image)
log.debug("set_background_image: {0} - {1} - {2}".format(background_current_item, label, bg_image))
background_current_item += 1
if background_current_item >= len(background_items):
@@ -133,7 +132,7 @@ def check_for_new_content():
log.debug("Using simple new content check")
current_time_stamp = str(time.time())
home_window.set_property("jellycon_widget_reload", current_time_stamp)
log.debug("Setting New Widget Hash: {0}", current_time_stamp)
log.debug("Setting New Widget Hash: {0}".format(current_time_stamp))
return
url_params = {}
@@ -144,13 +143,11 @@ 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)
log.debug("LATEST_ADDED_ITEM: {0}", result)
result = downloadUtils.download_url(added_url, suppress=True)
log.debug("LATEST_ADDED_ITEM: {0}".format(result))
last_added_date = ""
if result is not None:
@@ -158,7 +155,7 @@ def check_for_new_content():
if len(items) > 0:
item = items[0]
last_added_date = item.get("Etag", "")
log.debug("last_added_date: {0}", last_added_date)
log.debug("last_added_date: {0}".format(last_added_date))
url_params = {}
url_params["Recursive"] = True
@@ -168,13 +165,11 @@ 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)
log.debug("LATEST_PLAYED_ITEM: {0}", result)
result = downloadUtils.download_url(played_url, suppress=True)
log.debug("LATEST_PLAYED_ITEM: {0}".format(result))
last_played_date = ""
if result is not None:
@@ -186,30 +181,30 @@ def check_for_new_content():
if user_data is not None:
last_played_date = user_data.get("LastPlayedDate", "")
log.debug("last_played_date: {0}", last_played_date)
log.debug("last_played_date: {0}".format(last_played_date))
current_widget_hash = home_window.get_property("jellycon_widget_reload")
log.debug("Current Widget Hash: {0}", current_widget_hash)
log.debug("Current Widget Hash: {0}".format(current_widget_hash))
m = hashlib.md5()
m.update(last_played_date + last_added_date)
new_widget_hash = m.hexdigest()
log.debug("New Widget Hash: {0}", new_widget_hash)
log.debug("New Widget Hash: {0}".format(new_widget_hash))
if current_widget_hash != new_widget_hash:
home_window.set_property("jellycon_widget_reload", new_widget_hash)
log.debug("Setting New Widget Hash: {0}", new_widget_hash)
log.debug("Setting New Widget Hash: {0}".format(new_widget_hash))
@timer
def get_widget_content_cast(handle, params):
log.debug("getWigetContentCast Called: {0}", params)
log.debug("getWigetContentCast Called: {0}".format(params))
server = downloadUtils.get_server()
item_id = params["id"]
data_manager = DataManager()
result = data_manager.get_content("{server}/Users/{userid}/Items/" + item_id + "?format=json")
log.debug("ItemInfo: {0}", result)
result = data_manager.get_content("{server}/Users/{userid}/Items/" + item_id)
log.debug("ItemInfo: {0}".format(result))
if not result:
return
@@ -272,7 +267,7 @@ def get_widget_content_cast(handle, params):
@timer
def get_widget_content(handle, params):
log.debug("getWigetContent Called: {0}", params)
log.debug("getWigetContent Called: {0}".format(params))
settings = xbmcaddon.Addon()
hide_watched = settings.getSetting("hide_watched") == "true"
@@ -283,12 +278,11 @@ def get_widget_content(handle, params):
log.error("getWigetContent type not set")
return
log.debug("widget_type: {0}", widget_type)
log.debug("widget_type: {0}".format(widget_type))
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":
@@ -377,7 +369,7 @@ def get_widget_content(handle, params):
set_id = 0
while len(ids) < 20 and suggested_items:
items = suggested_items[set_id]
log.debug("BaselineItemName : {0} - {1}", set_id, items.get("BaselineItemName"))
log.debug("BaselineItemName : {0} - {1}".format(set_id, items.get("BaselineItemName")))
items = items["Items"]
rand = random.randint(0, len(items) - 1)
# log.debug("random suggestions index : {0} {1}", rand, set_id)
@@ -397,7 +389,7 @@ def get_widget_content(handle, params):
set_id = 0
id_list = ",".join(ids)
log.debug("Recommended Items : {0}", len(ids), id_list)
log.debug("Recommended Items : {0}".format(len(ids), id_list))
url_params["Ids"] = id_list
items_url = get_jellyfin_url(url_verb, url_params)
@@ -417,7 +409,7 @@ def get_widget_content(handle, params):
if detected_type is not None:
# if the media type is not set then try to use the detected type
log.debug("Detected content type: {0}", detected_type)
log.debug("Detected content type: {0}".format(detected_type))
content_type = None
if detected_type == "Movie":

View File

@@ -4,9 +4,10 @@
<setting label="30388" type="lsep"/>
<setting label="30011" type="action" action="RunScript(plugin.video.jellycon,0,?mode=DETECT_SERVER_USER)" option="close"/>
<setting id="protocol" type="select" label="30390" lvalues="30391|30392" default="0" />
<setting id="ipaddress" type="text" label="30000" default="&lt;none&gt;" visible="true" enable="true" />
<setting id="port" type="text" label="30001" default="8096" visible="true" enable="true" />
<setting id="ipaddress" type="text" label="30000" default="" visible="false" enable="false" />
<setting id="protocol" type="select" label="30390" lvalues="30391|30392" default="0" visible="false"/>
<setting id="port" type="text" label="30001" default="8096" visible="false" enable="false" />
<setting id="server_address" type="text" label="30000" default="" visible="true" enable="true" />
<setting id="verify_cert" type="bool" label="30003" default="false" visible="true" enable="true" />
<setting label="30389" type="lsep"/>
@@ -133,7 +134,7 @@
<setting id="use_cached_widget_data" type="bool" label="30441" default="false" visible="true" enable="true" />
<setting id="showLoadProgress" type="bool" label="30120" default="false" visible="true" enable="true" />
<setting id="suppressErrors" type="bool" label="30315" default="false" visible="true" enable="true" />
<setting id="speed_test_data_size" type="slider" label="30436" default="15" range="5,1,100" option="int" visible="true"/>
<setting id="speed_test_data_size" type="slider" label="30436" default="10" range="1,1,10" option="int" visible="true"/>
</category>
<category label="30421">

View File

@@ -9,7 +9,7 @@ import xbmcaddon
import xbmcgui
from resources.lib.downloadutils import DownloadUtils, save_user_details
from resources.lib.simple_logging import SimpleLogging
from resources.lib.loghandler import LazyLogger
from resources.lib.play_utils import Service, PlaybackService, send_progress
from resources.lib.kodi_utils import HomeWindow
from resources.lib.widgets import set_background_image, set_random_movies
@@ -35,13 +35,13 @@ home_window.clear_property("userid")
home_window.clear_property("AccessToken")
home_window.clear_property("Params")
log = SimpleLogging('service')
log = LazyLogger('service')
monitor = xbmc.Monitor()
try:
clear_old_cache_data()
except Exception as error:
log.error("Error in clear_old_cache_data() : {0}", error)
log.error("Error in clear_old_cache_data() : {0}".format(error))
# wait for 10 seconds for the Kodi splash screen to close
i = 0
@@ -60,7 +60,7 @@ try:
download_utils.authenticate()
download_utils.get_user_id()
except Exception as error:
log.error("Error with initial service auth: {0}", error)
log.error("Error with initial service auth: {0}".format(error))
image_server = HttpImageServerThread()
@@ -181,8 +181,8 @@ while not xbmc.abortRequested:
set_background_image(False)
except Exception as error:
log.error("Exception in Playback Monitor: {0}", error)
log.error("{0}", traceback.format_exc())
log.error("Exception in Playback Monitor: {0}".format(error))
log.error("{0}".format(traceback.format_exc()))
first_run = False
xbmc.sleep(1000)