Merge branch 'develop' into translations

This commit is contained in:
tomkat83 2017-05-11 20:16:39 +02:00
commit 4cbb1dc7bc
21 changed files with 226 additions and 108 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-1.7.7-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip) [![stable version](https://img.shields.io/badge/stable_version-1.7.7-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip)
[![beta version](https://img.shields.io/badge/beta_version-1.7.12-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip) [![beta version](https://img.shields.io/badge/beta_version-1.7.17-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip)
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.12" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.17" provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.3.0" /> <import addon="script.module.requests" version="2.3.0" />
@ -44,7 +44,32 @@
<disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer> <disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer>
<disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer> <disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer>
<disclaimer lang="es_ES">Usar a su propio riesgo</disclaimer> <disclaimer lang="es_ES">Usar a su propio riesgo</disclaimer>
<news>version 1.7.12 (beta only) <news>version 1.7.17 (beta only)
- Don't add media by other add-ons to queue
- Fix KeyError for Plex Companion
- Repace Kodi mkdirs with os.makedirs
- Use xbmcvfs exists instead of os.path.exists
version 1.7.16 (beta only)
- Fix PKC complaining about files not found
- Fix multiple subtitles per language not showing
- Update Czech translation
- Fix too many arguments when marking 100% watched
- More small fixes
version 1.7.15 (beta only)
- Fix companion for "Playback via PMS"
- Change sleeping behavior for playqueue client
- Plex Companion: add itemType to playstate
- Less logging
version 1.7.14 (beta only)
- Fix TypeError, but for real now
version 1.7.13 (beta only)
- Fix TypeError with AdvancedSettings.xml missing
version 1.7.12 (beta only)
- Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database - Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database
- Some Plex Companion fixes - Some Plex Companion fixes
- Fix UnicodeDecodeError on user switch - Fix UnicodeDecodeError on user switch

View file

@ -1,3 +1,28 @@
version 1.7.17 (beta only)
- Don't add media by other add-ons to queue
- Fix KeyError for Plex Companion
- Repace Kodi mkdirs with os.makedirs
- Use xbmcvfs exists instead of os.path.exists
version 1.7.16 (beta only)
- Fix PKC complaining about files not found
- Fix multiple subtitles per language not showing
- Update Czech translation
- Fix too many arguments when marking 100% watched
- More small fixes
version 1.7.15 (beta only)
- Fix companion for "Playback via PMS"
- Change sleeping behavior for playqueue client
- Plex Companion: add itemType to playstate
- Less logging
version 1.7.14 (beta only)
- Fix TypeError, but for real now
version 1.7.13 (beta only)
- Fix TypeError with AdvancedSettings.xml missing
version 1.7.12 (beta only) version 1.7.12 (beta only)
- Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database - Major music overhaul: Direct Paths should now work! Many thanks @Memesa for the pointers! Don't forget to reset your database
- Some Plex Companion fixes - Some Plex Companion fixes

View file

@ -1899,3 +1899,23 @@ msgstr ""
msgctxt "#39711" msgctxt "#39711"
msgid "New Plex music library detected. Sorry, but we need to restart Kodi now due to the changes made." msgid "New Plex music library detected. Sorry, but we need to restart Kodi now due to the changes made."
msgstr "" msgstr ""
# Shown during sync process
msgctxt "#39712"
msgid "downloaded"
msgstr ""
# Shown during sync process
msgctxt "#39713"
msgid "processed"
msgstr ""
# Shown during sync process
msgctxt "#39714"
msgid "Sync"
msgstr ""
# Shown during sync process
msgctxt "#39715"
msgid "items"
msgstr ""

View file

@ -1,21 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################### ###############################################################################
import logging
from xbmcgui import ListItem from xbmcgui import ListItem
###############################################################################
log = logging.getLogger("PLEX."+__name__)
###############################################################################
def convert_PKC_to_listitem(PKC_listitem): def convert_PKC_to_listitem(PKC_listitem):
""" """
Insert a PKC_listitem and you will receive a valid XBMC listitem Insert a PKC_listitem and you will receive a valid XBMC listitem
""" """
data = PKC_listitem.data data = PKC_listitem.data
log.debug('data is: %s' % data)
listitem = ListItem(label=data.get('label'), listitem = ListItem(label=data.get('label'),
label2=data.get('label2'), label2=data.get('label2'),
path=data.get('path')) path=data.get('path'))

View file

@ -39,16 +39,17 @@ import xml.etree.ElementTree as etree
from re import compile as re_compile, sub from re import compile as re_compile, sub
from json import dumps from json import dumps
from urllib import urlencode, quote_plus, unquote from urllib import urlencode, quote_plus, unquote
from os.path import basename, join, exists from os.path import basename, join
from os import makedirs from os import makedirs
import xbmcgui import xbmcgui
from xbmc import sleep, executebuiltin from xbmc import sleep, executebuiltin
from xbmcvfs import exists
import clientinfo as client import clientinfo as client
from downloadutils import DownloadUtils from downloadutils import DownloadUtils
from utils import window, settings, language as lang, tryDecode, tryEncode, \ from utils import window, settings, language as lang, tryDecode, tryEncode, \
DateToKodi DateToKodi, exists_dir
from PlexFunctions import PMSHttpsEnabled from PlexFunctions import PMSHttpsEnabled
import plexdb_functions as plexdb import plexdb_functions as plexdb
import variables as v import variables as v
@ -2117,10 +2118,9 @@ class API():
continue continue
if fanartcount > maxfanarts: if fanartcount > maxfanarts:
break break
if exists(tryEncode(entry['url'])): allartworks['Backdrop'].append(
allartworks['Backdrop'].append( entry['url'].replace(' ', '%20'))
entry['url'].replace(' ', '%20')) fanartcount += 1
fanartcount += 1
return allartworks return allartworks
def getSetArtwork(self, parentInfo=False): def getSetArtwork(self, parentInfo=False):
@ -2303,6 +2303,7 @@ class API():
except (TypeError, KeyError, IndexError): except (TypeError, KeyError, IndexError):
return return
kodiindex = 0 kodiindex = 0
fileindex = 0
for stream in mediastreams: for stream in mediastreams:
# Since plex returns all possible tracks together, have to pull # Since plex returns all possible tracks together, have to pull
# only external subtitles - only for these a 'key' exists # only external subtitles - only for these a 'key' exists
@ -2318,8 +2319,10 @@ class API():
if stream.attrib.get('languageCode') is not None: if stream.attrib.get('languageCode') is not None:
path = self.download_external_subtitles( path = self.download_external_subtitles(
"{server}%s" % key, "{server}%s" % key,
"subtitle.%s.%s" % (stream.attrib['languageCode'], "subtitle%02d.%s.%s" % (fileindex,
stream.attrib['codec'])) stream.attrib['languageCode'],
stream.attrib['codec']))
fileindex += 1
# We don't know the language - no need to download # We don't know the language - no need to download
else: else:
path = self.addPlexCredentialsToUrl( path = self.addPlexCredentialsToUrl(
@ -2341,7 +2344,7 @@ class API():
Returns the path to the downloaded subtitle or None Returns the path to the downloaded subtitle or None
""" """
if not exists(v.EXTERNAL_SUBTITLE_TEMP_PATH): if not exists_dir(v.EXTERNAL_SUBTITLE_TEMP_PATH):
makedirs(v.EXTERNAL_SUBTITLE_TEMP_PATH) makedirs(v.EXTERNAL_SUBTITLE_TEMP_PATH)
path = join(v.EXTERNAL_SUBTITLE_TEMP_PATH, filename) path = join(v.EXTERNAL_SUBTITLE_TEMP_PATH, filename)
r = DownloadUtils().downloadUrl(url, return_response=True) r = DownloadUtils().downloadUrl(url, return_response=True)
@ -2541,16 +2544,22 @@ 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)) == 1 check = exists(tryEncode(path))
else: else:
# directories # directories
if "\\" in path: if "\\" in path:
# Add the missing backslash if not path.endswith('\\'):
check = exists(tryEncode(path + "\\")) == 1 # Add the missing backslash
check = exists_dir(tryEncode(path + "\\"))
else:
check = exists_dir(tryEncode(path))
else: else:
check = exists(tryEncode(path + "/")) == 1 if not path.endswith('/'):
check = exists_dir(tryEncode(path + "/"))
else:
check = exists_dir(tryEncode(path))
if check is False: if not check:
if forceCheck is False: if forceCheck is False:
# Validate the path is correct with user intervention # Validate the path is correct with user intervention
if self.askToValidate(path): if self.askToValidate(path):

View file

@ -4,16 +4,16 @@
import logging import logging
from json import dumps, loads from json import dumps, loads
import requests import requests
from os.path import exists
from shutil import rmtree from shutil import rmtree
from urllib import quote_plus, unquote from urllib import quote_plus, unquote
from threading import Thread from threading import Thread
from Queue import Queue, Empty from Queue import Queue, Empty
from xbmc import executeJSONRPC, sleep, translatePath from xbmc import executeJSONRPC, sleep, translatePath
from xbmcvfs import exists
from utils import window, settings, language as lang, kodiSQL, tryEncode, \ from utils import window, settings, language as lang, kodiSQL, tryEncode, \
ThreadMethods, ThreadMethodsAdditionalStop, dialog ThreadMethods, ThreadMethodsAdditionalStop, dialog, exists_dir
# Disable annoying requests warnings # Disable annoying requests warnings
import requests.packages.urllib3 import requests.packages.urllib3
@ -229,7 +229,7 @@ class Artwork():
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 = translatePath("special://thumbnails/") path = translatePath("special://thumbnails/")
if exists(path): if exists_dir(path):
rmtree(path, ignore_errors=True) rmtree(path, ignore_errors=True)
# remove all existing data from texture DB # remove all existing data from texture DB

View file

@ -3,7 +3,7 @@
import logging import logging
from shutil import copyfile from shutil import copyfile
from os import walk, makedirs from os import walk, makedirs
from os.path import basename, join, exists from os.path import basename, join
from sys import argv from sys import argv
from urllib import urlencode from urllib import urlencode
@ -12,7 +12,7 @@ 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, tryEncode, \
CatchExceptions, JSONRPC CatchExceptions, JSONRPC, exists_dir
import downloadutils import downloadutils
from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \ from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \
@ -74,7 +74,7 @@ def togglePlexTV():
if settings('plexToken'): if settings('plexToken'):
log.info('Reseting plex.tv credentials in settings') log.info('Reseting plex.tv credentials in settings')
settings('plexLogin', value="") settings('plexLogin', value="")
settings('plexToken', value=""), settings('plexToken', value="")
settings('plexid', value="") settings('plexid', value="")
settings('plexHomeSize', value="1") settings('plexHomeSize', value="1")
settings('plexAvatar', value="") settings('plexAvatar', value="")
@ -498,7 +498,7 @@ def getVideoFiles(plexId, params):
path = path.replace('\\', '\\\\') path = path.replace('\\', '\\\\')
# Directory only, get rid of filename # Directory only, get rid of filename
path = path.replace(basename(path), '') path = path.replace(basename(path), '')
if exists(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 = join(root, directory) item_path = join(root, directory)
@ -513,6 +513,7 @@ def getVideoFiles(plexId, params):
xbmcplugin.addDirectoryItem(handle=HANDLE, xbmcplugin.addDirectoryItem(handle=HANDLE,
url=file, url=file,
listitem=li) listitem=li)
break
else: else:
log.error('Kodi cannot access folder %s' % path) log.error('Kodi cannot access folder %s' % path)
xbmcplugin.endOfDirectory(HANDLE) xbmcplugin.endOfDirectory(HANDLE)
@ -536,7 +537,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 = translatePath("special://thumbnails/plex/%s/" % plexid) fanartDir = translatePath("special://thumbnails/plex/%s/" % plexid)
if not exists(fanartDir): if not exists_dir(fanartDir):
# Download the images to the cache directory # Download the images to the cache directory
makedirs(fanartDir) makedirs(fanartDir)
xml = GetPlexMetadata(plexid) xml = GetPlexMetadata(plexid)

View file

@ -402,11 +402,11 @@ class InitialSetup():
# Get current Kodi video cache setting # Get current Kodi video cache setting
cache, _ = advancedsettings_xml(['cache', 'memorysize']) cache, _ = advancedsettings_xml(['cache', 'memorysize'])
if cache is not None: if cache is None:
cache = str(cache.text)
else:
# Kodi default cache # Kodi default cache
cache = '20971520' cache = '20971520'
else:
cache = str(cache.text)
log.info('Current Kodi video memory cache in bytes: %s' % cache) log.info('Current Kodi video memory cache in bytes: %s' % cache)
settings('kodi_video_cache', value=cache) settings('kodi_video_cache', value=cache)

View file

@ -165,7 +165,7 @@ class Items(object):
'Mark item played at %s percent.' 'Mark item played at %s percent.'
% (item['ratingKey'], str(complete), MARK_PLAYED_AT), 1) % (item['ratingKey'], str(complete), MARK_PLAYED_AT), 1)
if complete >= MARK_PLAYED_AT: if complete >= MARK_PLAYED_AT:
log.info('Marking as completely watched in Kodi', 1) log.info('Marking as completely watched in Kodi')
sleep(500) sleep(500)
try: try:
item['viewCount'] += 1 item['viewCount'] += 1
@ -1189,10 +1189,9 @@ class TVShows(Items):
v.KODI_TYPE_SEASON) v.KODI_TYPE_SEASON)
for season in seasons: for season in seasons:
self.removeSeason(season[1]) self.removeSeason(season[1])
else: # Delete plex season entries
# Delete plex season entries plex_db.removeItems_byParentId(showid,
plex_db.removeItems_byParentId(showid, v.KODI_TYPE_SEASON)
v.KODI_TYPE_SEASON)
self.removeShow(showid) self.removeShow(showid)
plex_db.removeItem(show[0]) plex_db.removeItem(show[0])
@ -1208,14 +1207,12 @@ class TVShows(Items):
seasonid, v.KODI_TYPE_EPISODE) seasonid, v.KODI_TYPE_EPISODE)
for episode in season_episodes: for episode in season_episodes:
self.removeEpisode(episode[1], episode[2]) self.removeEpisode(episode[1], episode[2])
else: # Remove plex episodes
# Remove plex episodes plex_db.removeItems_byParentId(seasonid,
plex_db.removeItems_byParentId(seasonid, v.KODI_TYPE_EPISODE)
v.KODI_TYPE_EPISODE) # Remove plex seasons
else: plex_db.removeItems_byParentId(kodiid,
# Remove plex seasons v.KODI_TYPE_SEASON)
plex_db.removeItems_byParentId(kodiid,
v.KODI_TYPE_SEASON)
# Remove tvshow # Remove tvshow
self.removeShow(kodiid) self.removeShow(kodiid)
@ -1228,9 +1225,8 @@ class TVShows(Items):
v.KODI_TYPE_EPISODE) v.KODI_TYPE_EPISODE)
for episode in season_episodes: for episode in season_episodes:
self.removeEpisode(episode[1], episode[2]) self.removeEpisode(episode[1], episode[2])
else: # Remove plex episodes
# Remove plex episodes plex_db.removeItems_byParentId(kodiid, v.KODI_TYPE_EPISODE)
plex_db.removeItems_byParentId(kodiid, v.KODI_TYPE_EPISODE)
# Remove season # Remove season
self.removeSeason(kodiid) self.removeSeason(kodiid)

View file

@ -1409,9 +1409,8 @@ class Kodidb_Functions():
ID = 'idEpisode' ID = 'idEpisode'
elif kodi_type == v.KODI_TYPE_SONG: elif kodi_type == v.KODI_TYPE_SONG:
ID = 'idSong' ID = 'idSong'
query = ('''UPDATE %s SET userrating = ? WHERE %s = ?''' query = '''UPDATE ? SET userrating = ? WHERE ? = ?'''
% (kodi_type, ID)) self.cursor.execute(query, (kodi_type, userrating, ID, kodi_id))
self.cursor.execute(query, (userrating, kodi_id))
def create_entry_uniqueid(self): def create_entry_uniqueid(self):
self.cursor.execute( self.cursor.execute(

View file

@ -54,9 +54,8 @@ class Threaded_Show_Sync_Info(Thread):
total = self.total total = self.total
dialog = self.dialog dialog = self.dialog
threadStopped = self.threadStopped threadStopped = self.threadStopped
dialog.create("%s: Sync %s: %s items" dialog.create("%s %s: %s %s"
% (lang(29999), self.item_type, str(total)), % (lang(39714), self.item_type, str(total), lang(39715)))
"Starting")
total = 2 * total total = 2 * total
totalProgress = 0 totalProgress = 0
@ -71,9 +70,11 @@ class Threaded_Show_Sync_Info(Thread):
except ZeroDivisionError: except ZeroDivisionError:
percentage = 0 percentage = 0
dialog.update(percentage, dialog.update(percentage,
message="%s downloaded. %s processed: %s" message="%s %s. %s %s: %s"
% (get_progress, % (get_progress,
lang(39712),
process_progress, process_progress,
lang(39713),
viewName)) viewName))
# Sleep for x milliseconds # Sleep for x milliseconds
sleep(200) sleep(200)

View file

@ -4,10 +4,10 @@ import logging
from threading import Thread from threading import Thread
import Queue import Queue
from random import shuffle from random import shuffle
from os.path import exists
import xbmc import xbmc
import xbmcgui import xbmcgui
from xbmcvfs import exists
from utils import window, settings, getUnixTimestamp, sourcesXML,\ from utils import window, settings, getUnixTimestamp, sourcesXML,\
ThreadMethods, ThreadMethodsAdditionalStop, LogTime, getScreensaver,\ ThreadMethods, ThreadMethodsAdditionalStop, LogTime, getScreensaver,\
@ -297,15 +297,6 @@ class LibrarySync(Thread):
} }
if self.enableMusic: if self.enableMusic:
process['music'] = self.PlexMusic process['music'] = self.PlexMusic
if self.direct_paths is True:
if music.set_excludefromscan_music_folders() is True:
log.info('Detected new Music library - restarting now')
# 'New Plex music library detected. Sorry, but we need to
# restart Kodi now due to the changes made.'
dialog('ok', lang(29999), lang(39711))
from xbmc import executebuiltin
executebuiltin('RestartApp')
return False
# Do the processing # Do the processing
for itemtype in process: for itemtype in process:
@ -483,6 +474,16 @@ class LibrarySync(Thread):
""" """
Compare the views to Plex Compare the views to Plex
""" """
if self.direct_paths is True:
if music.set_excludefromscan_music_folders() is True:
log.info('Detected new Music library - restarting now')
# 'New Plex music library detected. Sorry, but we need to
# restart Kodi now due to the changes made.'
dialog('ok', lang(29999), lang(39711))
from xbmc import executebuiltin
executebuiltin('RestartApp')
return False
self.views = [] self.views = []
vnodes = self.vnodes vnodes = self.vnodes

View file

@ -200,7 +200,7 @@ class PlaybackUtils():
playqueue, playqueue,
self.currentPosition+1, self.currentPosition+1,
convert_PKC_to_listitem(listitem), convert_PKC_to_listitem(listitem),
playurl, file=playurl,
kodi_item={'id': kodi_id, 'type': kodi_type}) kodi_item={'id': kodi_id, 'type': kodi_type})
else: else:
# Full metadata$ # Full metadata$

View file

@ -75,6 +75,7 @@ class Playqueue_Object(Playlist_Object_Baseclase):
class Playlist_Item(object): class Playlist_Item(object):
ID = None # Plex playlist/playqueue id, e.g. playQueueItemID ID = None # Plex playlist/playqueue id, e.g. playQueueItemID
plex_id = None # Plex unique item id, "ratingKey" plex_id = None # Plex unique item id, "ratingKey"
plex_type = None # Plex type, e.g. 'movie', 'clip'
plex_UUID = None # Plex librarySectionUUID plex_UUID = None # Plex librarySectionUUID
kodi_id = None # Kodi unique kodi id (unique only within type!) kodi_id = None # Kodi unique kodi id (unique only within type!)
kodi_type = None # Kodi type: 'movie' kodi_type = None # Kodi type: 'movie'
@ -102,20 +103,22 @@ def playlist_item_from_kodi(kodi_item):
""" """
item = Playlist_Item() item = Playlist_Item()
item.kodi_id = kodi_item.get('id') item.kodi_id = kodi_item.get('id')
item.kodi_type = kodi_item.get('type')
if item.kodi_id: if item.kodi_id:
with plexdb.Get_Plex_DB() as plex_db: with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byKodiId(kodi_item['id'], plex_dbitem = plex_db.getItem_byKodiId(kodi_item['id'],
kodi_item['type']) kodi_item['type'])
try: try:
item.plex_id = plex_dbitem[0] item.plex_id = plex_dbitem[0]
item.plex_type = plex_dbitem[2]
item.plex_UUID = plex_dbitem[0] # we dont need the uuid yet :-) item.plex_UUID = plex_dbitem[0] # we dont need the uuid yet :-)
except TypeError: except TypeError:
pass pass
item.file = kodi_item.get('file') item.file = kodi_item.get('file')
if item.file is not None and item.plex_id is None: if item.plex_id is None and item.file is not None:
item.plex_id = dict( query = dict(parse_qsl(urlsplit(item.file).query))
parse_qsl(urlsplit(item.file).query)).get('plex_id') item.plex_id = query.get('plex_id')
item.kodi_type = kodi_item.get('type') item.plex_type = query.get('itemType')
if item.plex_id is None: if item.plex_id is None:
item.uri = 'library://whatever/item/%s' % quote(item.file, safe='') item.uri = 'library://whatever/item/%s' % quote(item.file, safe='')
else: else:
@ -137,13 +140,14 @@ def playlist_item_from_plex(plex_id):
with plexdb.Get_Plex_DB() as plex_db: with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byId(plex_id) plex_dbitem = plex_db.getItem_byId(plex_id)
try: try:
item.plex_type = plex_dbitem[5]
item.kodi_id = plex_dbitem[0] item.kodi_id = plex_dbitem[0]
item.kodi_type = plex_dbitem[4] item.kodi_type = plex_dbitem[4]
except: except:
raise KeyError('Could not find plex_id %s in database' % plex_id) raise KeyError('Could not find plex_id %s in database' % plex_id)
item.plex_UUID = plex_id item.plex_UUID = plex_id
item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' % item.uri = ('library://%s/item/library%%2Fmetadata%%2F%s' %
(item.plex_UUID, plex_id)) (item.plex_UUID, plex_id))
log.debug('Made playlist item from plex: %s' % item) log.debug('Made playlist item from plex: %s' % item)
return item return item
@ -155,6 +159,7 @@ def playlist_item_from_xml(playlist, xml_video_element):
item = Playlist_Item() item = Playlist_Item()
api = API(xml_video_element) api = API(xml_video_element)
item.plex_id = api.getRatingKey() item.plex_id = api.getRatingKey()
item.plex_type = api.getType()
item.ID = xml_video_element.attrib['%sItemID' % playlist.kind] item.ID = xml_video_element.attrib['%sItemID' % playlist.kind]
item.guid = xml_video_element.attrib.get('guid') item.guid = xml_video_element.attrib.get('guid')
if item.guid is not None: if item.guid is not None:
@ -314,8 +319,6 @@ def add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None):
WILL ALSO UPDATE OUR PLAYLISTS WILL ALSO UPDATE OUR PLAYLISTS
""" """
log.debug('Adding new item plex_id: %s, kodi_item: %s on the Plex side at '
'position %s for %s' % (plex_id, kodi_item, pos, playlist))
if plex_id: if plex_id:
try: try:
item = playlist_item_from_plex(plex_id) item = playlist_item_from_plex(plex_id)
@ -532,9 +535,10 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
# We need to add this to our internal queue as well # We need to add this to our internal queue as well
if xml_video_element is not None: if xml_video_element is not None:
item = playlist_item_from_xml(playlist, xml_video_element) item = playlist_item_from_xml(playlist, xml_video_element)
item.file = file
else: else:
item = playlist_item_from_kodi(kodi_item) item = playlist_item_from_kodi(kodi_item)
if file is not None:
item.file = file
playlist.items.insert(pos, item) playlist.items.insert(pos, item)
log.debug('Done inserting for %s' % playlist) log.debug('Done inserting for %s' % playlist)

View file

@ -17,6 +17,7 @@ log = logging.getLogger("PLEX."+__name__)
# Lock used for playqueue manipulations # Lock used for playqueue manipulations
lock = RLock() lock = RLock()
PLUGIN = 'plugin://%s' % v.ADDON_ID
############################################################################### ###############################################################################
@ -147,11 +148,19 @@ class Playqueue(Thread):
log.debug('Comparing new Kodi playqueue %s with our play queue %s' log.debug('Comparing new Kodi playqueue %s with our play queue %s'
% (new, old)) % (new, old))
for i, new_item in enumerate(new): for i, new_item in enumerate(new):
if (new_item['file'].startswith('plugin://') and
not new_item['file'].startswith(PLUGIN)):
# Ignore new media added by other addons
continue
for j, old_item in enumerate(old): for j, old_item in enumerate(old):
if self.threadStopped(): if self.threadStopped():
# Chances are that we got an empty Kodi playlist due to # Chances are that we got an empty Kodi playlist due to
# Kodi exit # Kodi exit
return return
if (old_item['file'].startswith('plugin://') and
not old_item['file'].startswith(PLUGIN)):
# Ignore media by other addons
continue
if new_item.get('id') is None: if new_item.get('id') is None:
identical = old_item.file == new_item['file'] identical = old_item.file == new_item['file']
else: else:
@ -206,5 +215,9 @@ class Playqueue(Thread):
# compare old and new playqueue # compare old and new playqueue
self._compare_playqueues(playqueue, kodi_playqueue) self._compare_playqueues(playqueue, kodi_playqueue)
playqueue.old_kodi_pl = list(kodi_playqueue) playqueue.old_kodi_pl = list(kodi_playqueue)
sleep(50) # Still sleep a bit so Kodi does not become
# unresponsive
sleep(10)
continue
sleep(200)
log.info("----===## PlayQueue client stopped ##===----") log.info("----===## PlayQueue client stopped ##===----")

View file

@ -122,9 +122,11 @@ class SubscriptionManager:
ret += ' shuffle="%s"' % info['shuffle'] ret += ' shuffle="%s"' % info['shuffle']
ret += ' mute="%s"' % self.mute ret += ' mute="%s"' % self.mute
ret += ' repeat="%s"' % info['repeat'] ret += ' repeat="%s"' % info['repeat']
ret += ' itemType="%s"' % info['itemType']
# Might need an update in the future # Might need an update in the future
ret += ' subtitleStreamID="-1"' if ptype == 'video':
ret += ' audioStreamID="-1"' ret += ' subtitleStreamID="-1"'
ret += ' audioStreamID="-1"'
ret += '/>' ret += '/>'
return ret return ret
@ -225,7 +227,8 @@ class SubscriptionManager:
props = self.js.jsonrpc( props = self.js.jsonrpc(
"Player.GetProperties", "Player.GetProperties",
{"playerid": playerid, {"playerid": playerid,
"properties": ["time", "properties": ["type",
"time",
"totaltime", "totaltime",
"speed", "speed",
"shuffled", "shuffled",
@ -244,12 +247,13 @@ class SubscriptionManager:
{"playerid": playerid, {"playerid": playerid,
"properties": ["position"]})['position'] "properties": ["position"]})['position']
try: try:
info['playQueueItemID'] = playqueue.items[pos].ID info['playQueueItemID'] = playqueue.items[pos].ID or 'null'
info['guid'] = playqueue.items[pos].guid info['guid'] = playqueue.items[pos].guid or 'null'
info['playQueueID'] = playqueue.ID info['playQueueID'] = playqueue.ID or 'null'
info['playQueueVersion'] = playqueue.version info['playQueueVersion'] = playqueue.version or 'null'
info['itemType'] = playqueue.items[pos].plex_type or 'null'
except: except:
pass info['itemType'] = props.get('type') or 'null'
except: except:
import traceback import traceback
log.error("Traceback:\n%s" % traceback.format_exc()) log.error("Traceback:\n%s" % traceback.format_exc())

View file

@ -258,10 +258,11 @@ class Plex_DB_Functions():
def getItem_byKodiId(self, kodi_id, kodi_type): def getItem_byKodiId(self, kodi_id, kodi_type):
""" """
Returns the tuple (plex_id, parent_id) for kodi_id and kodi_type Returns the tuple (plex_id, parent_id, plex_type) for kodi_id and
kodi_type
""" """
query = ''' query = '''
SELECT plex_id, parent_id SELECT plex_id, parent_id, plex_type
FROM plex FROM plex
WHERE kodi_id = ? WHERE kodi_id = ?
AND kodi_type = ? AND kodi_type = ?

View file

@ -3,11 +3,11 @@
############################################################################### ###############################################################################
import logging import logging
import threading import threading
from os.path import exists
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcaddon import xbmcaddon
from xbmcvfs import exists
from utils import window, settings, language as lang, ThreadMethods, \ from utils import window, settings, language as lang, ThreadMethods, \
@ -211,10 +211,9 @@ class UserClient(threading.Thread):
# Get /profile/addon_data # Get /profile/addon_data
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')) addondir = xbmc.translatePath(self.addon.getAddonInfo('profile'))
hasSettings = exists("%ssettings.xml" % addondir)
# If there's no settings.xml # If there's no settings.xml
if not hasSettings: if not exists("%ssettings.xml" % addondir):
log.error("Error, no settings.xml found.") log.error("Error, no settings.xml found.")
self.auth = False self.auth = False
return False return False

View file

@ -13,17 +13,18 @@ from unicodedata import normalize
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from functools import wraps from functools import wraps
from calendar import timegm from calendar import timegm
from os.path import exists, join from os.path import join
from os import remove, makedirs, walk from os import remove, walk, makedirs
from shutil import rmtree from shutil import rmtree
from urllib import quote_plus from urllib import quote_plus
import xbmc import xbmc
import xbmcaddon import xbmcaddon
import xbmcgui import xbmcgui
from xbmcvfs import exists, delete
from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \ from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \
DB_PLEX_PATH, KODI_PROFILE DB_PLEX_PATH, KODI_PROFILE, KODIVERSION
############################################################################### ###############################################################################
@ -91,6 +92,30 @@ def settings(setting, value=None):
return tryDecode(addon.getSetting(setting)) return tryDecode(addon.getSetting(setting))
def exists_dir(path):
"""
Safe way to check whether the directory path exists already (broken in Kodi
<17)
Feed with encoded string
"""
if KODIVERSION >= 17:
answ = exists(path)
else:
dummyfile = join(path, 'dummyfile.txt')
try:
with open(dummyfile, 'w') as f:
f.write('text')
except IOError:
# folder does not exist yet
answ = 0
else:
# Folder exists. Delete file again.
delete(dummyfile)
answ = 1
return answ
def language(stringid): def language(stringid):
# Central string retrieval # Central string retrieval
return ADDON.getLocalizedString(stringid) return ADDON.getLocalizedString(stringid)
@ -322,7 +347,7 @@ def reset():
for row in rows: for row in rows:
tablename = row[0] tablename = row[0]
if tablename != "version": if tablename != "version":
cursor.execute("DELETE FROM " + tablename) cursor.execute("DELETE FROM ?", (tablename,))
connection.commit() connection.commit()
cursor.close() cursor.close()
@ -335,7 +360,7 @@ def reset():
for row in rows: for row in rows:
tablename = row[0] tablename = row[0]
if tablename != "version": if tablename != "version":
cursor.execute("DELETE FROM " + tablename) cursor.execute("DELETE FROM ?", (tablename, ))
connection.commit() connection.commit()
cursor.close() cursor.close()
@ -348,7 +373,7 @@ def reset():
for row in rows: for row in rows:
tablename = row[0] tablename = row[0]
if tablename != "version": if tablename != "version":
cursor.execute("DELETE FROM " + tablename) cursor.execute("DELETE FROM ?", (tablename, ))
cursor.execute('DROP table IF EXISTS plex') cursor.execute('DROP table IF EXISTS plex')
cursor.execute('DROP table IF EXISTS view') cursor.execute('DROP table IF EXISTS view')
connection.commit() connection.commit()
@ -372,7 +397,7 @@ def reset():
for row in rows: for row in rows:
tableName = row[0] tableName = row[0]
if(tableName != "version"): if(tableName != "version"):
cursor.execute("DELETE FROM " + tableName) cursor.execute("DELETE FROM ?", (tableName, ))
connection.commit() connection.commit()
cursor.close() cursor.close()
@ -542,8 +567,10 @@ def __setSubElement(element, subelement):
def advancedsettings_xml(node_list, new_value=None, attrib=None, def advancedsettings_xml(node_list, new_value=None, attrib=None,
force_create=False): force_create=False):
""" """
Returns the etree element for nodelist (if it exists) and the tree. None if Returns
not set etree element, tree
or
None, None
node_list is a list of node names starting from the outside, ignoring the node_list is a list of node names starting from the outside, ignoring the
outter advancedsettings. Example nodelist=['video', 'busydialogdelayms'] outter advancedsettings. Example nodelist=['video', 'busydialogdelayms']
@ -576,7 +603,7 @@ def advancedsettings_xml(node_list, new_value=None, attrib=None,
# Document is blank or missing # Document is blank or missing
if new_value is None and attrib is None and force_create is False: if new_value is None and attrib is None and force_create is False:
log.debug('Could not parse advancedsettings.xml, returning None') log.debug('Could not parse advancedsettings.xml, returning None')
return return None, None
# Create topmost xml entry # Create topmost xml entry
tree = etree.ElementTree(element=etree.Element('advancedsettings')) tree = etree.ElementTree(element=etree.Element('advancedsettings'))
root = tree.getroot() root = tree.getroot()

View file

@ -3,13 +3,14 @@
import logging import logging
from shutil import copytree from shutil import copytree
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from os import remove, listdir, makedirs
from os.path import isfile, join
import xbmc import xbmc
from os import remove, makedirs, listdir from xbmcvfs import exists
from os.path import exists, isfile, join
from utils import window, settings, language as lang, tryEncode, indent, \ from utils import window, settings, language as lang, tryEncode, indent, \
normalize_nodes normalize_nodes, exists_dir
import variables as v import variables as v
############################################################################### ###############################################################################
@ -74,14 +75,14 @@ class VideoNodes(object):
return return
# Verify the video directory # Verify the video directory
if exists(path) is False: if not exists_dir(path):
copytree( copytree(
src=xbmc.translatePath("special://xbmc/system/library/video"), src=xbmc.translatePath("special://xbmc/system/library/video"),
dst=xbmc.translatePath("special://profile/library/video")) dst=xbmc.translatePath("special://profile/library/video"))
# Create the node directory # Create the node directory
if mediatype != "photos": if mediatype != "photos":
if exists(nodepath) is False: if not exists_dir(nodepath):
# folder does not exist yet # folder does not exist yet
log.debug('Creating folder %s' % nodepath) log.debug('Creating folder %s' % nodepath)
makedirs(nodepath) makedirs(nodepath)
@ -387,7 +388,7 @@ class VideoNodes(object):
windowpath = "ActivateWindow(Video,%s,return)" % path windowpath = "ActivateWindow(Video,%s,return)" % path
# Create the video node directory # Create the video node directory
if not exists(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=xbmc.translatePath("special://xbmc/system/library/video"), src=xbmc.translatePath("special://xbmc/system/library/video"),