mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-04-20 17:53:56 +00:00
Initial work on Kodi 19 (and Python 3) support
This commit is contained in:
parent
898b0d1faf
commit
a51bf9c2cc
68 changed files with 403 additions and 339 deletions
1
.env
Normal file
1
.env
Normal file
|
@ -0,0 +1 @@
|
||||||
|
PYTHONPATH=jellyfin_kodi
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
*.pyo
|
*.pyo
|
||||||
|
__pycache__/
|
||||||
__local__/
|
__local__/
|
||||||
machine_guid
|
machine_guid
|
||||||
/resources/media/Thumbs.db
|
/resources/media/Thumbs.db
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
<import addon="xbmc.python" version="2.25.0"/>
|
<import addon="xbmc.python" version="2.25.0"/>
|
||||||
<import addon="script.module.requests" version="2.22.0"/>
|
<import addon="script.module.requests" version="2.22.0"/>
|
||||||
<import addon="script.module.dateutil" version="2.7.3"/>
|
<import addon="script.module.dateutil" version="2.7.3"/>
|
||||||
|
<import addon="script.module.six" />
|
||||||
|
<import addon="script.module.kodi-six" />
|
||||||
<import addon="script.module.addon.signals" version="0.0.1"/>
|
<import addon="script.module.addon.signals" version="0.0.1"/>
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.pluginsource"
|
<extension point="xbmc.python.pluginsource"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,13 +7,12 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
||||||
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
|
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
|
||||||
|
|
||||||
sys.path.insert(0, __base__)
|
sys.path.insert(0, __base__)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,13 +7,12 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
||||||
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
|
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
|
||||||
|
|
||||||
sys.path.insert(0, __base__)
|
sys.path.insert(0, __base__)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,13 +7,12 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
||||||
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
|
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
|
||||||
|
|
||||||
sys.path.insert(0, __base__)
|
sys.path.insert(0, __base__)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon, xbmcvfs
|
||||||
import xbmcaddon
|
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
from helper import translate, window, settings, addon_id, dialog
|
from helper import translate, window, settings, addon_id, dialog
|
||||||
from helper.utils import create_id
|
from helper.utils import create_id
|
||||||
|
@ -63,7 +62,7 @@ def get_device_name():
|
||||||
Otherwise fallback to the Kodi device name.
|
Otherwise fallback to the Kodi device name.
|
||||||
'''
|
'''
|
||||||
if not settings('deviceNameOpt.bool'):
|
if not settings('deviceNameOpt.bool'):
|
||||||
device_name = xbmc.getInfoLabel('System.FriendlyName').decode('utf-8')
|
device_name = xbmc.getInfoLabel('System.FriendlyName')
|
||||||
else:
|
else:
|
||||||
device_name = settings('deviceName')
|
device_name = settings('deviceName')
|
||||||
device_name = device_name.replace("\"", "_")
|
device_name = device_name.replace("\"", "_")
|
||||||
|
@ -86,7 +85,7 @@ def get_device_id(reset=False):
|
||||||
if client_id:
|
if client_id:
|
||||||
return client_id
|
return client_id
|
||||||
|
|
||||||
directory = xbmc.translatePath('special://profile/addon_data/plugin.video.jellyfin/').decode('utf-8')
|
directory = xbmc.translatePath('special://profile/addon_data/plugin.video.jellyfin/')
|
||||||
|
|
||||||
if not xbmcvfs.exists(directory):
|
if not xbmcvfs.exists(directory):
|
||||||
xbmcvfs.mkdir(directory)
|
xbmcvfs.mkdir(directory)
|
||||||
|
@ -98,7 +97,7 @@ def get_device_id(reset=False):
|
||||||
if not client_id or reset:
|
if not client_id or reset:
|
||||||
LOG.info("Generating a new GUID.")
|
LOG.info("Generating a new GUID.")
|
||||||
|
|
||||||
client_id = str("%012X" % create_id())
|
client_id = str(create_id())
|
||||||
file_guid = xbmcvfs.File(jellyfin_guid, 'w')
|
file_guid = xbmcvfs.File(jellyfin_guid, 'w')
|
||||||
file_guid.write(client_id)
|
file_guid.write(client_id)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
import client
|
import client
|
||||||
from database import get_credentials, save_credentials
|
from database import get_credentials, save_credentials
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -8,10 +8,10 @@ import json
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcvfs
|
||||||
import xbmcvfs
|
from six import text_type
|
||||||
|
|
||||||
import jellyfin_db
|
from database import jellyfin_db
|
||||||
from helper import translate, settings, window, dialog
|
from helper import translate, settings, window, dialog
|
||||||
from objects import obj
|
from objects import obj
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@ LOG = logging.getLogger("JELLYFIN." + __name__)
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
UNICODE = type(u"")
|
|
||||||
|
|
||||||
|
|
||||||
class Database(object):
|
class Database(object):
|
||||||
|
|
||||||
''' This should be called like a context.
|
''' This should be called like a context.
|
||||||
|
@ -76,7 +73,7 @@ class Database(object):
|
||||||
|
|
||||||
def _get_database(self, path, silent=False):
|
def _get_database(self, path, silent=False):
|
||||||
|
|
||||||
path = xbmc.translatePath(path).decode('utf-8')
|
path = xbmc.translatePath(path)
|
||||||
|
|
||||||
if not silent:
|
if not silent:
|
||||||
|
|
||||||
|
@ -104,7 +101,7 @@ class Database(object):
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
xbmc.sleep(200)
|
xbmc.sleep(200)
|
||||||
|
|
||||||
databases = xbmc.translatePath("special://database/").decode('utf-8')
|
databases = xbmc.translatePath("special://database/")
|
||||||
types = {
|
types = {
|
||||||
'video': "MyVideos",
|
'video': "MyVideos",
|
||||||
'music': "MyMusic",
|
'music': "MyMusic",
|
||||||
|
@ -118,19 +115,19 @@ class Database(object):
|
||||||
|
|
||||||
if (file.startswith(database) and not file.endswith('-wal') and not file.endswith('-shm') and not file.endswith('db-journal')):
|
if (file.startswith(database) and not file.endswith('-wal') and not file.endswith('-shm') and not file.endswith('db-journal')):
|
||||||
|
|
||||||
st = xbmcvfs.Stat(databases + file.decode('utf-8'))
|
st = xbmcvfs.Stat(databases + file)
|
||||||
modified_int = st.st_mtime()
|
modified_int = st.st_mtime()
|
||||||
LOG.debug("Database detected: %s time: %s", file.decode('utf-8'), modified_int)
|
LOG.debug("Database detected: %s time: %s", file, modified_int)
|
||||||
|
|
||||||
if modified_int > modified['time']:
|
if modified_int > modified['time']:
|
||||||
|
|
||||||
modified['time'] = modified_int
|
modified['time'] = modified_int
|
||||||
modified['file'] = file.decode('utf-8')
|
modified['file'] = file
|
||||||
|
|
||||||
LOG.info("Discovered database: %s", modified)
|
LOG.info("Discovered database: %s", modified)
|
||||||
self.discovered_file = modified['file']
|
self.discovered_file = modified['file']
|
||||||
|
|
||||||
return xbmc.translatePath("special://database/%s" % modified['file']).decode('utf-8')
|
return xbmc.translatePath("special://database/%s" % modified['file'])
|
||||||
|
|
||||||
def _sql(self, file):
|
def _sql(self, file):
|
||||||
|
|
||||||
|
@ -251,7 +248,7 @@ def reset():
|
||||||
if dialog("yesno", heading="{jellyfin}", line1=translate(33086)):
|
if dialog("yesno", heading="{jellyfin}", line1=translate(33086)):
|
||||||
reset_artwork()
|
reset_artwork()
|
||||||
|
|
||||||
addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
|
addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
|
||||||
|
|
||||||
if dialog("yesno", heading="{jellyfin}", line1=translate(33087)):
|
if dialog("yesno", heading="{jellyfin}", line1=translate(33087)):
|
||||||
|
|
||||||
|
@ -317,17 +314,17 @@ def reset_artwork():
|
||||||
|
|
||||||
''' Remove all existing texture.
|
''' Remove all existing texture.
|
||||||
'''
|
'''
|
||||||
thumbnails = xbmc.translatePath('special://thumbnails/').decode('utf-8')
|
thumbnails = xbmc.translatePath('special://thumbnails/')
|
||||||
|
|
||||||
if xbmcvfs.exists(thumbnails):
|
if xbmcvfs.exists(thumbnails):
|
||||||
dirs, ignore = xbmcvfs.listdir(thumbnails)
|
dirs, ignore = xbmcvfs.listdir(thumbnails)
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
ignore, thumbs = xbmcvfs.listdir(os.path.join(thumbnails, directory.decode('utf-8')))
|
ignore, thumbs = xbmcvfs.listdir(os.path.join(thumbnails, directory))
|
||||||
|
|
||||||
for thumb in thumbs:
|
for thumb in thumbs:
|
||||||
LOG.debug("DELETE thumbnail %s", thumb)
|
LOG.debug("DELETE thumbnail %s", thumb)
|
||||||
xbmcvfs.delete(os.path.join(thumbnails, directory.decode('utf-8'), thumb.decode('utf-8')))
|
xbmcvfs.delete(os.path.join(thumbnails, directory, thumb))
|
||||||
|
|
||||||
with Database('texture') as texdb:
|
with Database('texture') as texdb:
|
||||||
texdb.cursor.execute("SELECT tbl_name FROM sqlite_master WHERE type='table'")
|
texdb.cursor.execute("SELECT tbl_name FROM sqlite_master WHERE type='table'")
|
||||||
|
@ -343,7 +340,7 @@ def reset_artwork():
|
||||||
|
|
||||||
def get_sync():
|
def get_sync():
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
|
||||||
|
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
xbmcvfs.mkdirs(path)
|
xbmcvfs.mkdirs(path)
|
||||||
|
@ -364,7 +361,7 @@ def get_sync():
|
||||||
|
|
||||||
def save_sync(sync):
|
def save_sync(sync):
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
|
||||||
|
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
xbmcvfs.mkdirs(path)
|
xbmcvfs.mkdirs(path)
|
||||||
|
@ -373,14 +370,14 @@ def save_sync(sync):
|
||||||
|
|
||||||
with open(os.path.join(path, 'sync.json'), 'wb') as outfile:
|
with open(os.path.join(path, 'sync.json'), 'wb') as outfile:
|
||||||
data = json.dumps(sync, sort_keys=True, indent=4, ensure_ascii=False)
|
data = json.dumps(sync, sort_keys=True, indent=4, ensure_ascii=False)
|
||||||
if isinstance(data, UNICODE):
|
if isinstance(data, text_type):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
outfile.write(data)
|
outfile.write(data)
|
||||||
|
|
||||||
|
|
||||||
def get_credentials():
|
def get_credentials():
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
|
||||||
|
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
xbmcvfs.mkdirs(path)
|
xbmcvfs.mkdirs(path)
|
||||||
|
@ -424,14 +421,14 @@ def get_credentials():
|
||||||
|
|
||||||
def save_credentials(credentials):
|
def save_credentials(credentials):
|
||||||
credentials = credentials or {}
|
credentials = credentials or {}
|
||||||
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
|
||||||
|
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
xbmcvfs.mkdirs(path)
|
xbmcvfs.mkdirs(path)
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(path, 'data.json'), 'wb') as outfile:
|
with open(os.path.join(path, 'data.json'), 'wb') as outfile:
|
||||||
data = json.dumps(credentials, sort_keys=True, indent=4, ensure_ascii=False)
|
data = json.dumps(credentials, sort_keys=True, indent=4, ensure_ascii=False)
|
||||||
if isinstance(data, UNICODE):
|
if isinstance(data, text_type):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
outfile.write(data)
|
outfile.write(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import queries as QU
|
from database import queries as QU
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
get_item = """
|
get_item = """
|
||||||
SELECT kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type,
|
SELECT kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from serverconnect import ServerConnect
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
from usersconnect import UsersConnect
|
|
||||||
from loginmanual import LoginManual
|
from .serverconnect import ServerConnect
|
||||||
from servermanual import ServerManual
|
from .usersconnect import UsersConnect
|
||||||
|
from .loginmanual import LoginManual
|
||||||
|
from .servermanual import ServerManual
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmcgui
|
from kodi_six import xbmcgui, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
from helper import window, addon_id
|
from helper import window, addon_id
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmcgui
|
from six import iteritems
|
||||||
import xbmcaddon
|
from kodi_six import xbmcgui, xbmcaddon
|
||||||
|
|
||||||
from helper import translate, addon_id
|
from helper import translate, addon_id
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class LoginManual(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def set_args(self, **kwargs):
|
def set_args(self, **kwargs):
|
||||||
# connect_manager, user_image, servers
|
# connect_manager, user_image, servers
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in iteritems(kwargs):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def is_logged_in(self):
|
def is_logged_in(self):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcgui
|
||||||
import xbmcgui
|
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from six import iteritems
|
||||||
import xbmcgui
|
from kodi_six import xbmc, xbmcgui
|
||||||
|
|
||||||
from helper import translate
|
from helper import translate
|
||||||
from jellyfin.connection_manager import CONNECTION_STATE
|
from jellyfin.connection_manager import CONNECTION_STATE
|
||||||
|
@ -44,7 +45,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def set_args(self, **kwargs):
|
def set_args(self, **kwargs):
|
||||||
# connect_manager, user_image, servers
|
# connect_manager, user_image, servers
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in iteritems(kwargs):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def is_server_selected(self):
|
def is_server_selected(self):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -6,8 +7,8 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import xbmcgui
|
from six import iteritems
|
||||||
import xbmcaddon
|
from kodi_six import xbmcgui, xbmcaddon
|
||||||
|
|
||||||
from helper import translate, addon_id
|
from helper import translate, addon_id
|
||||||
from jellyfin.connection_manager import CONNECTION_STATE
|
from jellyfin.connection_manager import CONNECTION_STATE
|
||||||
|
@ -28,7 +29,7 @@ ERROR = {
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://stackoverflow.com/a/17871737/1035647
|
# https://stackoverflow.com/a/17871737/1035647
|
||||||
_IPV6_PATTERN = "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
_IPV6_PATTERN = r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
||||||
_IPV6_RE = re.compile(_IPV6_PATTERN)
|
_IPV6_RE = re.compile(_IPV6_PATTERN)
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ class ServerManual(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def set_args(self, **kwargs):
|
def set_args(self, **kwargs):
|
||||||
# connect_manager, user_image, servers, jellyfin_connect
|
# connect_manager, user_image, servers, jellyfin_connect
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in iteritems(kwargs):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from six import iteritems
|
||||||
import xbmcgui
|
from kodi_six import xbmc, xbmcgui
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class UsersConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def set_args(self, **kwargs):
|
def set_args(self, **kwargs):
|
||||||
# connect_manager, user_image, servers
|
# connect_manager, user_image, servers
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in iteritems(kwargs):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def is_user_selected(self):
|
def is_user_selected(self):
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import Queue
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import xbmc
|
from six.moves import queue as Queue
|
||||||
|
|
||||||
|
from kodi_six import xbmc
|
||||||
import requests
|
import requests
|
||||||
from helper import settings, stop, event, window, create_id
|
from helper import settings, stop, event, window, create_id
|
||||||
from jellyfin import Jellyfin
|
from jellyfin import Jellyfin
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcvfs
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
from helper import loghandler
|
from helper import loghandler
|
||||||
from jellyfin import Jellyfin
|
from jellyfin import Jellyfin
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,8 +7,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
import database
|
import database
|
||||||
from dialogs import context
|
from dialogs import context
|
||||||
|
@ -67,7 +67,7 @@ class Context(object):
|
||||||
elif self.select_menu():
|
elif self.select_menu():
|
||||||
self.action_menu()
|
self.action_menu()
|
||||||
|
|
||||||
if self._selected_option.decode('utf-8') in (OPTIONS['Delete'], OPTIONS['AddFav'], OPTIONS['RemoveFav']):
|
if self._selected_option in (OPTIONS['Delete'], OPTIONS['AddFav'], OPTIONS['RemoveFav']):
|
||||||
|
|
||||||
xbmc.sleep(500)
|
xbmc.sleep(500)
|
||||||
xbmc.executebuiltin('Container.Refresh')
|
xbmc.executebuiltin('Container.Refresh')
|
||||||
|
@ -91,7 +91,7 @@ class Context(object):
|
||||||
else:
|
else:
|
||||||
LOG.info("media is unknown")
|
LOG.info("media is unknown")
|
||||||
|
|
||||||
return media.decode('utf-8')
|
return media
|
||||||
|
|
||||||
def get_item_id(self):
|
def get_item_id(self):
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ class Context(object):
|
||||||
|
|
||||||
def action_menu(self):
|
def action_menu(self):
|
||||||
|
|
||||||
selected = self._selected_option.decode('utf-8')
|
selected = self._selected_option
|
||||||
|
|
||||||
if selected == OPTIONS['Refresh']:
|
if selected == OPTIONS['Refresh']:
|
||||||
TheVoid('RefreshItem', {'ServerId': self.server, 'Id': self.item['Id']})
|
TheVoid('RefreshItem', {'ServerId': self.server, 'Id': self.item['Id']})
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmc
|
from six.moves.urllib.parse import parse_qsl, urlencode
|
||||||
import xbmcvfs
|
from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon
|
||||||
import xbmcgui
|
|
||||||
import xbmcplugin
|
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
import client
|
import client
|
||||||
from database import reset, get_sync, Database, jellyfin_db, get_credentials
|
from database import reset, get_sync, Database, jellyfin_db, get_credentials
|
||||||
|
@ -39,7 +35,7 @@ class Events(object):
|
||||||
path = sys.argv[2]
|
path = sys.argv[2]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
params = dict(urlparse.parse_qsl(path[1:]))
|
params = dict(parse_qsl(path[1:]))
|
||||||
except Exception:
|
except Exception:
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
|
@ -146,7 +142,7 @@ def listing():
|
||||||
context = []
|
context = []
|
||||||
|
|
||||||
if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed') and view_id not in whitelist:
|
if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed') and view_id not in whitelist:
|
||||||
label = "%s %s" % (label.decode('utf-8'), translate(33166))
|
label = "%s %s" % (label, translate(33166))
|
||||||
context.append((translate(33123), "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)" % view_id))
|
context.append((translate(33123), "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)" % view_id))
|
||||||
|
|
||||||
if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist:
|
if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist:
|
||||||
|
@ -338,7 +334,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
|
||||||
'folder': item['Id'],
|
'folder': item['Id'],
|
||||||
'server': server_id
|
'server': server_id
|
||||||
}
|
}
|
||||||
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
context = []
|
context = []
|
||||||
|
|
||||||
if item['Type'] in ('Series', 'Season', 'Playlist'):
|
if item['Type'] in ('Series', 'Season', 'Playlist'):
|
||||||
|
@ -361,7 +357,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
|
||||||
'folder': 'genres-%s' % item['Id'],
|
'folder': 'genres-%s' % item['Id'],
|
||||||
'server': server_id
|
'server': server_id
|
||||||
}
|
}
|
||||||
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
list_li.append((path, li, True))
|
list_li.append((path, li, True))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -371,7 +367,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
|
||||||
'mode': "play",
|
'mode': "play",
|
||||||
'server': server_id
|
'server': server_id
|
||||||
}
|
}
|
||||||
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
li.setProperty('path', path)
|
li.setProperty('path', path)
|
||||||
context = [(translate(13412), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))]
|
context = [(translate(13412), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))]
|
||||||
|
|
||||||
|
@ -415,7 +411,7 @@ def browse_subfolders(media, view_id, server_id=None):
|
||||||
'folder': view_id if node[0] == 'all' else node[0],
|
'folder': view_id if node[0] == 'all' else node[0],
|
||||||
'server': server_id
|
'server': server_id
|
||||||
}
|
}
|
||||||
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
directory(node[1] or view['Name'], path)
|
directory(node[1] or view['Name'], path)
|
||||||
|
|
||||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||||
|
@ -440,7 +436,7 @@ def browse_letters(media, view_id, server_id=None):
|
||||||
'folder': 'firstletter-%s' % node,
|
'folder': 'firstletter-%s' % node,
|
||||||
'server': server_id
|
'server': server_id
|
||||||
}
|
}
|
||||||
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
directory(node, path)
|
directory(node, path)
|
||||||
|
|
||||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||||
|
@ -497,7 +493,7 @@ def get_fanart(item_id, path, server_id=None):
|
||||||
LOG.info("[ extra fanart ] %s", item_id)
|
LOG.info("[ extra fanart ] %s", item_id)
|
||||||
objects = Objects()
|
objects = Objects()
|
||||||
list_li = []
|
list_li = []
|
||||||
directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id).decode('utf-8')
|
directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id)
|
||||||
server = TheVoid('GetServerAddress', {'ServerId': server_id}).get()
|
server = TheVoid('GetServerAddress', {'ServerId': server_id}).get()
|
||||||
|
|
||||||
if not xbmcvfs.exists(directory):
|
if not xbmcvfs.exists(directory):
|
||||||
|
@ -520,7 +516,7 @@ def get_fanart(item_id, path, server_id=None):
|
||||||
dirs, files = xbmcvfs.listdir(directory)
|
dirs, files = xbmcvfs.listdir(directory)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
fanart = os.path.join(directory, file.decode('utf-8'))
|
fanart = os.path.join(directory, file)
|
||||||
li = xbmcgui.ListItem(file, path=fanart)
|
li = xbmcgui.ListItem(file, path=fanart)
|
||||||
list_li.append((fanart, li, False))
|
list_li.append((fanart, li, False))
|
||||||
|
|
||||||
|
@ -773,7 +769,7 @@ def get_themes():
|
||||||
from helper.playutils import PlayUtils
|
from helper.playutils import PlayUtils
|
||||||
from helper.xmls import tvtunes_nfo
|
from helper.xmls import tvtunes_nfo
|
||||||
|
|
||||||
library = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/library").decode('utf-8')
|
library = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/library")
|
||||||
play = settings('useDirectPaths') == "1"
|
play = settings('useDirectPaths') == "1"
|
||||||
|
|
||||||
if not xbmcvfs.exists(library + '/'):
|
if not xbmcvfs.exists(library + '/'):
|
||||||
|
@ -803,14 +799,14 @@ def get_themes():
|
||||||
|
|
||||||
for item in result['Items']:
|
for item in result['Items']:
|
||||||
|
|
||||||
folder = normalize_string(item['Name'].encode('utf-8'))
|
folder = normalize_string(item['Name'])
|
||||||
items[item['Id']] = folder
|
items[item['Id']] = folder
|
||||||
|
|
||||||
result = TheVoid('GetThemes', {'Type': "Song", 'Id': view}).get()
|
result = TheVoid('GetThemes', {'Type': "Song", 'Id': view}).get()
|
||||||
|
|
||||||
for item in result['Items']:
|
for item in result['Items']:
|
||||||
|
|
||||||
folder = normalize_string(item['Name'].encode('utf-8'))
|
folder = normalize_string(item['Name'])
|
||||||
items[item['Id']] = folder
|
items[item['Id']] = folder
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
|
@ -828,9 +824,9 @@ def get_themes():
|
||||||
putils = PlayUtils(theme, False, None, server, token)
|
putils = PlayUtils(theme, False, None, server, token)
|
||||||
|
|
||||||
if play:
|
if play:
|
||||||
paths.append(putils.direct_play(theme['MediaSources'][0]).encode('utf-8'))
|
paths.append(putils.direct_play(theme['MediaSources'][0]))
|
||||||
else:
|
else:
|
||||||
paths.append(putils.direct_url(theme['MediaSources'][0]).encode('utf-8'))
|
paths.append(putils.direct_url(theme['MediaSources'][0]))
|
||||||
|
|
||||||
tvtunes_nfo(nfo_file, paths)
|
tvtunes_nfo(nfo_file, paths)
|
||||||
|
|
||||||
|
@ -868,7 +864,7 @@ def backup():
|
||||||
|
|
||||||
delete_folder(backup)
|
delete_folder(backup)
|
||||||
|
|
||||||
addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin").decode('utf-8')
|
addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin")
|
||||||
destination_data = os.path.join(backup, "addon_data", "plugin.video.jellyfin")
|
destination_data = os.path.join(backup, "addon_data", "plugin.video.jellyfin")
|
||||||
destination_databases = os.path.join(backup, "Database")
|
destination_databases = os.path.join(backup, "Database")
|
||||||
|
|
||||||
|
@ -883,18 +879,18 @@ def backup():
|
||||||
|
|
||||||
databases = Objects().objects
|
databases = Objects().objects
|
||||||
|
|
||||||
db = xbmc.translatePath(databases['jellyfin']).decode('utf-8')
|
db = xbmc.translatePath(databases['jellyfin'])
|
||||||
xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit('\\', 1)[1]))
|
xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit('\\', 1)[1]))
|
||||||
LOG.info("copied jellyfin.db")
|
LOG.info("copied jellyfin.db")
|
||||||
|
|
||||||
db = xbmc.translatePath(databases['video']).decode('utf-8')
|
db = xbmc.translatePath(databases['video'])
|
||||||
filename = db.rsplit('\\', 1)[1]
|
filename = db.rsplit('\\', 1)[1]
|
||||||
xbmcvfs.copy(db, os.path.join(destination_databases, filename))
|
xbmcvfs.copy(db, os.path.join(destination_databases, filename))
|
||||||
LOG.info("copied %s", filename)
|
LOG.info("copied %s", filename)
|
||||||
|
|
||||||
if settings('enableMusic.bool'):
|
if settings('enableMusic.bool'):
|
||||||
|
|
||||||
db = xbmc.translatePath(databases['music']).decode('utf-8')
|
db = xbmc.translatePath(databases['music'])
|
||||||
filename = db.rsplit('\\', 1)[1]
|
filename = db.rsplit('\\', 1)[1]
|
||||||
xbmcvfs.copy(db, os.path.join(destination_databases, filename))
|
xbmcvfs.copy(db, os.path.join(destination_databases, filename))
|
||||||
LOG.info("copied %s", filename)
|
LOG.info("copied %s", filename)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -9,8 +10,8 @@ from datetime import datetime
|
||||||
|
|
||||||
# Workaround for threads using datetime: _striptime is locked
|
# Workaround for threads using datetime: _striptime is locked
|
||||||
import _strptime # noqa:F401
|
import _strptime # noqa:F401
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcgui
|
||||||
import xbmcgui
|
from six.moves import reload_module as reload
|
||||||
|
|
||||||
import objects
|
import objects
|
||||||
import connect
|
import connect
|
||||||
|
@ -181,8 +182,8 @@ class Service(xbmc.Monitor):
|
||||||
|
|
||||||
if settings('connectMsg.bool'):
|
if settings('connectMsg.bool'):
|
||||||
|
|
||||||
users = [user for user in (settings('additionalUsers') or "").decode('utf-8').split(',') if user]
|
users = [user for user in (settings('additionalUsers') or "").split(',') if user]
|
||||||
users.insert(0, settings('username').decode('utf-8'))
|
users.insert(0, settings('username'))
|
||||||
dialog("notification", heading="{jellyfin}", message="%s %s" % (translate(33000), ", ".join(users)),
|
dialog("notification", heading="{jellyfin}", message="%s %s" % (translate(33000), ", ".join(users)),
|
||||||
icon="{jellyfin}", time=1500, sound=False)
|
icon="{jellyfin}", time=1500, sound=False)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
import downloader as server
|
import downloader as server
|
||||||
import helper.xmls as xmls
|
import helper.xmls as xmls
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
from translate import translate
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
from exceptions import LibraryException
|
|
||||||
|
|
||||||
from utils import addon_id
|
from .translate import translate
|
||||||
from utils import window
|
from .exceptions import LibraryException
|
||||||
from utils import settings
|
|
||||||
from utils import kodi_version
|
|
||||||
from utils import dialog
|
|
||||||
from utils import find
|
|
||||||
from utils import event
|
|
||||||
from utils import validate
|
|
||||||
from utils import values
|
|
||||||
from utils import JSONRPC
|
|
||||||
from utils import indent
|
|
||||||
from utils import write_xml
|
|
||||||
from utils import compare_version
|
|
||||||
from utils import unzip
|
|
||||||
from utils import create_id
|
|
||||||
from utils import convert_to_local as Local
|
|
||||||
from utils import has_attribute
|
|
||||||
|
|
||||||
from wrapper import progress
|
from .utils import addon_id
|
||||||
from wrapper import catch
|
from .utils import window
|
||||||
from wrapper import silent_catch
|
from .utils import settings
|
||||||
from wrapper import stop
|
from .utils import kodi_version
|
||||||
from wrapper import jellyfin_item
|
from .utils import dialog
|
||||||
from wrapper import library_check
|
from .utils import find
|
||||||
|
from .utils import event
|
||||||
|
from .utils import validate
|
||||||
|
from .utils import values
|
||||||
|
from .utils import JSONRPC
|
||||||
|
from .utils import indent
|
||||||
|
from .utils import write_xml
|
||||||
|
from .utils import compare_version
|
||||||
|
from .utils import unzip
|
||||||
|
from .utils import create_id
|
||||||
|
from .utils import convert_to_local as Local
|
||||||
|
from .utils import has_attribute
|
||||||
|
|
||||||
|
from .wrapper import progress
|
||||||
|
from .wrapper import catch
|
||||||
|
from .wrapper import silent_catch
|
||||||
|
from .wrapper import stop
|
||||||
|
from .wrapper import jellyfin_item
|
||||||
|
from .wrapper import library_check
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
@ -9,8 +10,7 @@ import os
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
import database
|
import database
|
||||||
|
|
||||||
from . import window, settings
|
from . import window, settings
|
||||||
|
@ -18,7 +18,7 @@ from . import window, settings
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
||||||
__pluginpath__ = xbmc.translatePath(__addon__.getAddonInfo('path').decode('utf-8'))
|
__pluginpath__ = xbmc.translatePath(__addon__.getAddonInfo('path'))
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -65,15 +65,15 @@ class LogHandler(logging.StreamHandler):
|
||||||
|
|
||||||
if self.mask_info:
|
if self.mask_info:
|
||||||
for server in self.sensitive['Server']:
|
for server in self.sensitive['Server']:
|
||||||
string = string.replace(server.encode('utf-8') or "{server}", "{jellyfin-server}")
|
string = string.replace(server or "{server}", "{jellyfin-server}")
|
||||||
|
|
||||||
for token in self.sensitive['Token']:
|
for token in self.sensitive['Token']:
|
||||||
string = string.replace(token.encode('utf-8') or "{token}", "{jellyfin-token}")
|
string = string.replace(token or "{token}", "{jellyfin-token}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xbmc.log(string, level=xbmc.LOGNOTICE)
|
xbmc.log(string, level=xbmc.LOGNOTICE)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
xbmc.log(string.encode('utf-8'), level=xbmc.LOGNOTICE)
|
xbmc.log(string, level=xbmc.LOGNOTICE)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_log_level(cls, level):
|
def _get_log_level(cls, level):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -7,15 +8,13 @@ import os
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcvfs
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
import api
|
|
||||||
import client
|
import client
|
||||||
import requests
|
import requests
|
||||||
from downloader import TheVoid
|
from downloader import TheVoid
|
||||||
|
|
||||||
from . import translate, settings, window, dialog
|
from . import translate, settings, window, dialog, api
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -212,7 +211,7 @@ class PlayUtils(object):
|
||||||
self.item['PlaybackInfo'].update(self.info)
|
self.item['PlaybackInfo'].update(self.info)
|
||||||
|
|
||||||
API = api.API(self.item, self.info['ServerAddress'])
|
API = api.API(self.item, self.info['ServerAddress'])
|
||||||
window('jellyfinfilename', value=API.get_file_path(source.get('Path')).encode('utf-8'))
|
window('jellyfinfilename', value=API.get_file_path(source.get('Path')))
|
||||||
|
|
||||||
def live_stream(self, source):
|
def live_stream(self, source):
|
||||||
|
|
||||||
|
@ -484,7 +483,7 @@ class PlayUtils(object):
|
||||||
LOG.info("[ subtitles/%s ] %s", index, url)
|
LOG.info("[ subtitles/%s ] %s", index, url)
|
||||||
|
|
||||||
if 'Language' in stream:
|
if 'Language' in stream:
|
||||||
filename = "Stream.%s.%s" % (stream['Language'].encode('utf-8'), stream['Codec'].encode('utf-8'))
|
filename = "Stream.%s.%s" % (stream['Language'], stream['Codec'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subs.append(self.download_external_subs(url, filename))
|
subs.append(self.download_external_subs(url, filename))
|
||||||
|
@ -506,7 +505,7 @@ class PlayUtils(object):
|
||||||
''' Download external subtitles to temp folder
|
''' Download external subtitles to temp folder
|
||||||
to be able to have proper names to streams.
|
to be able to have proper names to streams.
|
||||||
'''
|
'''
|
||||||
temp = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/").decode('utf-8')
|
temp = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/")
|
||||||
|
|
||||||
if not xbmcvfs.exists(temp):
|
if not xbmcvfs.exists(temp):
|
||||||
xbmcvfs.mkdir(temp)
|
xbmcvfs.mkdir(temp)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -8,18 +9,16 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import urllib
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from dateutil import tz, parser
|
from dateutil import tz, parser
|
||||||
|
from six import text_type, string_types, iteritems
|
||||||
|
from six.moves.urllib.parse import quote_plus
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon, xbmcgui, xbmcvfs
|
||||||
import xbmcaddon
|
|
||||||
import xbmcgui
|
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
from translate import translate
|
from .translate import translate
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -122,7 +121,7 @@ def find(dict, item):
|
||||||
if item in dict:
|
if item in dict:
|
||||||
return dict[item]
|
return dict[item]
|
||||||
|
|
||||||
for key, value in sorted(dict.iteritems(), key=lambda (k, v): (v, k)):
|
for key, value in sorted(iteritems(dict), key=lambda kv: (kv[1], kv[0])):
|
||||||
|
|
||||||
if re.match(key, item, re.I):
|
if re.match(key, item, re.I):
|
||||||
return dict[key]
|
return dict[key]
|
||||||
|
@ -245,7 +244,7 @@ def validate(path):
|
||||||
if window('jellyfin_pathverified.bool'):
|
if window('jellyfin_pathverified.bool'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
path = path if os.path.supports_unicode_filenames else path.encode('utf-8')
|
path = path if os.path.supports_unicode_filenames else path
|
||||||
|
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
LOG.info("Could not find %s", path)
|
LOG.info("Could not find %s", path)
|
||||||
|
@ -291,14 +290,17 @@ def indent(elem, level=0):
|
||||||
|
|
||||||
|
|
||||||
def write_xml(content, file):
|
def write_xml(content, file):
|
||||||
with open(file, 'w') as infile:
|
if isinstance(content, text_type):
|
||||||
|
content = content.encode('utf-8')
|
||||||
|
|
||||||
|
with open(file, 'wb') as infile:
|
||||||
|
|
||||||
# replace apostrophes with double quotes only in xml keys, not texts
|
# replace apostrophes with double quotes only in xml keys, not texts
|
||||||
def replace_apostrophes(match):
|
def replace_apostrophes(match):
|
||||||
return match.group(0).replace("'", '"')
|
return match.group(0).replace(b"'", b'"')
|
||||||
content = re.sub("<(.*?)>", replace_apostrophes, content)
|
content = re.sub(b"<(.*?)>", replace_apostrophes, content)
|
||||||
|
|
||||||
content = content.replace('?>', ' standalone="yes" ?>', 1)
|
content = content.replace(b'?>', b' standalone="yes" ?>', 1)
|
||||||
infile.write(content)
|
infile.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
@ -312,7 +314,7 @@ def delete_folder(path):
|
||||||
delete_recursive(path, dirs)
|
delete_recursive(path, dirs)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
xbmcvfs.delete(os.path.join(path, file.decode('utf-8')))
|
xbmcvfs.delete(os.path.join(path, file))
|
||||||
|
|
||||||
xbmcvfs.delete(path)
|
xbmcvfs.delete(path)
|
||||||
|
|
||||||
|
@ -324,20 +326,20 @@ def delete_recursive(path, dirs):
|
||||||
''' Delete files and dirs recursively.
|
''' Delete files and dirs recursively.
|
||||||
'''
|
'''
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
dirs2, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
|
dirs2, files = xbmcvfs.listdir(os.path.join(path, directory))
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
xbmcvfs.delete(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
|
xbmcvfs.delete(os.path.join(path, directory, file))
|
||||||
|
|
||||||
delete_recursive(os.path.join(path, directory.decode('utf-8')), dirs2)
|
delete_recursive(os.path.join(path, directory), dirs2)
|
||||||
xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
|
xbmcvfs.rmdir(os.path.join(path, directory))
|
||||||
|
|
||||||
|
|
||||||
def unzip(path, dest, folder=None):
|
def unzip(path, dest, folder=None):
|
||||||
|
|
||||||
''' Unzip file. zipfile module seems to fail on android with badziperror.
|
''' Unzip file. zipfile module seems to fail on android with badziperror.
|
||||||
'''
|
'''
|
||||||
path = urllib.quote_plus(path)
|
path = quote_plus(path)
|
||||||
root = "zip://" + path + '/'
|
root = "zip://" + path + '/'
|
||||||
|
|
||||||
if folder:
|
if folder:
|
||||||
|
@ -352,7 +354,7 @@ def unzip(path, dest, folder=None):
|
||||||
unzip_recursive(root, dirs, dest)
|
unzip_recursive(root, dirs, dest)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
unzip_file(os.path.join(root, file.decode('utf-8')), os.path.join(dest, file.decode('utf-8')))
|
unzip_file(os.path.join(root, file), os.path.join(dest, file))
|
||||||
|
|
||||||
LOG.info("Unzipped %s", path)
|
LOG.info("Unzipped %s", path)
|
||||||
|
|
||||||
|
@ -361,8 +363,8 @@ def unzip_recursive(path, dirs, dest):
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
|
|
||||||
dirs_dir = os.path.join(path, directory.decode('utf-8'))
|
dirs_dir = os.path.join(path, directory)
|
||||||
dest_dir = os.path.join(dest, directory.decode('utf-8'))
|
dest_dir = os.path.join(dest, directory)
|
||||||
xbmcvfs.mkdir(dest_dir)
|
xbmcvfs.mkdir(dest_dir)
|
||||||
|
|
||||||
dirs2, files = xbmcvfs.listdir(dirs_dir)
|
dirs2, files = xbmcvfs.listdir(dirs_dir)
|
||||||
|
@ -371,7 +373,7 @@ def unzip_recursive(path, dirs, dest):
|
||||||
unzip_recursive(dirs_dir, dirs2, dest_dir)
|
unzip_recursive(dirs_dir, dirs2, dest_dir)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
unzip_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
|
unzip_file(os.path.join(dirs_dir, file), os.path.join(dest_dir, file))
|
||||||
|
|
||||||
|
|
||||||
def unzip_file(path, dest):
|
def unzip_file(path, dest):
|
||||||
|
@ -390,7 +392,7 @@ def get_zip_directory(path, folder):
|
||||||
return os.path.join(path, folder)
|
return os.path.join(path, folder)
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
result = get_zip_directory(os.path.join(path, directory.decode('utf-8')), folder)
|
result = get_zip_directory(os.path.join(path, directory), folder)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -408,7 +410,7 @@ def copytree(path, dest):
|
||||||
copy_recursive(path, dirs, dest)
|
copy_recursive(path, dirs, dest)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
copy_file(os.path.join(path, file.decode('utf-8')), os.path.join(dest, file.decode('utf-8')))
|
copy_file(os.path.join(path, file), os.path.join(dest, file))
|
||||||
|
|
||||||
LOG.info("Copied %s", path)
|
LOG.info("Copied %s", path)
|
||||||
|
|
||||||
|
@ -417,8 +419,8 @@ def copy_recursive(path, dirs, dest):
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
|
|
||||||
dirs_dir = os.path.join(path, directory.decode('utf-8'))
|
dirs_dir = os.path.join(path, directory)
|
||||||
dest_dir = os.path.join(dest, directory.decode('utf-8'))
|
dest_dir = os.path.join(dest, directory)
|
||||||
xbmcvfs.mkdir(dest_dir)
|
xbmcvfs.mkdir(dest_dir)
|
||||||
|
|
||||||
dirs2, files = xbmcvfs.listdir(dirs_dir)
|
dirs2, files = xbmcvfs.listdir(dirs_dir)
|
||||||
|
@ -427,7 +429,7 @@ def copy_recursive(path, dirs, dest):
|
||||||
copy_recursive(dirs_dir, dirs2, dest_dir)
|
copy_recursive(dirs_dir, dirs2, dest_dir)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
copy_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
|
copy_file(os.path.join(dirs_dir, file), os.path.join(dest_dir, file))
|
||||||
|
|
||||||
|
|
||||||
def copy_file(path, dest):
|
def copy_file(path, dest):
|
||||||
|
@ -458,7 +460,7 @@ def normalize_string(text):
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
|
|
||||||
text = text.rstrip('.')
|
text = text.rstrip('.')
|
||||||
text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
|
text = unicodedata.normalize('NFKD', text_type(text, 'utf-8')).encode('ascii', 'ignore')
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -475,7 +477,7 @@ def convert_to_local(date):
|
||||||
''' Convert the local datetime to local.
|
''' Convert the local datetime to local.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
date = parser.parse(date) if type(date) in (unicode, str) else date
|
date = parser.parse(date) if isinstance(date, string_types) else date
|
||||||
date = date.replace(tzinfo=tz.tzutc())
|
date = date.replace(tzinfo=tz.tzutc())
|
||||||
date = date.astimezone(tz.tzlocal())
|
date = date.astimezone(tz.tzlocal())
|
||||||
|
|
||||||
|
@ -485,6 +487,7 @@ def convert_to_local(date):
|
||||||
|
|
||||||
return str(date)
|
return str(date)
|
||||||
|
|
||||||
|
|
||||||
def has_attribute(obj, name):
|
def has_attribute(obj, name):
|
||||||
try:
|
try:
|
||||||
object.__getattribute__(obj, name)
|
object.__getattribute__(obj, name)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import xbmcgui
|
from kodi_six import xbmcgui
|
||||||
|
|
||||||
from .utils import should_stop
|
from .utils import should_stop
|
||||||
from .exceptions import LibraryException
|
from .exceptions import LibraryException
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,7 +7,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
from . import translate, indent, write_xml, dialog, settings
|
from . import translate, indent, write_xml, dialog, settings
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ def sources():
|
||||||
''' Create master lock compatible sources.
|
''' Create master lock compatible sources.
|
||||||
Also add the kodi.jellyfin.media source.
|
Also add the kodi.jellyfin.media source.
|
||||||
'''
|
'''
|
||||||
path = xbmc.translatePath("special://profile/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/")
|
||||||
file = os.path.join(path, 'sources.xml')
|
file = os.path.join(path, 'sources.xml')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -106,7 +107,7 @@ def advanced_settings():
|
||||||
if settings('useDirectPaths') != "0":
|
if settings('useDirectPaths') != "0":
|
||||||
return
|
return
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/")
|
||||||
file = os.path.join(path, 'advancedsettings.xml')
|
file = os.path.join(path, 'advancedsettings.xml')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from client import JellyfinClient
|
from .client import JellyfinClient
|
||||||
from helper import has_attribute
|
from helper import has_attribute
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
|
||||||
def jellyfin_url(client, handler):
|
def jellyfin_url(client, handler):
|
||||||
return "%s/%s" % (client.config.data['auth.server'], handler)
|
return "%s/%s" % (client.config.data['auth.server'], handler)
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import api
|
from . import api
|
||||||
from configuration import Config
|
from .configuration import Config
|
||||||
from http import HTTP
|
from .http import HTTP
|
||||||
from ws_client import WSClient
|
from .ws_client import WSClient
|
||||||
from connection_manager import ConnectionManager, CONNECTION_STATE
|
from .connection_manager import ConnectionManager, CONNECTION_STATE
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
''' This will hold all configs from the client.
|
''' This will hold all configs from the client.
|
||||||
Configuration set here will be used for the HTTP client.
|
Configuration set here will be used for the HTTP client.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -11,8 +12,8 @@ from distutils.version import LooseVersion
|
||||||
|
|
||||||
import urllib3
|
import urllib3
|
||||||
|
|
||||||
from credentials import Credentials
|
from .credentials import Credentials
|
||||||
from http import HTTP # noqa: I201,I100
|
from .http import HTTP # noqa: I201,I100
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -258,7 +259,7 @@ class ConnectionManager(object):
|
||||||
def _server_discovery(self):
|
def _server_discovery(self):
|
||||||
|
|
||||||
MULTI_GROUP = ("<broadcast>", 7359)
|
MULTI_GROUP = ("<broadcast>", 7359)
|
||||||
MESSAGE = "who is JellyfinServer?"
|
MESSAGE = b"who is JellyfinServer?"
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.settimeout(1.0) # This controls the socket.timeout exception
|
sock.settimeout(1.0) # This controls the socket.timeout exception
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -7,7 +8,8 @@ import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from exceptions import HTTPException
|
|
||||||
|
from .exceptions import HTTPException
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -49,13 +51,13 @@ class HTTP(object):
|
||||||
|
|
||||||
if '{server}' in string:
|
if '{server}' in string:
|
||||||
if self.config.data['auth.server']:
|
if self.config.data['auth.server']:
|
||||||
string = string.decode('utf-8').replace("{server}", self.config.data['auth.server'])
|
string = string.replace("{server}", self.config.data['auth.server'])
|
||||||
else:
|
else:
|
||||||
raise Exception("Server address not set.")
|
raise Exception("Server address not set.")
|
||||||
|
|
||||||
if '{UserId}'in string:
|
if '{UserId}'in string:
|
||||||
if self.config.data['auth.user_id']:
|
if self.config.data['auth.user_id']:
|
||||||
string = string.decode('utf-8').replace("{UserId}", self.config.data['auth.user_id'])
|
string = string.replace("{UserId}", self.config.data['auth.user_id'])
|
||||||
else:
|
else:
|
||||||
raise Exception("UserId is not set.")
|
raise Exception("UserId is not set.")
|
||||||
|
|
||||||
|
@ -209,17 +211,17 @@ class HTTP(object):
|
||||||
def _authorization(self, data):
|
def _authorization(self, data):
|
||||||
|
|
||||||
auth = "MediaBrowser "
|
auth = "MediaBrowser "
|
||||||
auth += "Client=%s, " % self.config.data['app.name'].encode('utf-8')
|
auth += "Client=%s, " % self.config.data['app.name']
|
||||||
auth += "Device=%s, " % self.config.data['app.device_name'].encode('utf-8')
|
auth += "Device=%s, " % self.config.data['app.device_name']
|
||||||
auth += "DeviceId=%s, " % self.config.data['app.device_id'].encode('utf-8')
|
auth += "DeviceId=%s, " % self.config.data['app.device_id']
|
||||||
auth += "Version=%s" % self.config.data['app.version'].encode('utf-8')
|
auth += "Version=%s" % self.config.data['app.version']
|
||||||
|
|
||||||
data['headers'].update({'x-emby-authorization': auth})
|
data['headers'].update({'x-emby-authorization': auth})
|
||||||
|
|
||||||
if self.config.data.get('auth.token') and self.config.data.get('auth.user_id'):
|
if self.config.data.get('auth.token') and self.config.data.get('auth.user_id'):
|
||||||
|
|
||||||
auth += ', UserId=%s' % self.config.data['auth.user_id'].encode('utf-8')
|
auth += ', UserId=%s' % self.config.data['auth.user_id']
|
||||||
data['headers'].update({'x-emby-authorization': auth, 'X-MediaBrowser-Token': self.config.data['auth.token'].encode('utf-8')})
|
data['headers'].update({'x-emby-authorization': auth, 'X-MediaBrowser-Token': self.config.data['auth.token']})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
"""
|
"""
|
||||||
websocket - WebSocket client library for Python
|
websocket - WebSocket client library for Python
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ except ImportError:
|
||||||
|
|
||||||
HAVE_SSL = False
|
HAVE_SSL = False
|
||||||
|
|
||||||
from urlparse import urlparse
|
|
||||||
import os
|
import os
|
||||||
import array
|
import array
|
||||||
import struct
|
import struct
|
||||||
|
@ -44,6 +45,10 @@ import threading
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from six import text_type, string_types, iteritems, int2byte, indexbytes
|
||||||
|
from six.moves import range
|
||||||
|
from six.moves.urllib.parse import urlparse
|
||||||
|
|
||||||
"""
|
"""
|
||||||
websocket python client.
|
websocket python client.
|
||||||
=========================
|
=========================
|
||||||
|
@ -221,7 +226,7 @@ def create_connection(url, timeout=None, **options):
|
||||||
|
|
||||||
|
|
||||||
_MAX_INTEGER = (1 << 32) - 1
|
_MAX_INTEGER = (1 << 32) - 1
|
||||||
_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
|
_AVAILABLE_KEY_CHARS = list(range(0x21, 0x2f + 1)) + list(range(0x3a, 0x7e + 1))
|
||||||
_MAX_CHAR_BYTE = (1 << 8) - 1
|
_MAX_CHAR_BYTE = (1 << 8) - 1
|
||||||
|
|
||||||
# ref. Websocket gets an update, and it breaks stuff.
|
# ref. Websocket gets an update, and it breaks stuff.
|
||||||
|
@ -304,7 +309,7 @@ class ABNF(object):
|
||||||
|
|
||||||
opcode: operation code. please see OPCODE_XXX.
|
opcode: operation code. please see OPCODE_XXX.
|
||||||
"""
|
"""
|
||||||
if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode):
|
if opcode == ABNF.OPCODE_TEXT and isinstance(data, text_type):
|
||||||
data = data.encode("utf-8")
|
data = data.encode("utf-8")
|
||||||
# mask must be set if send data from client
|
# mask must be set if send data from client
|
||||||
return ABNF(1, 0, 0, 0, opcode, 1, data)
|
return ABNF(1, 0, 0, 0, opcode, 1, data)
|
||||||
|
@ -321,14 +326,14 @@ class ABNF(object):
|
||||||
if length >= ABNF.LENGTH_63:
|
if length >= ABNF.LENGTH_63:
|
||||||
raise ValueError("data is too long")
|
raise ValueError("data is too long")
|
||||||
|
|
||||||
frame_header = chr(self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | self.opcode)
|
frame_header = int2byte(self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | self.opcode)
|
||||||
if length < ABNF.LENGTH_7:
|
if length < ABNF.LENGTH_7:
|
||||||
frame_header += chr(self.mask << 7 | length)
|
frame_header += int2byte(self.mask << 7 | length)
|
||||||
elif length < ABNF.LENGTH_16:
|
elif length < ABNF.LENGTH_16:
|
||||||
frame_header += chr(self.mask << 7 | 0x7e)
|
frame_header += int2byte(self.mask << 7 | 0x7e)
|
||||||
frame_header += struct.pack("!H", length)
|
frame_header += struct.pack("!H", length)
|
||||||
else:
|
else:
|
||||||
frame_header += chr(self.mask << 7 | 0x7f)
|
frame_header += int2byte(self.mask << 7 | 0x7f)
|
||||||
frame_header += struct.pack("!Q", length)
|
frame_header += struct.pack("!Q", length)
|
||||||
|
|
||||||
if not self.mask:
|
if not self.mask:
|
||||||
|
@ -352,7 +357,7 @@ class ABNF(object):
|
||||||
"""
|
"""
|
||||||
_m = array.array("B", mask_key)
|
_m = array.array("B", mask_key)
|
||||||
_d = array.array("B", data)
|
_d = array.array("B", data)
|
||||||
for i in xrange(len(_d)):
|
for i in range(len(_d)):
|
||||||
_d[i] ^= _m[i % 4]
|
_d[i] ^= _m[i % 4]
|
||||||
return _d.tostring()
|
return _d.tostring()
|
||||||
|
|
||||||
|
@ -475,31 +480,39 @@ class WebSocket(object):
|
||||||
self._handshake(hostname, port, resource, **options)
|
self._handshake(hostname, port, resource, **options)
|
||||||
|
|
||||||
def _handshake(self, host, port, resource, **options):
|
def _handshake(self, host, port, resource, **options):
|
||||||
|
if isinstance(host, string_types):
|
||||||
|
host = host.encode('utf-8')
|
||||||
|
if isinstance(resource, string_types):
|
||||||
|
resource = resource.encode('utf-8')
|
||||||
|
|
||||||
headers = []
|
headers = []
|
||||||
headers.append("GET %s HTTP/1.1" % resource)
|
headers.append(b"GET %s HTTP/1.1" % resource)
|
||||||
headers.append("Upgrade: websocket")
|
headers.append(b"Upgrade: websocket")
|
||||||
headers.append("Connection: Upgrade")
|
headers.append(b"Connection: Upgrade")
|
||||||
if port == 80:
|
if port == 80:
|
||||||
hostport = host
|
hostport = host
|
||||||
else:
|
else:
|
||||||
hostport = "%s:%d" % (host, port)
|
hostport = b"%s:%d" % (host, port)
|
||||||
headers.append("Host: %s" % hostport)
|
headers.append(b"Host: %s" % hostport)
|
||||||
|
|
||||||
if "origin" in options:
|
if "origin" in options:
|
||||||
headers.append("Origin: %s" % options["origin"])
|
headers.append(b"Origin: %s" % options["origin"])
|
||||||
else:
|
else:
|
||||||
headers.append("Origin: http://%s" % hostport)
|
headers.append(b"Origin: http://%s" % hostport)
|
||||||
|
|
||||||
key = _create_sec_websocket_key()
|
key = _create_sec_websocket_key()
|
||||||
headers.append("Sec-WebSocket-Key: %s" % key)
|
headers.append(b"Sec-WebSocket-Key: %s" % key)
|
||||||
headers.append("Sec-WebSocket-Version: %s" % VERSION)
|
headers.append(b"Sec-WebSocket-Version: %d" % VERSION)
|
||||||
if "header" in options:
|
if "header" in options:
|
||||||
headers.extend(options["header"])
|
for header in options["header"]:
|
||||||
|
if isinstance(header, string_types):
|
||||||
|
header = header.encode('utf-8')
|
||||||
|
headers.extend(header)
|
||||||
|
|
||||||
headers.append("")
|
headers.append(b"")
|
||||||
headers.append("")
|
headers.append(b"")
|
||||||
|
|
||||||
header_str = "\r\n".join(headers)
|
header_str = b"\r\n".join(headers)
|
||||||
self._send(header_str)
|
self._send(header_str)
|
||||||
if traceEnabled:
|
if traceEnabled:
|
||||||
logger.debug("--- request header ---")
|
logger.debug("--- request header ---")
|
||||||
|
@ -519,7 +532,7 @@ class WebSocket(object):
|
||||||
self.connected = True
|
self.connected = True
|
||||||
|
|
||||||
def _validate_header(self, headers, key):
|
def _validate_header(self, headers, key):
|
||||||
for k, v in _HEADERS_TO_CHECK.iteritems():
|
for k, v in iteritems(_HEADERS_TO_CHECK):
|
||||||
r = headers.get(k, None)
|
r = headers.get(k, None)
|
||||||
if not r:
|
if not r:
|
||||||
return False
|
return False
|
||||||
|
@ -653,13 +666,13 @@ class WebSocket(object):
|
||||||
# Header
|
# Header
|
||||||
if self._frame_header is None:
|
if self._frame_header is None:
|
||||||
self._frame_header = self._recv_strict(2)
|
self._frame_header = self._recv_strict(2)
|
||||||
b1 = ord(self._frame_header[0])
|
b1 = indexbytes(self._frame_header, 0)
|
||||||
fin = b1 >> 7 & 1
|
fin = b1 >> 7 & 1
|
||||||
rsv1 = b1 >> 6 & 1
|
rsv1 = b1 >> 6 & 1
|
||||||
rsv2 = b1 >> 5 & 1
|
rsv2 = b1 >> 5 & 1
|
||||||
rsv3 = b1 >> 4 & 1
|
rsv3 = b1 >> 4 & 1
|
||||||
opcode = b1 & 0xf
|
opcode = b1 & 0xf
|
||||||
b2 = ord(self._frame_header[1])
|
b2 = indexbytes(self._frame_header, 1)
|
||||||
has_mask = b2 >> 7 & 1
|
has_mask = b2 >> 7 & 1
|
||||||
# Frame length
|
# Frame length
|
||||||
if self._frame_length is None:
|
if self._frame_length is None:
|
||||||
|
@ -753,7 +766,7 @@ class WebSocket(object):
|
||||||
|
|
||||||
def _recv(self, bufsize):
|
def _recv(self, bufsize):
|
||||||
try:
|
try:
|
||||||
bytes = self.sock.recv(bufsize)
|
_bytes = self.sock.recv(bufsize)
|
||||||
except socket.timeout as e:
|
except socket.timeout as e:
|
||||||
raise WebSocketTimeoutException(e.args[0])
|
raise WebSocketTimeoutException(e.args[0])
|
||||||
except SSLError as e:
|
except SSLError as e:
|
||||||
|
@ -761,16 +774,16 @@ class WebSocket(object):
|
||||||
raise WebSocketTimeoutException(e.args[0])
|
raise WebSocketTimeoutException(e.args[0])
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
if not bytes:
|
if not _bytes:
|
||||||
raise WebSocketConnectionClosedException()
|
raise WebSocketConnectionClosedException()
|
||||||
return bytes
|
return _bytes
|
||||||
|
|
||||||
def _recv_strict(self, bufsize):
|
def _recv_strict(self, bufsize):
|
||||||
shortage = bufsize - sum(len(x) for x in self._recv_buffer)
|
shortage = bufsize - sum(len(x) for x in self._recv_buffer)
|
||||||
while shortage > 0:
|
while shortage > 0:
|
||||||
bytes = self._recv(shortage)
|
_bytes = self._recv(shortage)
|
||||||
self._recv_buffer.append(bytes)
|
self._recv_buffer.append(_bytes)
|
||||||
shortage -= len(bytes)
|
shortage -= len(_bytes)
|
||||||
unified = "".join(self._recv_buffer)
|
unified = "".join(self._recv_buffer)
|
||||||
if shortage == 0:
|
if shortage == 0:
|
||||||
self._recv_buffer = []
|
self._recv_buffer = []
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -6,9 +7,9 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
import websocket
|
from . import websocket
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import Queue
|
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import xbmc
|
from six.moves import queue as Queue
|
||||||
import xbmcgui
|
|
||||||
|
from kodi_six import xbmc, xbmcgui
|
||||||
|
|
||||||
from objects import Movies, TVShows, MusicVideos, Music
|
from objects import Movies, TVShows, MusicVideos, Music
|
||||||
from database import Database, jellyfin_db, get_sync, save_sync
|
from database import Database, jellyfin_db, get_sync, save_sync
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
import connect
|
import connect
|
||||||
import downloader
|
import downloader
|
||||||
|
@ -291,7 +292,7 @@ class Monitor(xbmc.Monitor):
|
||||||
for additional in users:
|
for additional in users:
|
||||||
for user in all_users:
|
for user in all_users:
|
||||||
|
|
||||||
if user['Name'].lower() in additional.decode('utf-8').lower():
|
if user['Name'].lower() in additional.lower():
|
||||||
server.jellyfin.session_add_user(server.config.data['app.session'], user['Id'], True)
|
server.jellyfin.session_add_user(server.config.data['app.session'], user['Id'], True)
|
||||||
|
|
||||||
self.additional_users(server)
|
self.additional_users(server)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from movies import Movies
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
from musicvideos import MusicVideos
|
|
||||||
from tvshows import TVShows
|
from .movies import Movies
|
||||||
from music import Music
|
from .musicvideos import MusicVideos
|
||||||
from obj import Objects
|
from .tvshows import TVShows
|
||||||
from actions import Actions
|
from .music import Music
|
||||||
from actions import PlaylistWorker
|
from .obj import Objects
|
||||||
from actions import on_play, on_update, special_listener
|
from .actions import Actions
|
||||||
import utils
|
from .actions import PlaylistWorker
|
||||||
|
from .actions import on_play, on_update, special_listener
|
||||||
|
from . import utils
|
||||||
|
|
||||||
Objects().mapping()
|
Objects().mapping()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -7,14 +8,11 @@ import threading
|
||||||
import sys
|
import sys
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcgui, xbmcplugin, xbmcaddon
|
||||||
import xbmcgui
|
|
||||||
import xbmcplugin
|
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
import database
|
import database
|
||||||
from downloader import TheVoid
|
from downloader import TheVoid
|
||||||
from obj import Objects
|
from .obj import Objects
|
||||||
from helper import translate, playutils, api, window, settings, dialog
|
from helper import translate, playutils, api, window, settings, dialog
|
||||||
from dialogs import resume
|
from dialogs import resume
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from kodi import Kodi
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
from movies import Movies
|
|
||||||
from musicvideos import MusicVideos
|
from .kodi import Kodi
|
||||||
from tvshows import TVShows
|
from .movies import Movies
|
||||||
from music import Music
|
from .musicvideos import MusicVideos
|
||||||
from artwork import Artwork
|
from .tvshows import TVShows
|
||||||
|
from .music import Music
|
||||||
|
from .artwork import Artwork
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
|
||||||
import Queue
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import xbmc
|
from six.moves import queue as Queue
|
||||||
import xbmcvfs
|
from six.moves.urllib.parse import urlencode
|
||||||
|
|
||||||
import queries as QU
|
from kodi_six import xbmc, xbmcvfs
|
||||||
import queries_texture as QUTEX
|
|
||||||
|
from . import queries as QU
|
||||||
|
from . import queries_texture as QUTEX
|
||||||
from helper import settings
|
from helper import settings
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -139,10 +140,10 @@ class Artwork(object):
|
||||||
''' urlencode needs a utf-string.
|
''' urlencode needs a utf-string.
|
||||||
return the result as unicode
|
return the result as unicode
|
||||||
'''
|
'''
|
||||||
text = urllib.urlencode({'blahblahblah': text.encode('utf-8')})
|
text = urlencode({'blahblahblah': text})
|
||||||
text = text[13:]
|
text = text[13:]
|
||||||
|
|
||||||
return text.decode('utf-8')
|
return text
|
||||||
|
|
||||||
def add_worker(self):
|
def add_worker(self):
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ class Artwork(object):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
LOG.debug("Could not find cached url: %s", url)
|
LOG.debug("Could not find cached url: %s", url)
|
||||||
else:
|
else:
|
||||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached).decode('utf-8')
|
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached)
|
||||||
xbmcvfs.delete(thumbnails)
|
xbmcvfs.delete(thumbnails)
|
||||||
texturedb.cursor.execute(QUTEX.delete_cache, (url,))
|
texturedb.cursor.execute(QUTEX.delete_cache, (url,))
|
||||||
LOG.info("DELETE cached %s", cached)
|
LOG.info("DELETE cached %s", cached)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import artwork
|
from . import artwork
|
||||||
import queries as QU
|
from . import queries as QU
|
||||||
from helper import values
|
from helper import values
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from kodi import Kodi
|
from .kodi import Kodi
|
||||||
import queries as QU
|
from . import queries as QU
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import queries_music as QU
|
from . import queries_music as QU
|
||||||
from kodi import Kodi
|
from .kodi import Kodi
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import queries as QU
|
from . import queries as QU
|
||||||
from kodi import Kodi
|
from .kodi import Kodi
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
''' Queries for the Kodi database. obj reflect key/value to retrieve from jellyfin items.
|
''' Queries for the Kodi database. obj reflect key/value to retrieve from jellyfin items.
|
||||||
Some functions require additional information, therefore obj do not always reflect
|
Some functions require additional information, therefore obj do not always reflect
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
create_artist = """
|
create_artist = """
|
||||||
SELECT coalesce(max(idArtist), 1)
|
SELECT coalesce(max(idArtist), 1)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
get_cache = """
|
get_cache = """
|
||||||
SELECT cachedurl
|
SELECT cachedurl
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import queries as QU
|
from . import queries as QU
|
||||||
from kodi import Kodi
|
from .kodi import Kodi
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
from six.moves.urllib.parse import urlencode
|
||||||
|
|
||||||
import downloader as server
|
import downloader as server
|
||||||
from obj import Objects
|
from .obj import Objects
|
||||||
from kodi import Movies as KodiDb, queries as QU
|
from .kodi import Movies as KodiDb, queries as QU
|
||||||
from database import jellyfin_db, queries as QUEM
|
from database import jellyfin_db, queries as QUEM
|
||||||
from helper import api, stop, validate, jellyfin_item, library_check, values, settings, Local
|
from helper import api, stop, validate, jellyfin_item, library_check, values, settings, Local
|
||||||
|
|
||||||
|
@ -177,12 +178,12 @@ class Movies(KodiDb):
|
||||||
else:
|
else:
|
||||||
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
|
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
|
||||||
params = {
|
params = {
|
||||||
'filename': obj['Filename'].encode('utf-8'),
|
'filename': obj['Filename'],
|
||||||
'id': obj['Id'],
|
'id': obj['Id'],
|
||||||
'dbid': obj['MovieId'],
|
'dbid': obj['MovieId'],
|
||||||
'mode': "play"
|
'mode': "play"
|
||||||
}
|
}
|
||||||
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
|
obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
|
||||||
|
|
||||||
@stop()
|
@stop()
|
||||||
@jellyfin_item()
|
@jellyfin_item()
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from obj import Objects
|
from .obj import Objects
|
||||||
from kodi import Music as KodiDb, queries_music as QU
|
from .kodi import Music as KodiDb, queries_music as QU
|
||||||
from database import jellyfin_db, queries as QUEM
|
from database import jellyfin_db, queries as QUEM
|
||||||
from helper import api, stop, validate, jellyfin_item, values, library_check, Local
|
from helper import api, stop, validate, jellyfin_item, values, library_check, Local
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import urllib
|
from six.moves.urllib.parse import urlencode
|
||||||
|
|
||||||
from obj import Objects
|
from .obj import Objects
|
||||||
from kodi import MusicVideos as KodiDb, queries as QU
|
from .kodi import MusicVideos as KodiDb, queries as QU
|
||||||
from database import jellyfin_db, queries as QUEM
|
from database import jellyfin_db, queries as QUEM
|
||||||
from helper import api, stop, validate, library_check, jellyfin_item, values, Local
|
from helper import api, stop, validate, library_check, jellyfin_item, values, Local
|
||||||
|
|
||||||
|
@ -165,12 +166,12 @@ class MusicVideos(KodiDb):
|
||||||
else:
|
else:
|
||||||
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
|
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
|
||||||
params = {
|
params = {
|
||||||
'filename': obj['Filename'].encode('utf-8'),
|
'filename': obj['Filename'],
|
||||||
'id': obj['Id'],
|
'id': obj['Id'],
|
||||||
'dbid': obj['MvideoId'],
|
'dbid': obj['MvideoId'],
|
||||||
'mode': "play"
|
'mode': "play"
|
||||||
}
|
}
|
||||||
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
|
obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
|
||||||
|
|
||||||
@stop()
|
@stop()
|
||||||
@jellyfin_item()
|
@jellyfin_item()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -6,6 +7,8 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
LOG = logging.getLogger("JELLYFIN." + __name__)
|
LOG = logging.getLogger("JELLYFIN." + __name__)
|
||||||
|
@ -53,7 +56,7 @@ class Objects(object):
|
||||||
|
|
||||||
mapping = self.objects[mapping_name]
|
mapping = self.objects[mapping_name]
|
||||||
|
|
||||||
for key, value in mapping.iteritems():
|
for key, value in iteritems(mapping):
|
||||||
|
|
||||||
self.mapped_item[key] = None
|
self.mapped_item[key] = None
|
||||||
params = value.split(',')
|
params = value.split(',')
|
||||||
|
@ -144,7 +147,7 @@ class Objects(object):
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
for key, value in filters.iteritems():
|
for key, value in iteritems(filters):
|
||||||
|
|
||||||
inverse = False
|
inverse = False
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import urllib
|
|
||||||
from ntpath import dirname
|
from ntpath import dirname
|
||||||
|
|
||||||
from obj import Objects
|
from six.moves.urllib.parse import urlencode
|
||||||
from kodi import TVShows as KodiDb, queries as QU
|
|
||||||
|
from .obj import Objects
|
||||||
|
from .kodi import TVShows as KodiDb, queries as QU
|
||||||
import downloader as server
|
import downloader as server
|
||||||
from database import jellyfin_db, queries as QUEM
|
from database import jellyfin_db, queries as QUEM
|
||||||
from helper import api, stop, validate, jellyfin_item, library_check, settings, values, Local
|
from helper import api, stop, validate, jellyfin_item, library_check, settings, values, Local
|
||||||
|
@ -392,12 +394,12 @@ class TVShows(KodiDb):
|
||||||
else:
|
else:
|
||||||
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['SeriesId']
|
obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['SeriesId']
|
||||||
params = {
|
params = {
|
||||||
'filename': obj['Filename'].encode('utf-8'),
|
'filename': obj['Filename'],
|
||||||
'id': obj['Id'],
|
'id': obj['Id'],
|
||||||
'dbid': obj['EpisodeId'],
|
'dbid': obj['EpisodeId'],
|
||||||
'mode': "play"
|
'mode': "play"
|
||||||
}
|
}
|
||||||
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
|
obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
|
||||||
|
|
||||||
def get_show_id(self, obj):
|
def get_show_id(self, obj):
|
||||||
obj['ShowId'] = self.jellyfin_db.get_item_by_id(*values(obj, QUEM.get_item_series_obj))
|
obj['ShowId'] = self.jellyfin_db.get_item_by_id(*values(obj, QUEM.get_item_series_obj))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcvfs
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
from objects.obj import Objects
|
from objects.obj import Objects
|
||||||
from helper import translate, api, window, settings, dialog, event, silent_catch, JSONRPC
|
from helper import translate, api, window, settings, dialog, event, silent_catch, JSONRPC
|
||||||
|
@ -85,7 +85,7 @@ class Player(xbmc.Player):
|
||||||
return
|
return
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
if item['Path'] == current_file.decode('utf-8'):
|
if item['Path'] == current_file:
|
||||||
items.pop(items.index(item))
|
items.pop(items.index(item))
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -412,13 +412,13 @@ class Player(xbmc.Player):
|
||||||
LOG.info("<[ transcode/%s ]", item['Id'])
|
LOG.info("<[ transcode/%s ]", item['Id'])
|
||||||
item['Server'].jellyfin.close_transcode(item['DeviceId'])
|
item['Server'].jellyfin.close_transcode(item['DeviceId'])
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/")
|
||||||
|
|
||||||
if xbmcvfs.exists(path):
|
if xbmcvfs.exists(path):
|
||||||
dirs, files = xbmcvfs.listdir(path)
|
dirs, files = xbmcvfs.listdir(path)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
xbmcvfs.delete(os.path.join(path, file.decode('utf-8')))
|
xbmcvfs.delete(os.path.join(path, file))
|
||||||
|
|
||||||
result = item['Server'].jellyfin.get_item(item['Id']) or {}
|
result = item['Server'].jellyfin.get_item(item['Id']) or {}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import urllib
|
|
||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
|
|
||||||
import xbmc
|
from six.moves.urllib.parse import urlencode
|
||||||
import xbmcvfs
|
from kodi_six import xbmc, xbmcvfs
|
||||||
|
|
||||||
from database import Database, jellyfin_db, get_sync, save_sync
|
from database import Database, jellyfin_db, get_sync, save_sync
|
||||||
from helper import translate, api, indent, write_xml, window, event
|
from helper import translate, api, indent, write_xml, window, event
|
||||||
|
@ -108,13 +108,13 @@ def verify_kodi_defaults():
|
||||||
|
|
||||||
''' Make sure we have the kodi default folder in place.
|
''' Make sure we have the kodi default folder in place.
|
||||||
'''
|
'''
|
||||||
node_path = xbmc.translatePath("special://profile/library/video").decode('utf-8')
|
node_path = xbmc.translatePath("special://profile/library/video")
|
||||||
|
|
||||||
if not xbmcvfs.exists(node_path):
|
if not xbmcvfs.exists(node_path):
|
||||||
try:
|
try:
|
||||||
shutil.copytree(
|
shutil.copytree(
|
||||||
src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
|
src=xbmc.translatePath("special://xbmc/system/library/video"),
|
||||||
dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
|
dst=xbmc.translatePath("special://profile/library/video"))
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
LOG.warning(error)
|
LOG.warning(error)
|
||||||
xbmcvfs.mkdir(node_path)
|
xbmcvfs.mkdir(node_path)
|
||||||
|
@ -129,7 +129,7 @@ def verify_kodi_defaults():
|
||||||
indent(xml)
|
indent(xml)
|
||||||
write_xml(etree.tostring(xml, 'UTF-8'), file)
|
write_xml(etree.tostring(xml, 'UTF-8'), file)
|
||||||
|
|
||||||
playlist_path = xbmc.translatePath("special://profile/playlists/video").decode('utf-8')
|
playlist_path = xbmc.translatePath("special://profile/playlists/video")
|
||||||
|
|
||||||
if not xbmcvfs.exists(playlist_path):
|
if not xbmcvfs.exists(playlist_path):
|
||||||
xbmcvfs.mkdirs(playlist_path)
|
xbmcvfs.mkdirs(playlist_path)
|
||||||
|
@ -223,8 +223,8 @@ class Views(object):
|
||||||
|
|
||||||
''' Set up playlists, video nodes, window prop.
|
''' Set up playlists, video nodes, window prop.
|
||||||
'''
|
'''
|
||||||
node_path = xbmc.translatePath("special://profile/library/video").decode('utf-8')
|
node_path = xbmc.translatePath("special://profile/library/video")
|
||||||
playlist_path = xbmc.translatePath("special://profile/playlists/video").decode('utf-8')
|
playlist_path = xbmc.translatePath("special://profile/playlists/video")
|
||||||
index = 0
|
index = 0
|
||||||
|
|
||||||
with Database('jellyfin') as jellyfindb:
|
with Database('jellyfin') as jellyfindb:
|
||||||
|
@ -779,16 +779,16 @@ class Views(object):
|
||||||
|
|
||||||
window_prop = "Jellyfin.nodes.%s" % index
|
window_prop = "Jellyfin.nodes.%s" % index
|
||||||
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
|
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
|
||||||
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
|
window('%s.title' % window_prop, view['Name'])
|
||||||
window('%s.content' % window_prop, path)
|
window('%s.content' % window_prop, path)
|
||||||
|
|
||||||
elif node == 'browse':
|
elif node == 'browse':
|
||||||
|
|
||||||
window_prop = "Jellyfin.nodes.%s" % index
|
window_prop = "Jellyfin.nodes.%s" % index
|
||||||
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
|
window('%s.title' % window_prop, view['Name'])
|
||||||
else:
|
else:
|
||||||
window_prop = "Jellyfin.nodes.%s.%s" % (index, node)
|
window_prop = "Jellyfin.nodes.%s.%s" % (index, node)
|
||||||
window('%s.title' % window_prop, node_label.encode('utf-8'))
|
window('%s.title' % window_prop, node_label)
|
||||||
window('%s.content' % window_prop, path)
|
window('%s.content' % window_prop, path)
|
||||||
|
|
||||||
window('%s.id' % window_prop, view['Id'])
|
window('%s.id' % window_prop, view['Id'])
|
||||||
|
@ -831,17 +831,17 @@ class Views(object):
|
||||||
|
|
||||||
window_prop = "Jellyfin.wnodes.%s" % index
|
window_prop = "Jellyfin.wnodes.%s" % index
|
||||||
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
|
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
|
||||||
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
|
window('%s.title' % window_prop, view['Name'])
|
||||||
window('%s.content' % window_prop, path)
|
window('%s.content' % window_prop, path)
|
||||||
|
|
||||||
elif node == 'browse':
|
elif node == 'browse':
|
||||||
|
|
||||||
window_prop = "Jellyfin.wnodes.%s" % index
|
window_prop = "Jellyfin.wnodes.%s" % index
|
||||||
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
|
window('%s.title' % window_prop, view['Name'])
|
||||||
window('%s.content' % window_prop, path)
|
window('%s.content' % window_prop, path)
|
||||||
else:
|
else:
|
||||||
window_prop = "Jellyfin.wnodes.%s.%s" % (index, node)
|
window_prop = "Jellyfin.wnodes.%s.%s" % (index, node)
|
||||||
window('%s.title' % window_prop, node_label.encode('utf-8'))
|
window('%s.title' % window_prop, node_label)
|
||||||
window('%s.content' % window_prop, path)
|
window('%s.content' % window_prop, path)
|
||||||
|
|
||||||
window('%s.id' % window_prop, view['Id'])
|
window('%s.id' % window_prop, view['Id'])
|
||||||
|
@ -881,7 +881,7 @@ class Views(object):
|
||||||
'mode': "nextepisodes",
|
'mode': "nextepisodes",
|
||||||
'limit': self.limit
|
'limit': self.limit
|
||||||
}
|
}
|
||||||
return "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
return "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
|
|
||||||
def window_browse(self, view, node=None):
|
def window_browse(self, view, node=None):
|
||||||
|
|
||||||
|
@ -896,7 +896,7 @@ class Views(object):
|
||||||
if node:
|
if node:
|
||||||
params['folder'] = node
|
params['folder'] = node
|
||||||
|
|
||||||
return "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
|
return "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
|
||||||
|
|
||||||
def window_clear(self, name=None):
|
def window_clear(self, name=None):
|
||||||
|
|
||||||
|
@ -932,23 +932,23 @@ class Views(object):
|
||||||
|
|
||||||
''' Remove all jellyfin playlists.
|
''' Remove all jellyfin playlists.
|
||||||
'''
|
'''
|
||||||
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/playlists/video/")
|
||||||
_, files = xbmcvfs.listdir(path)
|
_, files = xbmcvfs.listdir(path)
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.decode('utf-8').startswith('jellyfin'):
|
if file.startswith('jellyfin'):
|
||||||
self.delete_playlist(os.path.join(path, file.decode('utf-8')))
|
self.delete_playlist(os.path.join(path, file))
|
||||||
|
|
||||||
def delete_playlist_by_id(self, view_id):
|
def delete_playlist_by_id(self, view_id):
|
||||||
|
|
||||||
''' Remove playlist based based on view_id.
|
''' Remove playlist based based on view_id.
|
||||||
'''
|
'''
|
||||||
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/playlists/video/")
|
||||||
_, files = xbmcvfs.listdir(path)
|
_, files = xbmcvfs.listdir(path)
|
||||||
for file in files:
|
for file in files:
|
||||||
file = file.decode('utf-8')
|
file = file
|
||||||
|
|
||||||
if file.startswith('jellyfin') and file.endswith('%s.xsp' % view_id):
|
if file.startswith('jellyfin') and file.endswith('%s.xsp' % view_id):
|
||||||
self.delete_playlist(os.path.join(path, file.decode('utf-8')))
|
self.delete_playlist(os.path.join(path, file))
|
||||||
|
|
||||||
def delete_node(self, path):
|
def delete_node(self, path):
|
||||||
|
|
||||||
|
@ -959,37 +959,37 @@ class Views(object):
|
||||||
|
|
||||||
''' Remove node and children files.
|
''' Remove node and children files.
|
||||||
'''
|
'''
|
||||||
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/library/video/")
|
||||||
dirs, files = xbmcvfs.listdir(path)
|
dirs, files = xbmcvfs.listdir(path)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
|
|
||||||
if file.startswith('jellyfin'):
|
if file.startswith('jellyfin'):
|
||||||
self.delete_node(os.path.join(path, file.decode('utf-8')))
|
self.delete_node(os.path.join(path, file))
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
|
|
||||||
if directory.startswith('jellyfin'):
|
if directory.startswith('jellyfin'):
|
||||||
_, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
|
_, files = xbmcvfs.listdir(os.path.join(path, directory))
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
self.delete_node(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
|
self.delete_node(os.path.join(path, directory, file))
|
||||||
|
|
||||||
xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
|
xbmcvfs.rmdir(os.path.join(path, directory))
|
||||||
|
|
||||||
def delete_node_by_id(self, view_id):
|
def delete_node_by_id(self, view_id):
|
||||||
|
|
||||||
''' Remove node and children files based on view_id.
|
''' Remove node and children files based on view_id.
|
||||||
'''
|
'''
|
||||||
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/library/video/")
|
||||||
dirs, files = xbmcvfs.listdir(path)
|
dirs, files = xbmcvfs.listdir(path)
|
||||||
|
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
|
|
||||||
if directory.startswith('jellyfin') and directory.endswith(view_id):
|
if directory.startswith('jellyfin') and directory.endswith(view_id):
|
||||||
_, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
|
_, files = xbmcvfs.listdir(os.path.join(path, directory))
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
self.delete_node(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
|
self.delete_node(os.path.join(path, directory, file))
|
||||||
|
|
||||||
xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
|
xbmcvfs.rmdir(os.path.join(path, directory))
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import BaseHTTPServer
|
from six.moves import BaseHTTPServer
|
||||||
import logging
|
import logging
|
||||||
import httplib
|
from six.moves import http_client as httplib
|
||||||
import threading
|
import threading
|
||||||
import urlparse
|
from six.moves.urllib.parse import parse_qsl
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
if '?' in path:
|
if '?' in path:
|
||||||
path = path.split('?', 1)[1]
|
path = path.split('?', 1)[1]
|
||||||
|
|
||||||
params = dict(urlparse.parse_qsl(path))
|
params = dict(parse_qsl(path))
|
||||||
except Exception:
|
except Exception:
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
|
|
14
service.py
14
service.py
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
@ -7,13 +8,12 @@ import os
|
||||||
import threading
|
import threading
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import xbmc
|
from kodi_six import xbmc, xbmcaddon
|
||||||
import xbmcaddon
|
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
__addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
|
||||||
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
|
__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
|
||||||
|
|
||||||
sys.path.insert(0, __base__)
|
sys.path.insert(0, __base__)
|
||||||
|
|
||||||
|
@ -54,11 +54,11 @@ class ServiceManager(threading.Thread):
|
||||||
LOG.exception(error)
|
LOG.exception(error)
|
||||||
|
|
||||||
if service is not None:
|
if service is not None:
|
||||||
|
# TODO: fix this properly as to not match on str()
|
||||||
if 'ExitService' not in error:
|
if 'ExitService' not in str(error):
|
||||||
service.shutdown()
|
service.shutdown()
|
||||||
|
|
||||||
if 'RestartService' in error:
|
if 'RestartService' in str(error):
|
||||||
service.reload_objects()
|
service.reload_objects()
|
||||||
|
|
||||||
self.exception = error
|
self.exception = error
|
||||||
|
@ -79,7 +79,7 @@ if __name__ == "__main__":
|
||||||
session.start()
|
session.start()
|
||||||
session.join() # Block until the thread exits.
|
session.join() # Block until the thread exits.
|
||||||
|
|
||||||
if 'RestartService' in session.exception:
|
if 'RestartService' in str(session.exception):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
|
|
Loading…
Add table
Reference in a new issue