Prettify
This commit is contained in:
parent
406c2b9f63
commit
ca8ad96a05
19 changed files with 301 additions and 362 deletions
|
@ -32,7 +32,7 @@ sys_path.append(_base_resource)
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import entrypoint
|
import entrypoint
|
||||||
from utils import window, reset, passwordsXML, language as lang, dialog, \
|
from utils import window, reset, passwords_xml, language as lang, dialog, \
|
||||||
plex_command
|
plex_command
|
||||||
from pickler import unpickle_me, pickl_window
|
from pickler import unpickle_me, pickl_window
|
||||||
from PKC_listitem import convert_PKC_to_listitem
|
from PKC_listitem import convert_PKC_to_listitem
|
||||||
|
@ -115,7 +115,7 @@ class Main():
|
||||||
entrypoint.resetAuth()
|
entrypoint.resetAuth()
|
||||||
|
|
||||||
elif mode == 'passwords':
|
elif mode == 'passwords':
|
||||||
passwordsXML()
|
passwords_xml()
|
||||||
|
|
||||||
elif mode == 'switchuser':
|
elif mode == 'switchuser':
|
||||||
entrypoint.switchPlexUser()
|
entrypoint.switchPlexUser()
|
||||||
|
|
|
@ -40,8 +40,8 @@ from xbmcvfs import exists
|
||||||
|
|
||||||
import clientinfo as client
|
import clientinfo as client
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
from utils import window, settings, language as lang, tryDecode, tryEncode, \
|
from utils import window, settings, language as lang, try_decode, try_encode, \
|
||||||
DateToKodi, exists_dir, slugify, dialog
|
unix_date_to_kodi, exists_dir, slugify, dialog
|
||||||
import PlexFunctions as PF
|
import PlexFunctions as PF
|
||||||
import plexdb_functions as plexdb
|
import plexdb_functions as plexdb
|
||||||
import variables as v
|
import variables as v
|
||||||
|
@ -139,7 +139,7 @@ class API():
|
||||||
ans = None
|
ans = None
|
||||||
if ans is not None:
|
if ans is not None:
|
||||||
try:
|
try:
|
||||||
ans = tryDecode(unquote(ans))
|
ans = try_decode(unquote(ans))
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# Sometimes, Plex seems to have encoded in latin1
|
# Sometimes, Plex seems to have encoded in latin1
|
||||||
ans = unquote(ans).decode('latin1')
|
ans = unquote(ans).decode('latin1')
|
||||||
|
@ -167,7 +167,7 @@ class API():
|
||||||
self.item[0][0].attrib['key']))
|
self.item[0][0].attrib['key']))
|
||||||
# Attach Plex id to url to let it be picked up by our playqueue agent
|
# Attach Plex id to url to let it be picked up by our playqueue agent
|
||||||
# later
|
# later
|
||||||
return tryEncode('%s&plex_id=%s' % (path, self.getRatingKey()))
|
return try_encode('%s&plex_id=%s' % (path, self.getRatingKey()))
|
||||||
|
|
||||||
def getTVShowPath(self):
|
def getTVShowPath(self):
|
||||||
"""
|
"""
|
||||||
|
@ -194,7 +194,7 @@ class API():
|
||||||
"""
|
"""
|
||||||
res = self.item.attrib.get('addedAt')
|
res = self.item.attrib.get('addedAt')
|
||||||
if res is not None:
|
if res is not None:
|
||||||
res = DateToKodi(res)
|
res = unix_date_to_kodi(res)
|
||||||
else:
|
else:
|
||||||
res = '2000-01-01 10:00:00'
|
res = '2000-01-01 10:00:00'
|
||||||
return res
|
return res
|
||||||
|
@ -231,7 +231,7 @@ class API():
|
||||||
played = True if playcount else False
|
played = True if playcount else False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lastPlayedDate = DateToKodi(int(item['lastViewedAt']))
|
lastPlayedDate = unix_date_to_kodi(int(item['lastViewedAt']))
|
||||||
except (KeyError, ValueError):
|
except (KeyError, ValueError):
|
||||||
lastPlayedDate = None
|
lastPlayedDate = None
|
||||||
|
|
||||||
|
@ -884,7 +884,7 @@ class API():
|
||||||
parameters = {
|
parameters = {
|
||||||
'api_key': apiKey,
|
'api_key': apiKey,
|
||||||
'language': v.KODILANGUAGE,
|
'language': v.KODILANGUAGE,
|
||||||
'query': tryEncode(title)
|
'query': try_encode(title)
|
||||||
}
|
}
|
||||||
data = DU().downloadUrl(url,
|
data = DU().downloadUrl(url,
|
||||||
authenticate=False,
|
authenticate=False,
|
||||||
|
@ -1196,12 +1196,12 @@ class API():
|
||||||
languages.append(stream.attrib['language'])
|
languages.append(stream.attrib['language'])
|
||||||
languages = ', '.join(languages)
|
languages = ', '.join(languages)
|
||||||
if filename:
|
if filename:
|
||||||
option = tryEncode(filename)
|
option = try_encode(filename)
|
||||||
if languages:
|
if languages:
|
||||||
if option:
|
if option:
|
||||||
option = '%s (%s): ' % (option, tryEncode(languages))
|
option = '%s (%s): ' % (option, try_encode(languages))
|
||||||
else:
|
else:
|
||||||
option = '%s: ' % tryEncode(languages)
|
option = '%s: ' % try_encode(languages)
|
||||||
if 'videoResolution' in entry.attrib:
|
if 'videoResolution' in entry.attrib:
|
||||||
option = '%s%sp ' % (option,
|
option = '%s%sp ' % (option,
|
||||||
entry.attrib.get('videoResolution'))
|
entry.attrib.get('videoResolution'))
|
||||||
|
@ -1544,7 +1544,7 @@ class API():
|
||||||
# exist() needs a / or \ at the end to work for directories
|
# exist() needs a / or \ at the end to work for directories
|
||||||
if folder is False:
|
if folder is False:
|
||||||
# files
|
# files
|
||||||
check = exists(tryEncode(path))
|
check = exists(try_encode(path))
|
||||||
else:
|
else:
|
||||||
# directories
|
# directories
|
||||||
if "\\" in path:
|
if "\\" in path:
|
||||||
|
@ -1640,7 +1640,7 @@ class API():
|
||||||
plexitem = "plex_%s" % playurl
|
plexitem = "plex_%s" % playurl
|
||||||
window('%s.runtime' % plexitem, value=str(userdata['Runtime']))
|
window('%s.runtime' % plexitem, value=str(userdata['Runtime']))
|
||||||
window('%s.type' % plexitem, value=itemtype)
|
window('%s.type' % plexitem, value=itemtype)
|
||||||
state.PLEX_IDS[tryDecode(playurl)] = self.getRatingKey()
|
state.PLEX_IDS[try_decode(playurl)] = self.getRatingKey()
|
||||||
# window('%s.itemid' % plexitem, value=self.getRatingKey())
|
# window('%s.itemid' % plexitem, value=self.getRatingKey())
|
||||||
window('%s.playcount' % plexitem, value=str(userdata['PlayCount']))
|
window('%s.playcount' % plexitem, value=str(userdata['PlayCount']))
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from threading import Thread
|
||||||
from xbmc import sleep
|
from xbmc import sleep
|
||||||
|
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
from utils import settings, tryEncode, tryDecode
|
from utils import settings, try_encode, try_decode
|
||||||
from variables import PLEX_TO_KODI_TIMEFACTOR
|
from variables import PLEX_TO_KODI_TIMEFACTOR
|
||||||
import plex_tv
|
import plex_tv
|
||||||
|
|
||||||
|
@ -306,11 +306,11 @@ def _plex_gdm():
|
||||||
}
|
}
|
||||||
for line in response['data'].split('\n'):
|
for line in response['data'].split('\n'):
|
||||||
if 'Content-Type:' in line:
|
if 'Content-Type:' in line:
|
||||||
pms['product'] = tryDecode(line.split(':')[1].strip())
|
pms['product'] = try_decode(line.split(':')[1].strip())
|
||||||
elif 'Host:' in line:
|
elif 'Host:' in line:
|
||||||
pms['baseURL'] = line.split(':')[1].strip()
|
pms['baseURL'] = line.split(':')[1].strip()
|
||||||
elif 'Name:' in line:
|
elif 'Name:' in line:
|
||||||
pms['name'] = tryDecode(line.split(':')[1].strip())
|
pms['name'] = try_decode(line.split(':')[1].strip())
|
||||||
elif 'Port:' in line:
|
elif 'Port:' in line:
|
||||||
pms['port'] = line.split(':')[1].strip()
|
pms['port'] = line.split(':')[1].strip()
|
||||||
elif 'Resource-Identifier:' in line:
|
elif 'Resource-Identifier:' in line:
|
||||||
|
@ -820,7 +820,7 @@ def transcode_image_path(key, AuthToken, path, width, height):
|
||||||
path = 'http://127.0.0.1:32400' + key
|
path = 'http://127.0.0.1:32400' + key
|
||||||
else: # internal path, add-on
|
else: # internal path, add-on
|
||||||
path = 'http://127.0.0.1:32400' + path + '/' + key
|
path = 'http://127.0.0.1:32400' + path + '/' + key
|
||||||
path = tryEncode(path)
|
path = try_encode(path)
|
||||||
# This is bogus (note the extra path component) but ATV is stupid when it
|
# This is bogus (note the extra path component) but ATV is stupid when it
|
||||||
# comes to caching images, it doesn't use querystrings. Fortunately PMS is
|
# comes to caching images, it doesn't use querystrings. Fortunately PMS is
|
||||||
# lenient...
|
# lenient...
|
||||||
|
|
|
@ -12,8 +12,8 @@ import requests
|
||||||
from xbmc import sleep, translatePath
|
from xbmc import sleep, translatePath
|
||||||
from xbmcvfs import exists
|
from xbmcvfs import exists
|
||||||
|
|
||||||
from utils import window, settings, language as lang, kodiSQL, tryEncode, \
|
from utils import window, settings, language as lang, kodi_sql, try_encode, \
|
||||||
thread_methods, dialog, exists_dir, tryDecode
|
thread_methods, dialog, exists_dir, try_decode
|
||||||
import state
|
import state
|
||||||
|
|
||||||
# Disable annoying requests warnings
|
# Disable annoying requests warnings
|
||||||
|
@ -134,13 +134,13 @@ class Artwork():
|
||||||
if dialog('yesno', "Image Texture Cache", lang(39251)):
|
if dialog('yesno', "Image Texture Cache", lang(39251)):
|
||||||
LOG.info("Resetting all cache data first")
|
LOG.info("Resetting all cache data first")
|
||||||
# Remove all existing textures first
|
# Remove all existing textures first
|
||||||
path = tryDecode(translatePath("special://thumbnails/"))
|
path = try_decode(translatePath("special://thumbnails/"))
|
||||||
if exists_dir(path):
|
if exists_dir(path):
|
||||||
rmtree(path, ignore_errors=True)
|
rmtree(path, ignore_errors=True)
|
||||||
self.restoreCacheDirectories()
|
self.restoreCacheDirectories()
|
||||||
|
|
||||||
# remove all existing data from texture DB
|
# remove all existing data from texture DB
|
||||||
connection = kodiSQL('texture')
|
connection = kodi_sql('texture')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
query = 'SELECT tbl_name FROM sqlite_master WHERE type=?'
|
query = 'SELECT tbl_name FROM sqlite_master WHERE type=?'
|
||||||
cursor.execute(query, ('table', ))
|
cursor.execute(query, ('table', ))
|
||||||
|
@ -153,7 +153,7 @@ class Artwork():
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
# Cache all entries in video DB
|
# Cache all entries in video DB
|
||||||
connection = kodiSQL('video')
|
connection = kodi_sql('video')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
# dont include actors
|
# dont include actors
|
||||||
query = "SELECT url FROM art WHERE media_type != ?"
|
query = "SELECT url FROM art WHERE media_type != ?"
|
||||||
|
@ -166,7 +166,7 @@ class Artwork():
|
||||||
for url in result:
|
for url in result:
|
||||||
self.cacheTexture(url[0])
|
self.cacheTexture(url[0])
|
||||||
# Cache all entries in music DB
|
# Cache all entries in music DB
|
||||||
connection = kodiSQL('music')
|
connection = kodi_sql('music')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("SELECT url FROM art")
|
cursor.execute("SELECT url FROM art")
|
||||||
result = cursor.fetchall()
|
result = cursor.fetchall()
|
||||||
|
@ -179,7 +179,7 @@ class Artwork():
|
||||||
def cacheTexture(self, url):
|
def cacheTexture(self, url):
|
||||||
# Cache a single image url to the texture cache
|
# Cache a single image url to the texture cache
|
||||||
if url and self.enableTextureCache:
|
if url and self.enableTextureCache:
|
||||||
self.queue.put(double_urlencode(tryEncode(url)))
|
self.queue.put(double_urlencode(try_encode(url)))
|
||||||
|
|
||||||
def addArtwork(self, artwork, kodiId, mediaType, cursor):
|
def addArtwork(self, artwork, kodiId, mediaType, cursor):
|
||||||
# Kodi conversion table
|
# Kodi conversion table
|
||||||
|
@ -323,7 +323,7 @@ class Artwork():
|
||||||
|
|
||||||
def deleteCachedArtwork(self, url):
|
def deleteCachedArtwork(self, url):
|
||||||
# Only necessary to remove and apply a new backdrop or poster
|
# Only necessary to remove and apply a new backdrop or poster
|
||||||
connection = kodiSQL('texture')
|
connection = kodi_sql('texture')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
try:
|
try:
|
||||||
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
|
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
|
||||||
|
@ -336,7 +336,7 @@ class Artwork():
|
||||||
path = translatePath("special://thumbnails/%s" % cachedurl)
|
path = translatePath("special://thumbnails/%s" % cachedurl)
|
||||||
LOG.debug("Deleting cached thumbnail: %s" % path)
|
LOG.debug("Deleting cached thumbnail: %s" % path)
|
||||||
if exists(path):
|
if exists(path):
|
||||||
rmtree(tryDecode(path), ignore_errors=True)
|
rmtree(try_decode(path), ignore_errors=True)
|
||||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
finally:
|
finally:
|
||||||
|
@ -347,4 +347,4 @@ class Artwork():
|
||||||
LOG.info("Restoring cache directories...")
|
LOG.info("Restoring cache directories...")
|
||||||
paths = ("","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","Video","plex")
|
paths = ("","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","Video","plex")
|
||||||
for p in paths:
|
for p in paths:
|
||||||
makedirs(tryDecode(translatePath("special://thumbnails/%s" % p)))
|
makedirs(try_decode(translatePath("special://thumbnails/%s" % p)))
|
||||||
|
|
|
@ -11,8 +11,8 @@ import xbmcplugin
|
||||||
from xbmc import sleep, executebuiltin, translatePath
|
from xbmc import sleep, executebuiltin, translatePath
|
||||||
from xbmcgui import ListItem
|
from xbmcgui import ListItem
|
||||||
|
|
||||||
from utils import window, settings, language as lang, dialog, tryEncode, \
|
from utils import window, settings, language as lang, dialog, try_encode, \
|
||||||
CatchExceptions, exists_dir, plex_command, tryDecode
|
CatchExceptions, exists_dir, plex_command, try_decode
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
|
||||||
from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \
|
from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \
|
||||||
|
@ -53,11 +53,11 @@ def chooseServer():
|
||||||
if not __LogOut():
|
if not __LogOut():
|
||||||
return
|
return
|
||||||
|
|
||||||
from utils import deletePlaylists, deleteNodes
|
from utils import delete_playlists, delete_nodes
|
||||||
# First remove playlists
|
# First remove playlists
|
||||||
deletePlaylists()
|
delete_playlists()
|
||||||
# Remove video nodes
|
# Remove video nodes
|
||||||
deleteNodes()
|
delete_nodes()
|
||||||
|
|
||||||
# Log in again
|
# Log in again
|
||||||
__LogIn()
|
__LogIn()
|
||||||
|
@ -175,10 +175,10 @@ def switchPlexUser():
|
||||||
return
|
return
|
||||||
|
|
||||||
# First remove playlists of old user
|
# First remove playlists of old user
|
||||||
from utils import deletePlaylists, deleteNodes
|
from utils import delete_playlists, delete_nodes
|
||||||
deletePlaylists()
|
delete_playlists()
|
||||||
# Remove video nodes
|
# Remove video nodes
|
||||||
deleteNodes()
|
delete_nodes()
|
||||||
__LogIn()
|
__LogIn()
|
||||||
|
|
||||||
|
|
||||||
|
@ -455,14 +455,14 @@ def getVideoFiles(plexId, params):
|
||||||
if exists_dir(path):
|
if exists_dir(path):
|
||||||
for root, dirs, files in walk(path):
|
for root, dirs, files in walk(path):
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
item_path = tryEncode(join(root, directory))
|
item_path = try_encode(join(root, directory))
|
||||||
li = ListItem(item_path, path=item_path)
|
li = ListItem(item_path, path=item_path)
|
||||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||||
url=item_path,
|
url=item_path,
|
||||||
listitem=li,
|
listitem=li,
|
||||||
isFolder=True)
|
isFolder=True)
|
||||||
for file in files:
|
for file in files:
|
||||||
item_path = tryEncode(join(root, file))
|
item_path = try_encode(join(root, file))
|
||||||
li = ListItem(item_path, path=item_path)
|
li = ListItem(item_path, path=item_path)
|
||||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||||
url=file,
|
url=file,
|
||||||
|
@ -490,7 +490,7 @@ def getExtraFanArt(plexid, plexPath):
|
||||||
|
|
||||||
# We need to store the images locally for this to work
|
# We need to store the images locally for this to work
|
||||||
# because of the caching system in xbmc
|
# because of the caching system in xbmc
|
||||||
fanartDir = tryDecode(translatePath(
|
fanartDir = try_decode(translatePath(
|
||||||
"special://thumbnails/plex/%s/" % plexid))
|
"special://thumbnails/plex/%s/" % plexid))
|
||||||
if not exists_dir(fanartDir):
|
if not exists_dir(fanartDir):
|
||||||
# Download the images to the cache directory
|
# Download the images to the cache directory
|
||||||
|
@ -504,19 +504,19 @@ def getExtraFanArt(plexid, plexPath):
|
||||||
backdrops = api.getAllArtwork()['Backdrop']
|
backdrops = api.getAllArtwork()['Backdrop']
|
||||||
for count, backdrop in enumerate(backdrops):
|
for count, backdrop in enumerate(backdrops):
|
||||||
# Same ordering as in artwork
|
# Same ordering as in artwork
|
||||||
fanartFile = tryEncode(join(fanartDir, "fanart%.3d.jpg" % count))
|
fanartFile = try_encode(join(fanartDir, "fanart%.3d.jpg" % count))
|
||||||
li = ListItem("%.3d" % count, path=fanartFile)
|
li = ListItem("%.3d" % count, path=fanartFile)
|
||||||
xbmcplugin.addDirectoryItem(
|
xbmcplugin.addDirectoryItem(
|
||||||
handle=HANDLE,
|
handle=HANDLE,
|
||||||
url=fanartFile,
|
url=fanartFile,
|
||||||
listitem=li)
|
listitem=li)
|
||||||
copyfile(backdrop, tryDecode(fanartFile))
|
copyfile(backdrop, try_decode(fanartFile))
|
||||||
else:
|
else:
|
||||||
log.info("Found cached backdrop.")
|
log.info("Found cached backdrop.")
|
||||||
# Use existing cached images
|
# Use existing cached images
|
||||||
for root, dirs, files in walk(fanartDir):
|
for root, dirs, files in walk(fanartDir):
|
||||||
for file in files:
|
for file in files:
|
||||||
fanartFile = tryEncode(join(root, file))
|
fanartFile = try_encode(join(root, file))
|
||||||
li = ListItem(file, path=fanartFile)
|
li = ListItem(file, path=fanartFile)
|
||||||
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
xbmcplugin.addDirectoryItem(handle=HANDLE,
|
||||||
url=fanartFile,
|
url=fanartFile,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import xml.etree.ElementTree as etree
|
||||||
|
|
||||||
from xbmc import executebuiltin, translatePath
|
from xbmc import executebuiltin, translatePath
|
||||||
|
|
||||||
from utils import settings, window, language as lang, tryEncode, tryDecode, \
|
from utils import settings, window, language as lang, try_encode, try_decode, \
|
||||||
XmlKodiSetting, reboot_kodi, dialog
|
XmlKodiSetting, reboot_kodi, dialog
|
||||||
from migration import check_migration
|
from migration import check_migration
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
|
@ -76,7 +76,7 @@ def reload_pkc():
|
||||||
set_webserver()
|
set_webserver()
|
||||||
# To detect Kodi profile switches
|
# To detect Kodi profile switches
|
||||||
window('plex_kodiProfile',
|
window('plex_kodiProfile',
|
||||||
value=tryDecode(translatePath("special://profile")))
|
value=try_decode(translatePath("special://profile")))
|
||||||
getDeviceId()
|
getDeviceId()
|
||||||
# Initialize the PKC playqueues
|
# Initialize the PKC playqueues
|
||||||
PQ.init_playqueues()
|
PQ.init_playqueues()
|
||||||
|
@ -355,7 +355,7 @@ class InitialSetup(object):
|
||||||
dialog('ok',
|
dialog('ok',
|
||||||
lang(29999),
|
lang(29999),
|
||||||
'%s %s' % (lang(39214),
|
'%s %s' % (lang(39214),
|
||||||
tryEncode(server['name'])))
|
try_encode(server['name'])))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
@ -610,8 +610,8 @@ class InitialSetup(object):
|
||||||
line1=lang(39029),
|
line1=lang(39029),
|
||||||
line2=lang(39030)):
|
line2=lang(39030)):
|
||||||
LOG.debug("Presenting network credentials dialog.")
|
LOG.debug("Presenting network credentials dialog.")
|
||||||
from utils import passwordsXML
|
from utils import passwords_xml
|
||||||
passwordsXML()
|
passwords_xml()
|
||||||
# Disable Plex music?
|
# Disable Plex music?
|
||||||
if dialog('yesno', heading=lang(29999), line1=lang(39016)):
|
if dialog('yesno', heading=lang(29999), line1=lang(39016)):
|
||||||
LOG.debug("User opted to disable Plex music library.")
|
LOG.debug("User opted to disable Plex music library.")
|
||||||
|
|
|
@ -6,7 +6,7 @@ from ntpath import dirname
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from artwork import Artwork
|
from artwork import Artwork
|
||||||
from utils import window, kodiSQL, CatchExceptions
|
from utils import window, kodi_sql, CatchExceptions
|
||||||
import plexdb_functions as plexdb
|
import plexdb_functions as plexdb
|
||||||
import kodidb_functions as kodidb
|
import kodidb_functions as kodidb
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@ class Items(object):
|
||||||
"""
|
"""
|
||||||
Open DB connections and cursors
|
Open DB connections and cursors
|
||||||
"""
|
"""
|
||||||
self.plexconn = kodiSQL('plex')
|
self.plexconn = kodi_sql('plex')
|
||||||
self.plexcursor = self.plexconn.cursor()
|
self.plexcursor = self.plexconn.cursor()
|
||||||
self.kodiconn = kodiSQL('video')
|
self.kodiconn = kodi_sql('video')
|
||||||
self.kodicursor = self.kodiconn.cursor()
|
self.kodicursor = self.kodiconn.cursor()
|
||||||
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
||||||
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
||||||
|
@ -1273,10 +1273,10 @@ class Music(Items):
|
||||||
OVERWRITE this method, because we need to open another DB.
|
OVERWRITE this method, because we need to open another DB.
|
||||||
Open DB connections and cursors
|
Open DB connections and cursors
|
||||||
"""
|
"""
|
||||||
self.plexconn = kodiSQL('plex')
|
self.plexconn = kodi_sql('plex')
|
||||||
self.plexcursor = self.plexconn.cursor()
|
self.plexcursor = self.plexconn.cursor()
|
||||||
# Here it is, not 'video' but 'music'
|
# Here it is, not 'video' but 'music'
|
||||||
self.kodiconn = kodiSQL('music')
|
self.kodiconn = kodi_sql('music')
|
||||||
self.kodicursor = self.kodiconn.cursor()
|
self.kodicursor = self.kodiconn.cursor()
|
||||||
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
||||||
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from logging import getLogger
|
||||||
from ntpath import dirname
|
from ntpath import dirname
|
||||||
|
|
||||||
import artwork
|
import artwork
|
||||||
from utils import kodiSQL
|
from utils import kodi_sql
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -30,7 +30,7 @@ class GetKodiDB():
|
||||||
self.db_type = db_type
|
self.db_type = db_type
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.kodiconn = kodiSQL(self.db_type)
|
self.kodiconn = kodi_sql(self.db_type)
|
||||||
kodi_db = Kodidb_Functions(self.kodiconn.cursor())
|
kodi_db = Kodidb_Functions(self.kodiconn.cursor())
|
||||||
return kodi_db
|
return kodi_db
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@ from random import shuffle
|
||||||
import xbmc
|
import xbmc
|
||||||
from xbmcvfs import exists
|
from xbmcvfs import exists
|
||||||
|
|
||||||
from utils import window, settings, getUnixTimestamp, \
|
from utils import window, settings, unix_timestamp, thread_methods, \
|
||||||
thread_methods, create_actor_db_index, dialog, LogTime, playlistXSP,\
|
create_actor_db_index, dialog, LogTime, playlist_xsp, language as lang, \
|
||||||
language as lang, DateToKodi, reset, tryDecode, deletePlaylists, \
|
unix_date_to_kodi, reset, try_decode, delete_playlists, delete_nodes, \
|
||||||
deleteNodes, tryEncode, compare_version
|
try_encode, compare_version
|
||||||
import downloadutils
|
import downloadutils
|
||||||
import itemtypes
|
import itemtypes
|
||||||
import plexdb_functions as plexdb
|
import plexdb_functions as plexdb
|
||||||
|
@ -155,7 +155,7 @@ class LibrarySync(Thread):
|
||||||
log.debug('No timestamp; using 0')
|
log.debug('No timestamp; using 0')
|
||||||
|
|
||||||
# Set the timer
|
# Set the timer
|
||||||
koditime = getUnixTimestamp()
|
koditime = unix_timestamp()
|
||||||
# Toggle watched state
|
# Toggle watched state
|
||||||
scrobble(plexId, 'watched')
|
scrobble(plexId, 'watched')
|
||||||
# Let the PMS process this first!
|
# Let the PMS process this first!
|
||||||
|
@ -329,7 +329,7 @@ class LibrarySync(Thread):
|
||||||
# Create playlist for the video library
|
# Create playlist for the video library
|
||||||
if (foldername not in playlists and
|
if (foldername not in playlists and
|
||||||
mediatype in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
mediatype in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
playlistXSP(mediatype, foldername, folderid, viewtype)
|
playlist_xsp(mediatype, foldername, folderid, viewtype)
|
||||||
playlists.append(foldername)
|
playlists.append(foldername)
|
||||||
# Create the video node
|
# Create the video node
|
||||||
if (foldername not in nodes and
|
if (foldername not in nodes and
|
||||||
|
@ -371,7 +371,7 @@ class LibrarySync(Thread):
|
||||||
# The tag could be a combined view. Ensure there's
|
# The tag could be a combined view. Ensure there's
|
||||||
# no other tags with the same name before deleting
|
# no other tags with the same name before deleting
|
||||||
# playlist.
|
# playlist.
|
||||||
playlistXSP(mediatype,
|
playlist_xsp(mediatype,
|
||||||
current_viewname,
|
current_viewname,
|
||||||
folderid,
|
folderid,
|
||||||
current_viewtype,
|
current_viewtype,
|
||||||
|
@ -388,7 +388,7 @@ class LibrarySync(Thread):
|
||||||
# Added new playlist
|
# Added new playlist
|
||||||
if (foldername not in playlists and mediatype in
|
if (foldername not in playlists and mediatype in
|
||||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
playlistXSP(mediatype,
|
playlist_xsp(mediatype,
|
||||||
foldername,
|
foldername,
|
||||||
folderid,
|
folderid,
|
||||||
viewtype)
|
viewtype)
|
||||||
|
@ -414,7 +414,7 @@ class LibrarySync(Thread):
|
||||||
if mediatype != v.PLEX_TYPE_ARTIST:
|
if mediatype != v.PLEX_TYPE_ARTIST:
|
||||||
if (foldername not in playlists and mediatype in
|
if (foldername not in playlists and mediatype in
|
||||||
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
||||||
playlistXSP(mediatype,
|
playlist_xsp(mediatype,
|
||||||
foldername,
|
foldername,
|
||||||
folderid,
|
folderid,
|
||||||
viewtype)
|
viewtype)
|
||||||
|
@ -1102,7 +1102,7 @@ class LibrarySync(Thread):
|
||||||
"""
|
"""
|
||||||
self.videoLibUpdate = False
|
self.videoLibUpdate = False
|
||||||
self.musicLibUpdate = False
|
self.musicLibUpdate = False
|
||||||
now = getUnixTimestamp()
|
now = unix_timestamp()
|
||||||
deleteListe = []
|
deleteListe = []
|
||||||
for i, item in enumerate(self.itemsToProcess):
|
for i, item in enumerate(self.itemsToProcess):
|
||||||
if self.thread_stopped() or self.thread_suspended():
|
if self.thread_stopped() or self.thread_suspended():
|
||||||
|
@ -1220,7 +1220,7 @@ class LibrarySync(Thread):
|
||||||
'state': status,
|
'state': status,
|
||||||
'type': typus,
|
'type': typus,
|
||||||
'ratingKey': str(item['itemID']),
|
'ratingKey': str(item['itemID']),
|
||||||
'timestamp': getUnixTimestamp(),
|
'timestamp': unix_timestamp(),
|
||||||
'attempt': 0
|
'attempt': 0
|
||||||
})
|
})
|
||||||
elif typus in (v.PLEX_TYPE_MOVIE,
|
elif typus in (v.PLEX_TYPE_MOVIE,
|
||||||
|
@ -1237,7 +1237,7 @@ class LibrarySync(Thread):
|
||||||
'state': status,
|
'state': status,
|
||||||
'type': typus,
|
'type': typus,
|
||||||
'ratingKey': plex_id,
|
'ratingKey': plex_id,
|
||||||
'timestamp': getUnixTimestamp(),
|
'timestamp': unix_timestamp(),
|
||||||
'attempt': 0
|
'attempt': 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1276,7 +1276,7 @@ class LibrarySync(Thread):
|
||||||
'state': None, # Don't need a state here
|
'state': None, # Don't need a state here
|
||||||
'type': kodi_info[5],
|
'type': kodi_info[5],
|
||||||
'ratingKey': plex_id,
|
'ratingKey': plex_id,
|
||||||
'timestamp': getUnixTimestamp(),
|
'timestamp': unix_timestamp(),
|
||||||
'attempt': 0
|
'attempt': 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1386,7 +1386,7 @@ class LibrarySync(Thread):
|
||||||
resume,
|
resume,
|
||||||
session['duration'],
|
session['duration'],
|
||||||
session['file_id'],
|
session['file_id'],
|
||||||
DateToKodi(getUnixTimestamp()))
|
unix_date_to_kodi(unix_timestamp()))
|
||||||
|
|
||||||
def fanartSync(self, refresh=False):
|
def fanartSync(self, refresh=False):
|
||||||
"""
|
"""
|
||||||
|
@ -1430,9 +1430,9 @@ class LibrarySync(Thread):
|
||||||
window('plex_dbScan', value="true")
|
window('plex_dbScan', value="true")
|
||||||
state.DB_SCAN = True
|
state.DB_SCAN = True
|
||||||
# First remove playlists
|
# First remove playlists
|
||||||
deletePlaylists()
|
delete_playlists()
|
||||||
# Remove video nodes
|
# Remove video nodes
|
||||||
deleteNodes()
|
delete_nodes()
|
||||||
# Kick off refresh
|
# Kick off refresh
|
||||||
if self.maintainViews() is True:
|
if self.maintainViews() is True:
|
||||||
# Ran successfully
|
# Ran successfully
|
||||||
|
@ -1549,11 +1549,11 @@ class LibrarySync(Thread):
|
||||||
# Also runs when first installed
|
# Also runs when first installed
|
||||||
# Verify the video database can be found
|
# Verify the video database can be found
|
||||||
videoDb = v.DB_VIDEO_PATH
|
videoDb = v.DB_VIDEO_PATH
|
||||||
if not exists(tryEncode(videoDb)):
|
if not exists(try_encode(videoDb)):
|
||||||
# Database does not exists
|
# Database does not exists
|
||||||
log.error("The current Kodi version is incompatible "
|
log.error("The current Kodi version is incompatible "
|
||||||
"to know which Kodi versions are supported.")
|
"to know which Kodi versions are supported.")
|
||||||
log.error('Current Kodi version: %s' % tryDecode(
|
log.error('Current Kodi version: %s' % try_decode(
|
||||||
xbmc.getInfoLabel('System.BuildVersion')))
|
xbmc.getInfoLabel('System.BuildVersion')))
|
||||||
# "Current Kodi version is unsupported, cancel lib sync"
|
# "Current Kodi version is unsupported, cancel lib sync"
|
||||||
dialog('ok', heading='{plex}', line1=lang(39403))
|
dialog('ok', heading='{plex}', line1=lang(39403))
|
||||||
|
@ -1562,10 +1562,10 @@ class LibrarySync(Thread):
|
||||||
state.DB_SCAN = True
|
state.DB_SCAN = True
|
||||||
window('plex_dbScan', value="true")
|
window('plex_dbScan', value="true")
|
||||||
log.info("Db version: %s" % settings('dbCreatedWithVersion'))
|
log.info("Db version: %s" % settings('dbCreatedWithVersion'))
|
||||||
lastTimeSync = getUnixTimestamp()
|
lastTimeSync = unix_timestamp()
|
||||||
# Initialize time offset Kodi - PMS
|
# Initialize time offset Kodi - PMS
|
||||||
self.syncPMStime()
|
self.syncPMStime()
|
||||||
lastSync = getUnixTimestamp()
|
lastSync = unix_timestamp()
|
||||||
if settings('FanartTV') == 'true':
|
if settings('FanartTV') == 'true':
|
||||||
# Start getting additional missing artwork
|
# Start getting additional missing artwork
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
with plexdb.Get_Plex_DB() as plex_db:
|
||||||
|
@ -1579,8 +1579,8 @@ class LibrarySync(Thread):
|
||||||
'refresh': True
|
'refresh': True
|
||||||
})
|
})
|
||||||
log.info('Refreshing video nodes and playlists now')
|
log.info('Refreshing video nodes and playlists now')
|
||||||
deletePlaylists()
|
delete_playlists()
|
||||||
deleteNodes()
|
delete_nodes()
|
||||||
log.info("Initial start-up full sync starting")
|
log.info("Initial start-up full sync starting")
|
||||||
librarySync = fullSync()
|
librarySync = fullSync()
|
||||||
window('plex_dbScan', clear=True)
|
window('plex_dbScan', clear=True)
|
||||||
|
@ -1604,7 +1604,7 @@ class LibrarySync(Thread):
|
||||||
self.triage_lib_scans()
|
self.triage_lib_scans()
|
||||||
self.force_dialog = False
|
self.force_dialog = False
|
||||||
continue
|
continue
|
||||||
now = getUnixTimestamp()
|
now = unix_timestamp()
|
||||||
# Standard syncs - don't force-show dialogs
|
# Standard syncs - don't force-show dialogs
|
||||||
self.force_dialog = False
|
self.force_dialog = False
|
||||||
if (now - lastSync > FULL_SYNC_INTERVALL and
|
if (now - lastSync > FULL_SYNC_INTERVALL and
|
||||||
|
|
|
@ -12,7 +12,7 @@ LEVELS = {
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
def tryEncode(uniString, encoding='utf-8'):
|
def try_encode(uniString, encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
Will try to encode uniString (in unicode) to encoding. This possibly
|
Will try to encode uniString (in unicode) to encoding. This possibly
|
||||||
fails with e.g. Android TV's Python, which does not accept arguments for
|
fails with e.g. Android TV's Python, which does not accept arguments for
|
||||||
|
@ -43,5 +43,5 @@ class LogHandler(logging.StreamHandler):
|
||||||
try:
|
try:
|
||||||
xbmc.log(self.format(record), level=LEVELS[record.levelno])
|
xbmc.log(self.format(record), level=LEVELS[record.levelno])
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
xbmc.log(tryEncode(self.format(record)),
|
xbmc.log(try_encode(self.format(record)),
|
||||||
level=LEVELS[record.levelno])
|
level=LEVELS[record.levelno])
|
||||||
|
|
|
@ -18,7 +18,7 @@ from playutils import PlayUtils
|
||||||
from PKC_listitem import PKC_ListItem
|
from PKC_listitem import PKC_ListItem
|
||||||
from pickler import pickle_me, Playback_Successful
|
from pickler import pickle_me, Playback_Successful
|
||||||
import json_rpc as js
|
import json_rpc as js
|
||||||
from utils import settings, dialog, language as lang, tryEncode
|
from utils import settings, dialog, language as lang, try_encode
|
||||||
from plexbmchelper.subscribers import LOCKER
|
from plexbmchelper.subscribers import LOCKER
|
||||||
import variables as v
|
import variables as v
|
||||||
import state
|
import state
|
||||||
|
@ -167,7 +167,7 @@ def _prep_playlist_stack(xml):
|
||||||
path = ('plugin://plugin.video.plexkodiconnect?%s'
|
path = ('plugin://plugin.video.plexkodiconnect?%s'
|
||||||
% urlencode(params))
|
% urlencode(params))
|
||||||
listitem = api.CreateListItemFromPlexItem()
|
listitem = api.CreateListItemFromPlexItem()
|
||||||
listitem.setPath(tryEncode(path))
|
listitem.setPath(try_encode(path))
|
||||||
else:
|
else:
|
||||||
# Will add directly via the Kodi DB
|
# Will add directly via the Kodi DB
|
||||||
path = None
|
path = None
|
||||||
|
@ -244,7 +244,7 @@ def conclude_playback(playqueue, pos):
|
||||||
playurl = playutils.getPlayUrl()
|
playurl = playutils.getPlayUrl()
|
||||||
else:
|
else:
|
||||||
playurl = item.file
|
playurl = item.file
|
||||||
listitem.setPath(tryEncode(playurl))
|
listitem.setPath(try_encode(playurl))
|
||||||
if item.playmethod in ('DirectStream', 'DirectPlay'):
|
if item.playmethod in ('DirectStream', 'DirectPlay'):
|
||||||
listitem.setSubtitles(api.externalSubs())
|
listitem.setSubtitles(api.externalSubs())
|
||||||
else:
|
else:
|
||||||
|
@ -322,14 +322,14 @@ def process_indirect(key, offset, resolve=True):
|
||||||
return
|
return
|
||||||
playurl = xml[0].attrib['key']
|
playurl = xml[0].attrib['key']
|
||||||
item.file = playurl
|
item.file = playurl
|
||||||
listitem.setPath(tryEncode(playurl))
|
listitem.setPath(try_encode(playurl))
|
||||||
playqueue.items.append(item)
|
playqueue.items.append(item)
|
||||||
if resolve is True:
|
if resolve is True:
|
||||||
result.listitem = listitem
|
result.listitem = listitem
|
||||||
pickle_me(result)
|
pickle_me(result)
|
||||||
else:
|
else:
|
||||||
thread = Thread(target=Player().play,
|
thread = Thread(target=Player().play,
|
||||||
args={'item': tryEncode(playurl),
|
args={'item': try_encode(playurl),
|
||||||
'listitem': listitem})
|
'listitem': listitem})
|
||||||
thread.setDaemon(True)
|
thread.setDaemon(True)
|
||||||
LOG.info('Done initializing PKC playback, starting Kodi player')
|
LOG.info('Done initializing PKC playback, starting Kodi player')
|
||||||
|
|
|
@ -8,7 +8,7 @@ from re import compile as re_compile
|
||||||
|
|
||||||
import plexdb_functions as plexdb
|
import plexdb_functions as plexdb
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
from utils import tryEncode, escape_html
|
from utils import try_encode, escape_html
|
||||||
from PlexAPI import API
|
from PlexAPI import API
|
||||||
from PlexFunctions import GetPlexMetadata
|
from PlexFunctions import GetPlexMetadata
|
||||||
import json_rpc as js
|
import json_rpc as js
|
||||||
|
@ -60,7 +60,7 @@ class PlaylistObjectBaseclase(object):
|
||||||
continue
|
continue
|
||||||
if isinstance(getattr(self, key), (str, unicode)):
|
if isinstance(getattr(self, key), (str, unicode)):
|
||||||
answ += '\'%s\': \'%s\', ' % (key,
|
answ += '\'%s\': \'%s\', ' % (key,
|
||||||
tryEncode(getattr(self, key)))
|
try_encode(getattr(self, key)))
|
||||||
else:
|
else:
|
||||||
# e.g. int
|
# e.g. int
|
||||||
answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
|
answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
|
||||||
|
@ -168,7 +168,7 @@ class Playlist_Item(object):
|
||||||
continue
|
continue
|
||||||
if isinstance(getattr(self, key), (str, unicode)):
|
if isinstance(getattr(self, key), (str, unicode)):
|
||||||
answ += '\'%s\': \'%s\', ' % (key,
|
answ += '\'%s\': \'%s\', ' % (key,
|
||||||
tryEncode(getattr(self, key)))
|
try_encode(getattr(self, key)))
|
||||||
else:
|
else:
|
||||||
# e.g. int
|
# e.g. int
|
||||||
answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
|
answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
|
|
||||||
from utils import window, settings, language as lang, dialog, tryEncode
|
from utils import window, settings, language as lang, dialog, try_encode
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -274,7 +274,7 @@ class PlayUtils():
|
||||||
codec,
|
codec,
|
||||||
channellayout)
|
channellayout)
|
||||||
audio_streams_list.append(index)
|
audio_streams_list.append(index)
|
||||||
audio_streams.append(tryEncode(track))
|
audio_streams.append(try_encode(track))
|
||||||
audio_numb += 1
|
audio_numb += 1
|
||||||
|
|
||||||
# Subtitles
|
# Subtitles
|
||||||
|
@ -306,7 +306,7 @@ class PlayUtils():
|
||||||
"%s%s" % (window('pms_server'),
|
"%s%s" % (window('pms_server'),
|
||||||
stream.attrib['key']))
|
stream.attrib['key']))
|
||||||
downloadable_streams.append(index)
|
downloadable_streams.append(index)
|
||||||
download_subs.append(tryEncode(path))
|
download_subs.append(try_encode(path))
|
||||||
else:
|
else:
|
||||||
track = "%s (%s)" % (track, lang(39710)) # burn-in
|
track = "%s (%s)" % (track, lang(39710)) # burn-in
|
||||||
if stream.attrib.get('selected') == '1' and downloadable:
|
if stream.attrib.get('selected') == '1' and downloadable:
|
||||||
|
@ -315,7 +315,7 @@ class PlayUtils():
|
||||||
default_sub = index
|
default_sub = index
|
||||||
|
|
||||||
subtitle_streams_list.append(index)
|
subtitle_streams_list.append(index)
|
||||||
subtitle_streams.append(tryEncode(track))
|
subtitle_streams.append(try_encode(track))
|
||||||
sub_num += 1
|
sub_num += 1
|
||||||
|
|
||||||
if audio_numb > 1:
|
if audio_numb > 1:
|
||||||
|
|
|
@ -4,7 +4,7 @@ from logging import getLogger
|
||||||
from xbmc import sleep, executebuiltin
|
from xbmc import sleep, executebuiltin
|
||||||
|
|
||||||
from downloadutils import DownloadUtils as DU
|
from downloadutils import DownloadUtils as DU
|
||||||
from utils import dialog, language as lang, settings, tryEncode
|
from utils import dialog, language as lang, settings, try_encode
|
||||||
import variables as v
|
import variables as v
|
||||||
import state
|
import state
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ def choose_home_user(token):
|
||||||
username = user['title']
|
username = user['title']
|
||||||
userlist.append(username)
|
userlist.append(username)
|
||||||
# To take care of non-ASCII usernames
|
# To take care of non-ASCII usernames
|
||||||
userlist_coded.append(tryEncode(username))
|
userlist_coded.append(try_encode(username))
|
||||||
usernumber = len(userlist)
|
usernumber = len(userlist)
|
||||||
username = ''
|
username = ''
|
||||||
usertoken = ''
|
usertoken = ''
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from utils import kodiSQL
|
from utils import kodi_sql
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -22,7 +22,7 @@ class Get_Plex_DB():
|
||||||
and the db gets closed
|
and the db gets closed
|
||||||
"""
|
"""
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.plexconn = kodiSQL('plex')
|
self.plexconn = kodi_sql('plex')
|
||||||
return Plex_DB_Functions(self.plexconn.cursor())
|
return Plex_DB_Functions(self.plexconn.cursor())
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Various functions and decorators for PKC
|
||||||
|
"""
|
||||||
###############################################################################
|
###############################################################################
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from cProfile import Profile
|
from cProfile import Profile
|
||||||
|
@ -7,7 +9,7 @@ from pstats import Stats
|
||||||
from sqlite3 import connect, OperationalError
|
from sqlite3 import connect, OperationalError
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from time import localtime, strftime, strptime
|
from time import localtime, strftime
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial
|
||||||
|
@ -21,12 +23,13 @@ import xbmc
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
from xbmcvfs import exists, delete
|
from xbmcvfs import exists, delete
|
||||||
|
|
||||||
import variables as v
|
import variables as v
|
||||||
import state
|
import state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
WINDOW = xbmcgui.Window(10000)
|
||||||
ADDON = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
|
ADDON = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
|
||||||
|
@ -46,7 +49,8 @@ def reboot_kodi(message=None):
|
||||||
dialog('ok', heading='{plex}', line1=message)
|
dialog('ok', heading='{plex}', line1=message)
|
||||||
xbmc.executebuiltin('RestartApp')
|
xbmc.executebuiltin('RestartApp')
|
||||||
|
|
||||||
def window(property, value=None, clear=False, windowid=10000):
|
|
||||||
|
def window(prop, value=None, clear=False, windowid=10000):
|
||||||
"""
|
"""
|
||||||
Get or set window property - thread safe!
|
Get or set window property - thread safe!
|
||||||
|
|
||||||
|
@ -60,11 +64,11 @@ def window(property, value=None, clear=False, windowid=10000):
|
||||||
win = WINDOW
|
win = WINDOW
|
||||||
|
|
||||||
if clear:
|
if clear:
|
||||||
win.clearProperty(property)
|
win.clearProperty(prop)
|
||||||
elif value is not None:
|
elif value is not None:
|
||||||
win.setProperty(tryEncode(property), tryEncode(value))
|
win.setProperty(try_encode(prop), try_encode(value))
|
||||||
else:
|
else:
|
||||||
return tryDecode(win.getProperty(property))
|
return try_decode(win.getProperty(prop))
|
||||||
|
|
||||||
|
|
||||||
def plex_command(key, value):
|
def plex_command(key, value):
|
||||||
|
@ -90,10 +94,10 @@ def settings(setting, value=None):
|
||||||
addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
|
addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
|
||||||
if value is not None:
|
if value is not None:
|
||||||
# Takes string or unicode by default!
|
# Takes string or unicode by default!
|
||||||
addon.setSetting(tryEncode(setting), tryEncode(value))
|
addon.setSetting(try_encode(setting), try_encode(value))
|
||||||
else:
|
else:
|
||||||
# Should return unicode by default, but just in case
|
# Should return unicode by default, but just in case
|
||||||
return tryDecode(addon.getSetting(setting))
|
return try_decode(addon.getSetting(setting))
|
||||||
|
|
||||||
|
|
||||||
def exists_dir(path):
|
def exists_dir(path):
|
||||||
|
@ -104,24 +108,26 @@ def exists_dir(path):
|
||||||
Feed with encoded string or unicode
|
Feed with encoded string or unicode
|
||||||
"""
|
"""
|
||||||
if v.KODIVERSION >= 17:
|
if v.KODIVERSION >= 17:
|
||||||
answ = exists(tryEncode(path))
|
answ = exists(try_encode(path))
|
||||||
else:
|
else:
|
||||||
dummyfile = join(tryDecode(path), 'dummyfile.txt')
|
dummyfile = join(try_decode(path), 'dummyfile.txt')
|
||||||
try:
|
try:
|
||||||
with open(dummyfile, 'w') as f:
|
with open(dummyfile, 'w') as filer:
|
||||||
f.write('text')
|
filer.write('text')
|
||||||
except IOError:
|
except IOError:
|
||||||
# folder does not exist yet
|
# folder does not exist yet
|
||||||
answ = 0
|
answ = 0
|
||||||
else:
|
else:
|
||||||
# Folder exists. Delete file again.
|
# Folder exists. Delete file again.
|
||||||
delete(tryEncode(dummyfile))
|
delete(try_encode(dummyfile))
|
||||||
answ = 1
|
answ = 1
|
||||||
return answ
|
return answ
|
||||||
|
|
||||||
|
|
||||||
def language(stringid):
|
def language(stringid):
|
||||||
# Central string retrieval
|
"""
|
||||||
|
Central string retrieval from strings.po
|
||||||
|
"""
|
||||||
return ADDON.getLocalizedString(stringid)
|
return ADDON.getLocalizedString(stringid)
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,7 +242,7 @@ def kodi_time_to_millis(time):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def tryEncode(uniString, encoding='utf-8'):
|
def try_encode(uniString, encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
Will try to encode uniString (in unicode) to encoding. This possibly
|
Will try to encode uniString (in unicode) to encoding. This possibly
|
||||||
fails with e.g. Android TV's Python, which does not accept arguments for
|
fails with e.g. Android TV's Python, which does not accept arguments for
|
||||||
|
@ -252,7 +258,7 @@ def tryEncode(uniString, encoding='utf-8'):
|
||||||
return uniString
|
return uniString
|
||||||
|
|
||||||
|
|
||||||
def tryDecode(string, encoding='utf-8'):
|
def try_decode(string, encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
Will try to decode string (encoded) using encoding. This possibly
|
Will try to decode string (encoded) using encoding. This possibly
|
||||||
fails with e.g. Android TV's Python, which does not accept arguments for
|
fails with e.g. Android TV's Python, which does not accept arguments for
|
||||||
|
@ -295,7 +301,7 @@ def escape_html(string):
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
def DateToKodi(stamp):
|
def unix_date_to_kodi(stamp):
|
||||||
"""
|
"""
|
||||||
converts a Unix time stamp (seconds passed sinceJanuary 1 1970) to a
|
converts a Unix time stamp (seconds passed sinceJanuary 1 1970) to a
|
||||||
propper, human-readable time stamp used by Kodi
|
propper, human-readable time stamp used by Kodi
|
||||||
|
@ -313,49 +319,42 @@ def DateToKodi(stamp):
|
||||||
return localdate
|
return localdate
|
||||||
|
|
||||||
|
|
||||||
def IntFromStr(string):
|
def unix_timestamp(seconds_into_the_future=None):
|
||||||
"""
|
|
||||||
Returns an int from string or the int 0 if something happened
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
result = int(string)
|
|
||||||
except:
|
|
||||||
result = 0
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def getUnixTimestamp(secondsIntoTheFuture=None):
|
|
||||||
"""
|
"""
|
||||||
Returns a Unix time stamp (seconds passed since January 1 1970) for NOW as
|
Returns a Unix time stamp (seconds passed since January 1 1970) for NOW as
|
||||||
an integer.
|
an integer.
|
||||||
|
|
||||||
Optionally, pass secondsIntoTheFuture: positive int's will result in a
|
Optionally, pass seconds_into_the_future: positive int's will result in a
|
||||||
future timestamp, negative the past
|
future timestamp, negative the past
|
||||||
"""
|
"""
|
||||||
if secondsIntoTheFuture:
|
if seconds_into_the_future:
|
||||||
future = datetime.utcnow() + timedelta(seconds=secondsIntoTheFuture)
|
future = datetime.utcnow() + timedelta(seconds=seconds_into_the_future)
|
||||||
else:
|
else:
|
||||||
future = datetime.utcnow()
|
future = datetime.utcnow()
|
||||||
return timegm(future.timetuple())
|
return timegm(future.timetuple())
|
||||||
|
|
||||||
|
|
||||||
def kodiSQL(media_type="video"):
|
def kodi_sql(media_type=None):
|
||||||
|
"""
|
||||||
|
Open a connection to the Kodi database.
|
||||||
|
media_type: 'video' (standard if not passed), 'plex', 'music', 'texture'
|
||||||
|
"""
|
||||||
if media_type == "plex":
|
if media_type == "plex":
|
||||||
dbPath = v.DB_PLEX_PATH
|
db_path = v.DB_PLEX_PATH
|
||||||
elif media_type == "music":
|
elif media_type == "music":
|
||||||
dbPath = v.DB_MUSIC_PATH
|
db_path = v.DB_MUSIC_PATH
|
||||||
elif media_type == "texture":
|
elif media_type == "texture":
|
||||||
dbPath = v.DB_TEXTURE_PATH
|
db_path = v.DB_TEXTURE_PATH
|
||||||
else:
|
else:
|
||||||
dbPath = v.DB_VIDEO_PATH
|
db_path = v.DB_VIDEO_PATH
|
||||||
return connect(dbPath, timeout=60.0)
|
return connect(db_path, timeout=60.0)
|
||||||
|
|
||||||
|
|
||||||
def create_actor_db_index():
|
def create_actor_db_index():
|
||||||
"""
|
"""
|
||||||
Index the "actors" because we got a TON - speed up SELECT and WHEN
|
Index the "actors" because we got a TON - speed up SELECT and WHEN
|
||||||
"""
|
"""
|
||||||
conn = kodiSQL('video')
|
conn = kodi_sql('video')
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
|
@ -370,6 +369,10 @@ def create_actor_db_index():
|
||||||
|
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
|
"""
|
||||||
|
User navigated to the PKC settings, Advanced, and wants to reset the Kodi
|
||||||
|
database and possibly PKC entirely
|
||||||
|
"""
|
||||||
# Are you sure you want to reset your local Kodi database?
|
# Are you sure you want to reset your local Kodi database?
|
||||||
if not dialog('yesno',
|
if not dialog('yesno',
|
||||||
heading='{plex} %s ' % language(30132),
|
heading='{plex} %s ' % language(30132),
|
||||||
|
@ -380,7 +383,7 @@ def reset():
|
||||||
plex_command('STOP_SYNC', 'True')
|
plex_command('STOP_SYNC', 'True')
|
||||||
count = 10
|
count = 10
|
||||||
while window('plex_dbScan') == "true":
|
while window('plex_dbScan') == "true":
|
||||||
log.debug("Sync is running, will retry: %s..." % count)
|
LOG.debug("Sync is running, will retry: %s...", count)
|
||||||
count -= 1
|
count -= 1
|
||||||
if count == 0:
|
if count == 0:
|
||||||
# Could not stop the database from running. Please try again later.
|
# Could not stop the database from running. Please try again later.
|
||||||
|
@ -391,14 +394,14 @@ def reset():
|
||||||
xbmc.sleep(1000)
|
xbmc.sleep(1000)
|
||||||
|
|
||||||
# Clean up the playlists
|
# Clean up the playlists
|
||||||
deletePlaylists()
|
delete_playlists()
|
||||||
|
|
||||||
# Clean up the video nodes
|
# Clean up the video nodes
|
||||||
deleteNodes()
|
delete_nodes()
|
||||||
|
|
||||||
# Wipe the kodi databases
|
# Wipe the kodi databases
|
||||||
log.info("Resetting the Kodi video database.")
|
LOG.info("Resetting the Kodi video database.")
|
||||||
connection = kodiSQL('video')
|
connection = kodi_sql('video')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
@ -410,8 +413,8 @@ def reset():
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
if settings('enableMusic') == "true":
|
if settings('enableMusic') == "true":
|
||||||
log.info("Resetting the Kodi music database.")
|
LOG.info("Resetting the Kodi music database.")
|
||||||
connection = kodiSQL('music')
|
connection = kodi_sql('music')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
@ -423,8 +426,8 @@ def reset():
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
# Wipe the Plex database
|
# Wipe the Plex database
|
||||||
log.info("Resetting the Plex database.")
|
LOG.info("Resetting the Plex database.")
|
||||||
connection = kodiSQL('plex')
|
connection = kodi_sql('plex')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
@ -441,21 +444,21 @@ def reset():
|
||||||
if dialog('yesno',
|
if dialog('yesno',
|
||||||
heading='{plex} %s ' % language(30132),
|
heading='{plex} %s ' % language(30132),
|
||||||
line1=language(39602)):
|
line1=language(39602)):
|
||||||
log.info("Resetting all cached artwork.")
|
LOG.info("Resetting all cached artwork.")
|
||||||
# Remove all existing textures first
|
# Remove all existing textures first
|
||||||
path = xbmc.translatePath("special://thumbnails/")
|
path = xbmc.translatePath("special://thumbnails/")
|
||||||
if exists(path):
|
if exists(path):
|
||||||
rmtree(tryDecode(path), ignore_errors=True)
|
rmtree(try_decode(path), ignore_errors=True)
|
||||||
# remove all existing data from texture DB
|
# remove all existing data from texture DB
|
||||||
connection = kodiSQL('texture')
|
connection = kodi_sql('texture')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
query = 'SELECT tbl_name FROM sqlite_master WHERE type=?'
|
query = 'SELECT tbl_name FROM sqlite_master WHERE type=?'
|
||||||
cursor.execute(query, ("table", ))
|
cursor.execute(query, ("table", ))
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
for row in rows:
|
for row in rows:
|
||||||
tableName = row[0]
|
table_name = row[0]
|
||||||
if(tableName != "version"):
|
if table_name != "version":
|
||||||
cursor.execute("DELETE FROM %s" % tableName)
|
cursor.execute("DELETE FROM %s" % table_name)
|
||||||
connection.commit()
|
connection.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
@ -469,44 +472,36 @@ def reset():
|
||||||
line1=language(39603)):
|
line1=language(39603)):
|
||||||
# Delete the settings
|
# Delete the settings
|
||||||
addon = xbmcaddon.Addon()
|
addon = xbmcaddon.Addon()
|
||||||
addondir = tryDecode(xbmc.translatePath(addon.getAddonInfo('profile')))
|
addondir = try_decode(xbmc.translatePath(addon.getAddonInfo('profile')))
|
||||||
dataPath = "%ssettings.xml" % addondir
|
LOG.info("Deleting: settings.xml")
|
||||||
log.info("Deleting: settings.xml")
|
remove("%ssettings.xml" % addondir)
|
||||||
remove(dataPath)
|
|
||||||
reboot_kodi()
|
reboot_kodi()
|
||||||
|
|
||||||
|
|
||||||
def profiling(sortby="cumulative"):
|
def profiling(sortby="cumulative"):
|
||||||
# Will print results to Kodi log
|
"""
|
||||||
|
Will print results to Kodi log. Must be enabled in the Python source code
|
||||||
|
"""
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
"""
|
||||||
|
decorator construct
|
||||||
|
"""
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
"""
|
||||||
pr = Profile()
|
wrapper construct
|
||||||
|
"""
|
||||||
pr.enable()
|
profile = Profile()
|
||||||
|
profile.enable()
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
pr.disable()
|
profile.disable()
|
||||||
|
string_io = StringIO()
|
||||||
s = StringIO()
|
stats = Stats(profile, stream=string_io).sort_stats(sortby)
|
||||||
ps = Stats(pr, stream=s).sort_stats(sortby)
|
stats.print_stats()
|
||||||
ps.print_stats()
|
LOG.info(string_io.getvalue())
|
||||||
log.info(s.getvalue())
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def convertdate(date):
|
|
||||||
try:
|
|
||||||
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
except TypeError:
|
|
||||||
# TypeError: attribute of type 'NoneType' is not callable
|
|
||||||
# Known Kodi/python error
|
|
||||||
date = datetime(*(strptime(date, "%Y-%m-%dT%H:%M:%SZ")[0:6]))
|
|
||||||
|
|
||||||
return date
|
|
||||||
|
|
||||||
|
|
||||||
def compare_version(current, minimum):
|
def compare_version(current, minimum):
|
||||||
"""
|
"""
|
||||||
|
@ -515,38 +510,36 @@ def compare_version(current, minimum):
|
||||||
|
|
||||||
Input strings: e.g. "1.2.3"; always with Major, Minor and Patch!
|
Input strings: e.g. "1.2.3"; always with Major, Minor and Patch!
|
||||||
"""
|
"""
|
||||||
log.info("current DB: %s minimum DB: %s" % (current, minimum))
|
LOG.info("current DB: %s minimum DB: %s", current, minimum)
|
||||||
try:
|
try:
|
||||||
currMajor, currMinor, currPatch = current.split(".")
|
curr_major, curr_minor, curr_patch = current.split(".")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# there WAS no current DB, e.g. deleted.
|
# there WAS no current DB, e.g. deleted.
|
||||||
return True
|
return True
|
||||||
minMajor, minMinor, minPatch = minimum.split(".")
|
min_major, min_minor, min_patch = minimum.split(".")
|
||||||
currMajor = int(currMajor)
|
curr_major = int(curr_major)
|
||||||
currMinor = int(currMinor)
|
curr_minor = int(curr_minor)
|
||||||
currPatch = int(currPatch)
|
curr_patch = int(curr_patch)
|
||||||
minMajor = int(minMajor)
|
min_major = int(min_major)
|
||||||
minMinor = int(minMinor)
|
min_minor = int(min_minor)
|
||||||
minPatch = int(minPatch)
|
min_patch = int(min_patch)
|
||||||
|
|
||||||
if currMajor > minMajor:
|
if curr_major > min_major:
|
||||||
return True
|
return True
|
||||||
elif currMajor < minMajor:
|
elif curr_major < min_major:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if currMinor > minMinor:
|
if curr_minor > min_minor:
|
||||||
return True
|
return True
|
||||||
elif currMinor < minMinor:
|
elif curr_minor < min_minor:
|
||||||
return False
|
|
||||||
|
|
||||||
if currPatch >= minPatch:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
return curr_patch >= min_patch
|
||||||
|
|
||||||
|
|
||||||
def normalize_nodes(text):
|
def normalize_nodes(text):
|
||||||
# For video nodes
|
"""
|
||||||
|
For video nodes
|
||||||
|
"""
|
||||||
text = text.replace(":", "")
|
text = text.replace(":", "")
|
||||||
text = text.replace("/", "-")
|
text = text.replace("/", "-")
|
||||||
text = text.replace("\\", "-")
|
text = text.replace("\\", "-")
|
||||||
|
@ -561,13 +554,15 @@ def normalize_nodes(text):
|
||||||
# Remove dots from the last character as windows can not have directories
|
# Remove dots from the last character as windows can not have directories
|
||||||
# with dots at the end
|
# with dots at the end
|
||||||
text = text.rstrip('.')
|
text = text.rstrip('.')
|
||||||
text = tryEncode(normalize('NFKD', unicode(text, 'utf-8')))
|
text = try_encode(normalize('NFKD', unicode(text, 'utf-8')))
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def normalize_string(text):
|
def normalize_string(text):
|
||||||
# For theme media, do not modify unless
|
"""
|
||||||
# modified in TV Tunes
|
For theme media, do not modify unless modified in TV Tunes
|
||||||
|
"""
|
||||||
text = text.replace(":", "")
|
text = text.replace(":", "")
|
||||||
text = text.replace("/", "-")
|
text = text.replace("/", "-")
|
||||||
text = text.replace("\\", "-")
|
text = text.replace("\\", "-")
|
||||||
|
@ -580,7 +575,7 @@ def normalize_string(text):
|
||||||
# Remove dots from the last character as windows can not have directories
|
# Remove dots from the last character as windows can not have directories
|
||||||
# with dots at the end
|
# with dots at the end
|
||||||
text = text.rstrip('.')
|
text = text.rstrip('.')
|
||||||
text = tryEncode(normalize('NFKD', unicode(text, 'utf-8')))
|
text = try_encode(normalize('NFKD', unicode(text, 'utf-8')))
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -595,8 +590,8 @@ def indent(elem, level=0):
|
||||||
elem.text = i + " "
|
elem.text = i + " "
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
for elem in elem:
|
for item in elem:
|
||||||
indent(elem, level+1)
|
indent(item, level+1)
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
else:
|
else:
|
||||||
|
@ -604,30 +599,6 @@ def indent(elem, level=0):
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
|
|
||||||
|
|
||||||
def guisettingsXML():
|
|
||||||
"""
|
|
||||||
Returns special://userdata/guisettings.xml as an etree xml root element
|
|
||||||
"""
|
|
||||||
path = tryDecode(xbmc.translatePath("special://profile/"))
|
|
||||||
xmlpath = "%sguisettings.xml" % path
|
|
||||||
|
|
||||||
try:
|
|
||||||
xmlparse = etree.parse(xmlpath)
|
|
||||||
except IOError:
|
|
||||||
# Document is blank or missing
|
|
||||||
root = etree.Element('settings')
|
|
||||||
except etree.ParseError:
|
|
||||||
log.error('Error parsing %s' % xmlpath)
|
|
||||||
# "Kodi cannot parse {0}. PKC will not function correctly. Please visit
|
|
||||||
# {1} and correct your file!"
|
|
||||||
dialog('ok', language(29999), language(39716).format(
|
|
||||||
'guisettings.xml', 'http://kodi.wiki/view/userdata'))
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
root = xmlparse.getroot()
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
class XmlKodiSetting(object):
|
class XmlKodiSetting(object):
|
||||||
"""
|
"""
|
||||||
Used to load a Kodi XML settings file from special://profile as an etree
|
Used to load a Kodi XML settings file from special://profile as an etree
|
||||||
|
@ -670,7 +641,7 @@ class XmlKodiSetting(object):
|
||||||
except IOError:
|
except IOError:
|
||||||
# Document is blank or missing
|
# Document is blank or missing
|
||||||
if self.force_create is False:
|
if self.force_create is False:
|
||||||
log.debug('%s does not seem to exist; not creating', self.path)
|
LOG.debug('%s does not seem to exist; not creating', self.path)
|
||||||
# This will abort __enter__
|
# This will abort __enter__
|
||||||
self.__exit__(IOError, None, None)
|
self.__exit__(IOError, None, None)
|
||||||
# Create topmost xml entry
|
# Create topmost xml entry
|
||||||
|
@ -678,7 +649,7 @@ class XmlKodiSetting(object):
|
||||||
element=etree.Element(self.top_element))
|
element=etree.Element(self.top_element))
|
||||||
self.write_xml = True
|
self.write_xml = True
|
||||||
except etree.ParseError:
|
except etree.ParseError:
|
||||||
log.error('Error parsing %s', self.path)
|
LOG.error('Error parsing %s', self.path)
|
||||||
# "Kodi cannot parse {0}. PKC will not function correctly. Please
|
# "Kodi cannot parse {0}. PKC will not function correctly. Please
|
||||||
# visit {1} and correct your file!"
|
# visit {1} and correct your file!"
|
||||||
dialog('ok', language(29999), language(39716).format(
|
dialog('ok', language(29999), language(39716).format(
|
||||||
|
@ -775,7 +746,7 @@ class XmlKodiSetting(object):
|
||||||
elif old.attrib != attrib:
|
elif old.attrib != attrib:
|
||||||
already_set = False
|
already_set = False
|
||||||
if already_set is True:
|
if already_set is True:
|
||||||
log.debug('Element has already been found')
|
LOG.debug('Element has already been found')
|
||||||
return old
|
return old
|
||||||
# Need to set new setting, indeed
|
# Need to set new setting, indeed
|
||||||
self.write_xml = True
|
self.write_xml = True
|
||||||
|
@ -790,34 +761,35 @@ class XmlKodiSetting(object):
|
||||||
return element
|
return element
|
||||||
|
|
||||||
|
|
||||||
def passwordsXML():
|
def passwords_xml():
|
||||||
# To add network credentials
|
"""
|
||||||
path = tryDecode(xbmc.translatePath("special://userdata/"))
|
To add network credentials to Kodi's password xml
|
||||||
|
"""
|
||||||
|
path = try_decode(xbmc.translatePath("special://userdata/"))
|
||||||
xmlpath = "%spasswords.xml" % path
|
xmlpath = "%spasswords.xml" % path
|
||||||
dialog = xbmcgui.Dialog()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xmlparse = etree.parse(xmlpath)
|
xmlparse = etree.parse(xmlpath)
|
||||||
except IOError:
|
except IOError:
|
||||||
# Document is blank or missing
|
# Document is blank or missing
|
||||||
root = etree.Element('passwords')
|
root = etree.Element('passwords')
|
||||||
skipFind = True
|
skip_find = True
|
||||||
except etree.ParseError:
|
except etree.ParseError:
|
||||||
log.error('Error parsing %s' % xmlpath)
|
LOG.error('Error parsing %s', xmlpath)
|
||||||
# "Kodi cannot parse {0}. PKC will not function correctly. Please visit
|
# "Kodi cannot parse {0}. PKC will not function correctly. Please visit
|
||||||
# {1} and correct your file!"
|
# {1} and correct your file!"
|
||||||
dialog.ok(language(29999), language(39716).format(
|
dialog('ok', language(29999), language(39716).format(
|
||||||
'passwords.xml', 'http://forum.kodi.tv/'))
|
'passwords.xml', 'http://forum.kodi.tv/'))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
root = xmlparse.getroot()
|
root = xmlparse.getroot()
|
||||||
skipFind = False
|
skip_find = False
|
||||||
|
|
||||||
credentials = settings('networkCreds')
|
credentials = settings('networkCreds')
|
||||||
if credentials:
|
if credentials:
|
||||||
# Present user with options
|
# Present user with options
|
||||||
option = dialog.select(
|
option = dialog('select',
|
||||||
"Modify/Remove network credentials", ["Modify", "Remove"])
|
"Modify/Remove network credentials",
|
||||||
|
["Modify", "Remove"])
|
||||||
|
|
||||||
if option < 0:
|
if option < 0:
|
||||||
# User cancelled dialog
|
# User cancelled dialog
|
||||||
|
@ -825,76 +797,86 @@ def passwordsXML():
|
||||||
|
|
||||||
elif option == 1:
|
elif option == 1:
|
||||||
# User selected remove
|
# User selected remove
|
||||||
|
success = False
|
||||||
for paths in root.getiterator('passwords'):
|
for paths in root.getiterator('passwords'):
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if path.find('.//from').text == "smb://%s/" % credentials:
|
if path.find('.//from').text == "smb://%s/" % credentials:
|
||||||
paths.remove(path)
|
paths.remove(path)
|
||||||
log.info("Successfully removed credentials for: %s"
|
LOG.info("Successfully removed credentials for: %s",
|
||||||
% credentials)
|
credentials)
|
||||||
etree.ElementTree(root).write(xmlpath,
|
etree.ElementTree(root).write(xmlpath,
|
||||||
encoding="UTF-8")
|
encoding="UTF-8")
|
||||||
break
|
success = True
|
||||||
else:
|
if not success:
|
||||||
log.error("Failed to find saved server: %s in passwords.xml"
|
LOG.error("Failed to find saved server: %s in passwords.xml",
|
||||||
% credentials)
|
credentials)
|
||||||
|
dialog('notification',
|
||||||
|
heading='{plex}',
|
||||||
|
message="%s not found" % credentials,
|
||||||
|
icon='{warning}',
|
||||||
|
sound=False)
|
||||||
|
return
|
||||||
settings('networkCreds', value="")
|
settings('networkCreds', value="")
|
||||||
xbmcgui.Dialog().notification(
|
dialog('notification',
|
||||||
heading='PlexKodiConnect',
|
heading='{plex}',
|
||||||
message="%s removed from passwords.xml" % credentials,
|
message="%s removed from passwords.xml" % credentials,
|
||||||
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
|
icon='{plex}',
|
||||||
time=1000,
|
sound=False)
|
||||||
sound=False)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
elif option == 0:
|
elif option == 0:
|
||||||
# User selected to modify
|
# User selected to modify
|
||||||
server = dialog.input("Modify the computer name or ip address", credentials)
|
server = dialog('input',
|
||||||
|
"Modify the computer name or ip address",
|
||||||
|
credentials)
|
||||||
if not server:
|
if not server:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# No credentials added
|
# No credentials added
|
||||||
dialog.ok(
|
dialog('ok',
|
||||||
heading="Network credentials",
|
"Network credentials",
|
||||||
line1= (
|
'Input the server name or IP address as indicated in your plex '
|
||||||
"Input the server name or IP address as indicated in your plex library paths. "
|
'library paths. For example, the server name: '
|
||||||
'For example, the server name: \\\\SERVER-PC\\path\\ or smb://SERVER-PC/path is "SERVER-PC".'))
|
'\\\\SERVER-PC\\path\\ or smb://SERVER-PC/path is SERVER-PC')
|
||||||
server = dialog.input("Enter the server name or IP address")
|
server = dialog('input', "Enter the server name or IP address")
|
||||||
if not server:
|
if not server:
|
||||||
return
|
return
|
||||||
server = quote_plus(server)
|
server = quote_plus(server)
|
||||||
|
|
||||||
# Network username
|
# Network username
|
||||||
user = dialog.input("Enter the network username")
|
user = dialog('input', "Enter the network username")
|
||||||
if not user:
|
if not user:
|
||||||
return
|
return
|
||||||
user = quote_plus(user)
|
user = quote_plus(user)
|
||||||
# Network password
|
# Network password
|
||||||
password = dialog.input("Enter the network password",
|
password = dialog('input',
|
||||||
'', # Default input
|
"Enter the network password",
|
||||||
xbmcgui.INPUT_ALPHANUM,
|
'', # Default input
|
||||||
xbmcgui.ALPHANUM_HIDE_INPUT)
|
type='{alphanum}',
|
||||||
|
option='{hide}')
|
||||||
# Need to url-encode the password
|
# Need to url-encode the password
|
||||||
password = quote_plus(password)
|
password = quote_plus(password)
|
||||||
# Add elements. Annoying etree bug where findall hangs forever
|
# Add elements. Annoying etree bug where findall hangs forever
|
||||||
if skipFind is False:
|
if skip_find is False:
|
||||||
skipFind = True
|
skip_find = True
|
||||||
for path in root.findall('.//path'):
|
for path in root.findall('.//path'):
|
||||||
if path.find('.//from').text.lower() == "smb://%s/" % server.lower():
|
if path.find('.//from').text.lower() == "smb://%s/" % server.lower():
|
||||||
# Found the server, rewrite credentials
|
# Found the server, rewrite credentials
|
||||||
path.find('.//to').text = "smb://%s:%s@%s/" % (user, password, server)
|
path.find('.//to').text = ("smb://%s:%s@%s/"
|
||||||
skipFind = False
|
% (user, password, server))
|
||||||
|
skip_find = False
|
||||||
break
|
break
|
||||||
if skipFind:
|
if skip_find:
|
||||||
# Server not found, add it.
|
# Server not found, add it.
|
||||||
path = etree.SubElement(root, 'path')
|
path = etree.SubElement(root, 'path')
|
||||||
etree.SubElement(path, 'from', attrib={'pathversion': "1"}).text = "smb://%s/" % server
|
etree.SubElement(path, 'from', attrib={'pathversion': "1"}).text = \
|
||||||
|
"smb://%s/" % server
|
||||||
topath = "smb://%s:%s@%s/" % (user, password, server)
|
topath = "smb://%s:%s@%s/" % (user, password, server)
|
||||||
etree.SubElement(path, 'to', attrib={'pathversion': "1"}).text = topath
|
etree.SubElement(path, 'to', attrib={'pathversion': "1"}).text = topath
|
||||||
|
|
||||||
# Add credentials
|
# Add credentials
|
||||||
settings('networkCreds', value="%s" % server)
|
settings('networkCreds', value="%s" % server)
|
||||||
log.info("Added server: %s to passwords.xml" % server)
|
LOG.info("Added server: %s to passwords.xml", server)
|
||||||
# Prettify and write to file
|
# Prettify and write to file
|
||||||
try:
|
try:
|
||||||
indent(root)
|
indent(root)
|
||||||
|
@ -902,19 +884,12 @@ def passwordsXML():
|
||||||
pass
|
pass
|
||||||
etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
|
etree.ElementTree(root).write(xmlpath, encoding="UTF-8")
|
||||||
|
|
||||||
# dialog.notification(
|
|
||||||
# heading="PlexKodiConnect",
|
|
||||||
# message="Added to passwords.xml",
|
|
||||||
# icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
|
|
||||||
# time=5000,
|
|
||||||
# sound=False)
|
|
||||||
|
|
||||||
|
def playlist_xsp(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
|
||||||
"""
|
"""
|
||||||
Feed with tagname as unicode
|
Feed with tagname as unicode
|
||||||
"""
|
"""
|
||||||
path = tryDecode(xbmc.translatePath("special://profile/playlists/video/"))
|
path = try_decode(xbmc.translatePath("special://profile/playlists/video/"))
|
||||||
if viewtype == "mixed":
|
if viewtype == "mixed":
|
||||||
plname = "%s - %s" % (tagname, mediatype)
|
plname = "%s - %s" % (tagname, mediatype)
|
||||||
xsppath = "%sPlex %s - %s.xsp" % (path, viewid, mediatype)
|
xsppath = "%sPlex %s - %s.xsp" % (path, viewid, mediatype)
|
||||||
|
@ -923,16 +898,16 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
xsppath = "%sPlex %s.xsp" % (path, viewid)
|
xsppath = "%sPlex %s.xsp" % (path, viewid)
|
||||||
|
|
||||||
# Create the playlist directory
|
# Create the playlist directory
|
||||||
if not exists(tryEncode(path)):
|
if not exists(try_encode(path)):
|
||||||
log.info("Creating directory: %s" % path)
|
LOG.info("Creating directory: %s", path)
|
||||||
makedirs(path)
|
makedirs(path)
|
||||||
|
|
||||||
# Only add the playlist if it doesn't already exists
|
# Only add the playlist if it doesn't already exists
|
||||||
if exists(tryEncode(xsppath)):
|
if exists(try_encode(xsppath)):
|
||||||
log.info('Path %s does exist' % xsppath)
|
LOG.info('Path %s does exist', xsppath)
|
||||||
if delete:
|
if delete:
|
||||||
remove(xsppath)
|
remove(xsppath)
|
||||||
log.info("Successfully removed playlist: %s." % tagname)
|
LOG.info("Successfully removed playlist: %s.", tagname)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Using write process since there's no guarantee the xml declaration works
|
# Using write process since there's no guarantee the xml declaration works
|
||||||
|
@ -942,9 +917,9 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
'movie': 'movies',
|
'movie': 'movies',
|
||||||
'show': 'tvshows'
|
'show': 'tvshows'
|
||||||
}
|
}
|
||||||
log.info("Writing playlist file to: %s" % xsppath)
|
LOG.info("Writing playlist file to: %s", xsppath)
|
||||||
with open(xsppath, 'wb') as f:
|
with open(xsppath, 'wb') as filer:
|
||||||
f.write(tryEncode(
|
filer.write(try_encode(
|
||||||
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
|
||||||
'<smartplaylist type="%s">\n\t'
|
'<smartplaylist type="%s">\n\t'
|
||||||
'<name>Plex %s</name>\n\t'
|
'<name>Plex %s</name>\n\t'
|
||||||
|
@ -954,20 +929,24 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
'</rule>\n'
|
'</rule>\n'
|
||||||
'</smartplaylist>\n'
|
'</smartplaylist>\n'
|
||||||
% (itemtypes.get(mediatype, mediatype), plname, tagname)))
|
% (itemtypes.get(mediatype, mediatype), plname, tagname)))
|
||||||
log.info("Successfully added playlist: %s" % tagname)
|
LOG.info("Successfully added playlist: %s", tagname)
|
||||||
|
|
||||||
|
|
||||||
def deletePlaylists():
|
def delete_playlists():
|
||||||
# Clean up the playlists
|
"""
|
||||||
path = tryDecode(xbmc.translatePath("special://profile/playlists/video/"))
|
Clean up the playlists
|
||||||
|
"""
|
||||||
|
path = try_decode(xbmc.translatePath("special://profile/playlists/video/"))
|
||||||
for root, _, files in walk(path):
|
for root, _, files in walk(path):
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.startswith('Plex'):
|
if file.startswith('Plex'):
|
||||||
remove(join(root, file))
|
remove(join(root, file))
|
||||||
|
|
||||||
def deleteNodes():
|
def delete_nodes():
|
||||||
# Clean up video nodes
|
"""
|
||||||
path = tryDecode(xbmc.translatePath("special://profile/library/video/"))
|
Clean up video nodes
|
||||||
|
"""
|
||||||
|
path = try_decode(xbmc.translatePath("special://profile/library/video/"))
|
||||||
for root, dirs, _ in walk(path):
|
for root, dirs, _ in walk(path):
|
||||||
for directory in dirs:
|
for directory in dirs:
|
||||||
if directory.startswith('Plex-'):
|
if directory.startswith('Plex-'):
|
||||||
|
@ -993,9 +972,9 @@ def CatchExceptions(warnuser=False):
|
||||||
try:
|
try:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error('%s has crashed. Error: %s' % (func.__name__, e))
|
LOG.error('%s has crashed. Error: %s', func.__name__, e)
|
||||||
import traceback
|
import traceback
|
||||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
LOG.error("Traceback:\n%s", traceback.format_exc())
|
||||||
if warnuser:
|
if warnuser:
|
||||||
window('plex_scancrashed', value='true')
|
window('plex_scancrashed', value='true')
|
||||||
return
|
return
|
||||||
|
@ -1012,8 +991,8 @@ def LogTime(func):
|
||||||
starttotal = datetime.now()
|
starttotal = datetime.now()
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
elapsedtotal = datetime.now() - starttotal
|
elapsedtotal = datetime.now() - starttotal
|
||||||
log.info('It took %s to run the function %s'
|
LOG.info('It took %s to run the function %s',
|
||||||
% (elapsedtotal, func.__name__))
|
elapsedtotal, func.__name__)
|
||||||
return result
|
return result
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -1117,43 +1096,3 @@ class Lock_Function(object):
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
return result
|
return result
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# UNUSED METHODS
|
|
||||||
|
|
||||||
|
|
||||||
# def changePlayState(itemType, kodiId, playCount, lastplayed):
|
|
||||||
# """
|
|
||||||
# YET UNUSED
|
|
||||||
|
|
||||||
# kodiId: int or str
|
|
||||||
# playCount: int or str
|
|
||||||
# lastplayed: str or int unix timestamp
|
|
||||||
# """
|
|
||||||
# lastplayed = DateToKodi(lastplayed)
|
|
||||||
|
|
||||||
# kodiId = int(kodiId)
|
|
||||||
# playCount = int(playCount)
|
|
||||||
# method = {
|
|
||||||
# 'movie': ' VideoLibrary.SetMovieDetails',
|
|
||||||
# 'episode': 'VideoLibrary.SetEpisodeDetails',
|
|
||||||
# 'musicvideo': ' VideoLibrary.SetMusicVideoDetails', # TODO
|
|
||||||
# 'show': 'VideoLibrary.SetTVShowDetails', # TODO
|
|
||||||
# '': 'AudioLibrary.SetAlbumDetails', # TODO
|
|
||||||
# '': 'AudioLibrary.SetArtistDetails', # TODO
|
|
||||||
# 'track': 'AudioLibrary.SetSongDetails'
|
|
||||||
# }
|
|
||||||
# params = {
|
|
||||||
# 'movie': {
|
|
||||||
# 'movieid': kodiId,
|
|
||||||
# 'playcount': playCount,
|
|
||||||
# 'lastplayed': lastplayed
|
|
||||||
# },
|
|
||||||
# 'episode': {
|
|
||||||
# 'episodeid': kodiId,
|
|
||||||
# 'playcount': playCount,
|
|
||||||
# 'lastplayed': lastplayed
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# result = jsonrpc(method[itemType]).execute(params[itemType])
|
|
||||||
# log.debug("JSON result was: %s" % result)
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from xbmcaddon import Addon
|
||||||
# For any file operations with KODI function, use encoded strings!
|
# For any file operations with KODI function, use encoded strings!
|
||||||
|
|
||||||
|
|
||||||
def tryDecode(string, encoding='utf-8'):
|
def try_decode(string, encoding='utf-8'):
|
||||||
"""
|
"""
|
||||||
Will try to decode string (encoded) using encoding. This possibly
|
Will try to decode string (encoded) using encoding. This possibly
|
||||||
fails with e.g. Android TV's Python, which does not accept arguments for
|
fails with e.g. Android TV's Python, which does not accept arguments for
|
||||||
|
@ -37,7 +37,7 @@ ADDON_VERSION = _ADDON.getAddonInfo('version')
|
||||||
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
|
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
|
||||||
KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||||
KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion')
|
KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion')
|
||||||
KODI_PROFILE = tryDecode(xbmc.translatePath("special://profile"))
|
KODI_PROFILE = try_decode(xbmc.translatePath("special://profile"))
|
||||||
|
|
||||||
if xbmc.getCondVisibility('system.platform.osx'):
|
if xbmc.getCondVisibility('system.platform.osx'):
|
||||||
PLATFORM = "MacOSX"
|
PLATFORM = "MacOSX"
|
||||||
|
@ -56,7 +56,7 @@ elif xbmc.getCondVisibility('system.platform.android'):
|
||||||
else:
|
else:
|
||||||
PLATFORM = "Unknown"
|
PLATFORM = "Unknown"
|
||||||
|
|
||||||
DEVICENAME = tryDecode(_ADDON.getSetting('deviceName'))
|
DEVICENAME = try_decode(_ADDON.getSetting('deviceName'))
|
||||||
DEVICENAME = DEVICENAME.replace(":", "")
|
DEVICENAME = DEVICENAME.replace(":", "")
|
||||||
DEVICENAME = DEVICENAME.replace("/", "-")
|
DEVICENAME = DEVICENAME.replace("/", "-")
|
||||||
DEVICENAME = DEVICENAME.replace("\\", "-")
|
DEVICENAME = DEVICENAME.replace("\\", "-")
|
||||||
|
@ -86,7 +86,7 @@ _DB_VIDEO_VERSION = {
|
||||||
17: 107, # Krypton
|
17: 107, # Krypton
|
||||||
18: 108 # Leia
|
18: 108 # Leia
|
||||||
}
|
}
|
||||||
DB_VIDEO_PATH = tryDecode(xbmc.translatePath(
|
DB_VIDEO_PATH = try_decode(xbmc.translatePath(
|
||||||
"special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION]))
|
"special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION]))
|
||||||
|
|
||||||
_DB_MUSIC_VERSION = {
|
_DB_MUSIC_VERSION = {
|
||||||
|
@ -97,7 +97,7 @@ _DB_MUSIC_VERSION = {
|
||||||
17: 60, # Krypton
|
17: 60, # Krypton
|
||||||
18: 62 # Leia
|
18: 62 # Leia
|
||||||
}
|
}
|
||||||
DB_MUSIC_PATH = tryDecode(xbmc.translatePath(
|
DB_MUSIC_PATH = try_decode(xbmc.translatePath(
|
||||||
"special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION]))
|
"special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION]))
|
||||||
|
|
||||||
_DB_TEXTURE_VERSION = {
|
_DB_TEXTURE_VERSION = {
|
||||||
|
@ -108,12 +108,12 @@ _DB_TEXTURE_VERSION = {
|
||||||
17: 13, # Krypton
|
17: 13, # Krypton
|
||||||
18: 13 # Leia
|
18: 13 # Leia
|
||||||
}
|
}
|
||||||
DB_TEXTURE_PATH = tryDecode(xbmc.translatePath(
|
DB_TEXTURE_PATH = try_decode(xbmc.translatePath(
|
||||||
"special://database/Textures%s.db" % _DB_TEXTURE_VERSION[KODIVERSION]))
|
"special://database/Textures%s.db" % _DB_TEXTURE_VERSION[KODIVERSION]))
|
||||||
|
|
||||||
DB_PLEX_PATH = tryDecode(xbmc.translatePath("special://database/plex.db"))
|
DB_PLEX_PATH = try_decode(xbmc.translatePath("special://database/plex.db"))
|
||||||
|
|
||||||
EXTERNAL_SUBTITLE_TEMP_PATH = tryDecode(xbmc.translatePath(
|
EXTERNAL_SUBTITLE_TEMP_PATH = try_decode(xbmc.translatePath(
|
||||||
"special://profile/addon_data/%s/temp/" % ADDON_ID))
|
"special://profile/addon_data/%s/temp/" % ADDON_ID))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ from os import makedirs
|
||||||
import xbmc
|
import xbmc
|
||||||
from xbmcvfs import exists
|
from xbmcvfs import exists
|
||||||
|
|
||||||
from utils import window, settings, language as lang, tryEncode, indent, \
|
from utils import window, settings, language as lang, try_encode, indent, \
|
||||||
normalize_nodes, exists_dir, tryDecode
|
normalize_nodes, exists_dir, try_decode
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -62,9 +62,9 @@ class VideoNodes(object):
|
||||||
dirname = viewid
|
dirname = viewid
|
||||||
|
|
||||||
# Returns strings
|
# Returns strings
|
||||||
path = tryDecode(xbmc.translatePath(
|
path = try_decode(xbmc.translatePath(
|
||||||
"special://profile/library/video/"))
|
"special://profile/library/video/"))
|
||||||
nodepath = tryDecode(xbmc.translatePath(
|
nodepath = try_decode(xbmc.translatePath(
|
||||||
"special://profile/library/video/Plex-%s/" % dirname))
|
"special://profile/library/video/Plex-%s/" % dirname))
|
||||||
|
|
||||||
if delete:
|
if delete:
|
||||||
|
@ -77,9 +77,9 @@ class VideoNodes(object):
|
||||||
# Verify the video directory
|
# Verify the video directory
|
||||||
if not exists_dir(path):
|
if not exists_dir(path):
|
||||||
copytree(
|
copytree(
|
||||||
src=tryDecode(xbmc.translatePath(
|
src=try_decode(xbmc.translatePath(
|
||||||
"special://xbmc/system/library/video")),
|
"special://xbmc/system/library/video")),
|
||||||
dst=tryDecode(xbmc.translatePath(
|
dst=try_decode(xbmc.translatePath(
|
||||||
"special://profile/library/video")))
|
"special://profile/library/video")))
|
||||||
|
|
||||||
# Create the node directory
|
# Create the node directory
|
||||||
|
@ -292,7 +292,7 @@ class VideoNodes(object):
|
||||||
# To do: add our photos nodes to kodi picture sources somehow
|
# To do: add our photos nodes to kodi picture sources somehow
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if exists(tryEncode(nodeXML)):
|
if exists(try_encode(nodeXML)):
|
||||||
# Don't recreate xml if already exists
|
# Don't recreate xml if already exists
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -378,9 +378,9 @@ class VideoNodes(object):
|
||||||
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
|
etree.ElementTree(root).write(nodeXML, encoding="UTF-8")
|
||||||
|
|
||||||
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
||||||
tagname = tryEncode(tagname)
|
tagname = try_encode(tagname)
|
||||||
cleantagname = tryDecode(normalize_nodes(tagname))
|
cleantagname = try_decode(normalize_nodes(tagname))
|
||||||
nodepath = tryDecode(xbmc.translatePath(
|
nodepath = try_decode(xbmc.translatePath(
|
||||||
"special://profile/library/video/"))
|
"special://profile/library/video/"))
|
||||||
nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
|
nodeXML = "%splex_%s.xml" % (nodepath, cleantagname)
|
||||||
path = "library://video/plex_%s.xml" % cleantagname
|
path = "library://video/plex_%s.xml" % cleantagname
|
||||||
|
@ -394,9 +394,9 @@ class VideoNodes(object):
|
||||||
if not exists_dir(nodepath):
|
if not exists_dir(nodepath):
|
||||||
# We need to copy over the default items
|
# We need to copy over the default items
|
||||||
copytree(
|
copytree(
|
||||||
src=tryDecode(xbmc.translatePath(
|
src=try_decode(xbmc.translatePath(
|
||||||
"special://xbmc/system/library/video")),
|
"special://xbmc/system/library/video")),
|
||||||
dst=tryDecode(xbmc.translatePath(
|
dst=try_decode(xbmc.translatePath(
|
||||||
"special://profile/library/video")))
|
"special://profile/library/video")))
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
|
@ -411,7 +411,7 @@ class VideoNodes(object):
|
||||||
window('%s.content' % embynode, value=path)
|
window('%s.content' % embynode, value=path)
|
||||||
window('%s.type' % embynode, value=itemtype)
|
window('%s.type' % embynode, value=itemtype)
|
||||||
|
|
||||||
if exists(tryEncode(nodeXML)):
|
if exists(try_encode(nodeXML)):
|
||||||
# Don't recreate xml if already exists
|
# Don't recreate xml if already exists
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,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, unicode):
|
||||||
data = utils.tryEncode(data)
|
data = utils.try_encode(data)
|
||||||
# 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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue