Merge remote-tracking branch 'refs/remotes/croneter/master'

This commit is contained in:
Piotr Smolinski 2016-10-23 22:42:16 +02:00
commit 0b1266d8d8
23 changed files with 790 additions and 606 deletions

View file

@ -5,6 +5,9 @@ PKC combines the best of Kodi - ultra smooth navigation, beautiful and highly cu
Have a look at [some screenshots](https://github.com/croneter/PlexKodiConnect/wiki/Some-PKC-Screenshots) to see what's possible.
### Warning
This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk!
### Download and Installation
[ ![Download](https://api.bintray.com/packages/croneter/PlexKodiConnect/PlexKodiConnect/images/download.svg) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip)
@ -20,9 +23,9 @@ I'm not in any way affiliated with Plex. Thank you very much for a small donatio
### IMPORTANT NOTES
1. If your are using a **low CPU device like a Raspberry Pi or a CuBox**, PKC might be instable or crash during initial sync. Lower the number of threads in the [PKC settings under Sync Options](https://github.com/croneter/PlexKodiConnect/wiki/PKC-settings#sync-options): `Limit artwork cache threads: 5`
1. If you are using a **low CPU device like a Raspberry Pi or a CuBox**, PKC might be instable or crash during initial sync. Lower the number of threads in the [PKC settings under Sync Options](https://github.com/croneter/PlexKodiConnect/wiki/PKC-settings#sync-options): `Limit artwork cache threads: 5`
Don't forget to reboot Kodi after that.
2. If you post logs, your **Plex tokens** might be included. Be sure to double and tripple check for tokens before posting any logs anywhere by searching for `token`
2. If you post logs, your **Plex tokens** might be included. Be sure to double and triple check for tokens before posting any logs anywhere by searching for `token`
3. **Compatibility**: PKC is currently not compatible with Kodi's Video Extras plugin. **Deactivate Video Extras** if trailers/movies start randomly playing.
@ -107,5 +110,5 @@ The addon is not (and will not be) compatible with the MySQL database replacemen
### Credits
- PlexKodiConnect shamelessly uses pretty much all the code of "Emby for Kodi" by the awesome Emby team (see https://github.com/MediaBrowser/plugin.video.emby). Thanks for sharing guys!!
- Plex Companion ("PlexBMC Helper") and other stuff was adapted from @Hippojay 's great work (see https://github.com/hippojay).
- Plex Companion ("PlexBMC Helper") and other stuff were adapted from @Hippojay 's great work (see https://github.com/hippojay).
- The foundation of the Plex API is all iBaa's work (https://github.com/iBaa/PlexConnect).

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect"
version="1.3.3"
version="1.4.2"
provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
@ -19,10 +19,10 @@
<platform>all</platform>
<language>en</language>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
<forum></forum>
<forum>https://forums.plex.tv</forum>
<website>https://github.com/croneter/PlexKodiConnect</website>
<source></source>
<source>https://github.com/croneter/PlexKodiConnect</source>
<summary lang="en"></summary>
<description lang="en">Connect Kodi to your Plex Media Server</description>
<description lang="en">Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk!</description>
</extension>
</addon>

View file

@ -1,3 +1,69 @@
version 1.4.2
Make previous version available for everyone
version 1.4.1 (beta only)
- Fix Kodi crashing on low powered devices
- Fix movie year for Krypton (reset your Kodi DB!)
- Only start downloading art AFTER sync completed
- Add warning to addon description
- Revert "Don't set-up clips/trailers like other videos"
version 1.4.0
- Compatibility with new DVR component of the Plex Media Server
- Speed up sync - download all art in the background. This should especially speed up your initial sync. Remember to let Kodi sit for a while to let it download the artwork
- New setting to look for missing artwork (the non-Plex stuff ;-))
- Fix caching not working
- Ommit DVR status messages from the PMS. This should fix duplicate movies appearing
- Fix possible IndexError on deleting items
- Fix TypeError for manually entering PMS address
- Fix "Opening Stream..." dialog not closing
- Try to prevent OperationalError: database is locked
- Revert "Download one item at a time"
- Remove obsolete import
- Compile regex only once
- Music sync: Fix ProgrammingError
- Don't set-up clips/trailers like other videos (Should fix PKC trying to tell the PMS where we are playing that item)
- Fix capitalization
- Fix backgroundsync KeyError
- Don't double-update playstate of a playing item
- Rearrange the PKC settings
- Use file settings instead of window settings, should fix some errors on changing the PKC settings
- Remove size limitation on sync queue
- Fix TypeError if no extras available
- Other small fixes
version 1.3.9 (beta only)
- Hopefully maximum compatibility with the new DVR component of the Plex Media Server :-)
- Ommit DVR status messages from the PMS. This should fix duplicate movies appearing
- Fix possible IndexError on deleting items
version 1.3.8 (beta only)
- Fix TypeError for manually entering PMS address
- Fix "Opening Stream..." dialog not closing
- Try to prevent OperationalError: database is locked
- Revert "Download one item at a time"
- Remove obsolete import
- Compile regex only once
version 1.3.7 (beta only)
- Music sync: Fix ProgrammingError
- Don't set-up clips/trailers like other videos (Should fix PKC trying to tell the PMS where we are playing that item)
version 1.3.6 (beta only)
- Fix capitalization
version 1.3.5 (beta only)
- Fix backgroundsync KeyError
- Don't double-update playstate of a playing item
version 1.3.4 (beta only)
- Speed up sync - download all art in the background. This should especially speed up your initial sync. Remember to let Kodi sit for a while to let it download the artwork
- New setting to look for missing artwork (the non-Plex stuff ;-))
- Rearrange the PKC settings
- Fix caching not working
- Use file settings instead of window settings, should fix some errors on changing the PKC settings
- Other small fixes
version 1.3.3
- 1.3.1 and 1.3.2 for everyone
- Fix direct play & transcoding subtitles, finally!

View file

@ -10,6 +10,7 @@ import urlparse
import xbmc
import xbmcaddon
import xbmcgui
import xbmcplugin
_addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
@ -99,6 +100,10 @@ class Main():
entrypoint.getVideoFiles(plexid, plexpath)
return
if mode == 'fanart':
log.info('User requested fanarttv refresh')
utils.window('plex_runLibScan', value='fanart')
# Called by e.g. 3rd party plugin video extras
if ("/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0] or
"/Extras" in sys.argv[2]):
@ -156,22 +161,20 @@ class Main():
"Unable to run the sync, the add-on is not connected "
"to a Plex server.")
log.error("Not connected to a PMS.")
return
else:
if mode == 'repair':
utils.window('plex_runLibScan', value="repair")
log.warn("Requesting repair lib sync")
log.info("Requesting repair lib sync")
elif mode == 'manualsync':
log.warn("Requesting full library scan")
log.info("Requesting full library scan")
utils.window('plex_runLibScan', value="full")
elif mode == "texturecache":
import artwork
artwork.Artwork().fullTextureCacheSync()
utils.window('plex_runLibScan', value='del_textures')
else:
entrypoint.doMainListing()
# Prevent Kodi from hanging in an infinite loop waiting for more
xbmcplugin.endOfDirectory(int(sys.argv[1]))
if __name__ == "__main__":
log.info('plugin.video.plexkodiconnect started')

View file

@ -302,11 +302,12 @@
<string id="30536">Users must log in every time Kodi restarts</string>
<string id="30537">RESTART KODI IF YOU MAKE ANY CHANGES</string>
<string id="30538">Complete Re-Sync necessary</string>
<string id="30539">Download additional art from FanArtTV (slower!)</string>
<string id="30539">Download additional art from FanArtTV</string>
<string id="30540">Download movie set/collection art from FanArtTV</string>
<string id="30541">Don't ask to pick a certain stream/quality</string>
<string id="30542">Always pick best quality for trailers</string>
<string id="30543">Kodi runs on a low-power device (e.g. Raspberry Pi)</string>
<string id="30544">Artwork</string>
<!-- service add-on -->
<string id="33000">Welcome</string>
@ -348,12 +349,12 @@
<string id="39000">- Number of trailers to play before a movie</string>
<string id="39001">Boost audio when transcoding</string>
<string id="39002">Burnt-in subtitle size</string>
<string id="39003">Limit download sync threads (recommended for rpi: 1)</string>
<string id="39003">Limit download sync threads (rec. for rpi: 1)</string>
<string id="39004">Enable Plex Companion</string>
<string id="39005">Plex Companion Port (change only if needed)</string>
<string id="39006">Activate Plex Companion debug log</string>
<string id="39007">Activate Plex Companion GDM debug log</string>
<string id="39008">Allows flinging media to Kodi through Plex</string>
<string id="39008">Plex Companion: Allows flinging media to Kodi through Plex</string>
<string id="39009">Could not login to plex.tv. Please try signing in again.</string>
<string id="39010">Problems connecting to plex.tv. Network or internet issue?</string>
<string id="39011">Could not find any Plex server in the network. Aborting...</string>
@ -366,7 +367,7 @@
<string id="39018">[COLOR yellow]Repair local database (force update all content)[/COLOR]</string>
<string id="39019">[COLOR red]Partial or full reset of Database and PKC[/COLOR]</string>
<string id="39020">[COLOR yellow]Cache all images to Kodi texture cache[/COLOR]</string>
<string id="39020">[COLOR yellow]Cache all images to Kodi texture cache now[/COLOR]</string>
<string id="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string>
<string id="39022">local</string>
<string id="39023">Failed to authenticate. Did you login to plex.tv?</string>
@ -408,7 +409,7 @@
<string id="39058">Extend Plex TV Series "On Deck" view to all shows</string>
<string id="39059">Recently Added: Append show title to episode</string>
<string id="39060">Recently Added: Append season- and episode-number SxxExx</string>
<string id="39061">Would you like to download additional artwork from FanArtTV? Sync will be slower!</string>
<string id="39061">Would you like to download additional artwork from FanArtTV in the background?</string>
<string id="39062">Sync when screensaver is deactivated</string>
<string id="39063">Force Transcode Hi10P</string>
<string id="39064">Recently Added: Also show already watched episodes</string>
@ -446,10 +447,14 @@
<string id="39219">Abort (Yes) or save address anyway (No)?</string>
<string id="39220">connected</string>
<string id="39221">plex.tv toggle successful</string>
<string id="39222">[COLOR yellow]Look for missing fanart on FanartTV now[/COLOR]</string>
<string id="39223">Only look for missing fanart or refresh all fanart? The scan will take quite a while and happen in the background.</string>
<string id="39224">Refresh all</string>
<string id="39225">Missing only</string>
<!-- Plex Artwork.py -->
<string id="39250">Running the image cache process can take some time. Are you sure you want continue?</string>
<string id="39250">Running the image cache process can take some time. It will happen in the background. Are you sure you want continue?</string>
<string id="39251">Reset all existing cache data first?</string>
<!-- Plex PlexAPI.py -->

View file

@ -28,10 +28,12 @@
<string id="30536">Benutzer müssen sich bei jedem Neustart von Kodi neu anmelden</string>
<string id="30537">BEI ÄNDERUNGEN KODI NEU STARTEN</string>
<string id="30538">Komplette Neusynchronisierung nötig</string>
<string id="30539">Zusätzliche Bilder von FanArtTV herunterladen (langsamer!)</string>
<string id="30539">Zusätzliche Bilder von FanArtTV herunterladen</string>
<string id="30540">FanArtTV Film-Sets/Collections Bilder herunterladen</string>
<string id="30541">Nicht fragen, welcher Stream/Qualität gespielt wird</string>
<string id="30542">Trailer immer in der besten Qualität abspielen</string>
<string id="30543">Kodi läuft auf langsamer Hardware (z.B. Raspberry Pi)</string>
<string id="30544">Artwork</string>
<string id="30014">Verbindung</string>
<string id="30015">Netzwerk</string>
@ -286,12 +288,12 @@
<string id="39000">- Anzahl abzuspielender Trailer vor einem Film</string>
<string id="39001">Audio Verstärkung (audio boost) wenn transkodiert wird</string>
<string id="39002">Grösse des Untertitels, falls burnt-in</string>
<string id="39003">Anzahl Download Sync Threads beschränken (Empfehlung für rpi: 1)</string>
<string id="39003">Download Sync Threads beschränken (Empfehlung RPI: 1)</string>
<string id="39004">Plex Companion aktivieren</string>
<string id="39005">Plex Companion Port (nur bei Bedarf ändern)</string>
<string id="39006">Plex Companion debug log aktivieren</string>
<string id="39007">Plex Companion GDM debug log aktivieren</string>
<string id="39008">Spiele Inhalt von anderen Plex Geräten ab</string>
<string id="39008">Plex Companion: Spiele Inhalt von anderen Plex Geräten ab</string>
<string id="39009">Login bei plex.tv fehlgeschlagen. Bitte erneut versuchen.</string>
<string id="39010">Netzwerk Verbindungsprobleme für plex.tv. Funktionieren Netzwerk und/oder Internet?</string>
<string id="39011">Konnte keine Plex Server im Netzwerk finden. Abbruch...</string>
@ -304,7 +306,7 @@
<string id="39018">[COLOR yellow]Lokale Datenbank reparieren (allen Inhalt aktualisieren)[/COLOR]</string>
<string id="39019">[COLOR red]Datenbank und auf Wunsch PKC zurücksetzen[/COLOR]</string>
<string id="39020">[COLOR yellow]Alle Plex Bilder in Kodi zwischenspeichern[/COLOR]</string>
<string id="39020">[COLOR yellow]Alle Plex Bilder jetzt in Kodi zwischenspeichern[/COLOR]</string>
<string id="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string>
<string id="39022">lokal</string>
<string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</string>
@ -346,7 +348,7 @@
<string id="39058">Standard Plex Ansicht "Aktuell" auf alle TV Shows erweitern</string>
<string id="39059">"Zuletzt hinzugefügt": Serien- an Episoden-Titel anfügen</string>
<string id="39060">"Zuletzt hinzugefügt": Staffel und Episode anfügen, SxxExx</string>
<string id="39061">Zusätzliche Bilder von FanArtTV herunterladen? Die Synchronisierung wird länger dauern!</string>
<string id="39061">Zusätzliche Bilder von FanArtTV im Hintergrund herunterladen?</string>
<string id="39062">Sync wenn Bildschirmschoner deaktiviert wird</string>
<string id="39063">Hi10p Codec Transkodierung erzwingen</string>
<string id="39064">"Zuletzt hinzugefügt": gesehene Folgen anzeigen</string>
@ -384,9 +386,14 @@
<string id="39219">Abbrechen (Ja) oder PMS Adresse trotzdem speichern (Nein)?</string>
<string id="39220">verbunden</string>
<string id="39221">plex.tv wechsel OK</string>
<string id="39222">[COLOR yellow]Jetzt zusätzliche Bilder auf FanartTV suchen[/COLOR]</string>
<string id="39223">Nur nach fehlender Fanart suchen oder alle Fanart neu herunterladen? Die Suche wird lange dauern und komplett im Hintergrund stattfinden!</string>
<string id="39224">Alle</string>
<string id="39225">Fehlend</string>
<!-- Plex Artwork.py -->
<string id="39250">Alle Plex Bilder in Kodi zwischenzuspeichern kann sehr lange dauern. Möchten Sie wirklich fortfahren?</string>
<string id="39250">Alle Plex Bilder in Kodi zwischenzuspeichern kann lange dauern. Es wird im Hintergrund stattfinden. Möchten Sie wirklich fortfahren?</string>
<string id="39251">Sollen erst alle bestehenden Bilder im Cache gelöscht werden?</string>
<!-- Plex PlexAPI.py -->

View file

@ -57,7 +57,8 @@ import embydb_functions as embydb
log = logging.getLogger("PLEX."+__name__)
addonName = 'PlexKodiConnect'
REGEX_IMDB = re.compile(r'''/(tt\d+)''')
REGEX_TVDB = re.compile(r'''tvdb://(\d+)''')
###############################################################################
@ -620,10 +621,10 @@ class PlexAPI():
args=(PMS, queue))
threadQueue.append(t)
maxThreads = int(settings('imageCacheLimit'))
maxThreads = 5
threads = []
# poke PMS, own thread for each PMS
while(True):
while True:
# Remove finished threads
for t in threads:
if not t.isAlive():
@ -1450,10 +1451,10 @@ class API():
return None
if providername == 'imdb':
regex = re.compile(r'''/(tt\d+)''')
regex = REGEX_IMDB
elif providername == 'tvdb':
# originally e.g. com.plexapp.agents.thetvdb://276564?lang=en
regex = re.compile(r'''tvdb://(\d+)''')
regex = REGEX_TVDB
else:
return None
@ -1720,7 +1721,7 @@ class API():
'year':
"""
elements = []
for extra in self.item.find('Extras'):
for extra in self.item.findall('Extras'):
# Trailer:
key = extra.attrib.get('key', None)
title = extra.attrib.get('title', None)
@ -1841,16 +1842,14 @@ class API():
'Backdrop' : LIST with the first entry xml key "art"
}
"""
item = self.item.attrib
allartworks = {
'Primary': "",
'Primary': "", # corresponds to Plex poster ('thumb')
'Art': "",
'Banner': "",
'Banner': "", # corresponds to Plex banner ('banner') for series
'Logo': "",
'Thumb': "",
'Thumb': "", # corresponds to Plex (grand)parent posters (thumb)
'Disc': "",
'Backdrop': []
'Backdrop': [] # Corresponds to Plex fanart ('art')
}
# Process backdrops
# Get background artwork URL
@ -1870,14 +1869,26 @@ class API():
self.__getOneArtwork('parentArt'))
if not allartworks['Primary']:
allartworks['Primary'] = self.__getOneArtwork('parentThumb')
return allartworks
# Plex does not get much artwork - go ahead and get the rest from
# fanart tv only for movie or tv show
if settings('FanartTV') == 'true':
if item.get('type') in ('movie', 'show'):
externalId = self.getExternalItemId()
if externalId is not None:
allartworks = self.getFanartTVArt(externalId, allartworks)
def getFanartArtwork(self, allartworks, parentInfo=False):
"""
Downloads additional fanart from third party sources (well, link to
fanart only).
allartworks = {
'Primary': "",
'Art': "",
'Banner': "",
'Logo': "",
'Thumb': "",
'Disc': "",
'Backdrop': []
}
"""
externalId = self.getExternalItemId()
if externalId is not None:
allartworks = self.getFanartTVArt(externalId, allartworks)
return allartworks
def getExternalItemId(self, collection=False):
@ -2505,16 +2516,16 @@ class API():
'photo': 'photo'
}
typus = types[typus]
if window('remapSMB') == 'true':
path = path.replace(window('remapSMB%sOrg' % typus),
window('remapSMB%sNew' % typus),
if settings('remapSMB') == 'true':
path = path.replace(settings('remapSMB%sOrg' % typus),
settings('remapSMB%sNew' % typus),
1)
# There might be backslashes left over:
path = path.replace('\\', '/')
elif window('replaceSMB') == 'true':
elif settings('replaceSMB') == 'true':
if path.startswith('\\\\'):
path = 'smb:' + path.replace('\\', '/')
if window('plex_pathverified') == 'true' and forceCheck is False:
if settings('plex_pathverified') == 'true' and forceCheck is False:
return path
# exist() needs a / or \ at the end to work for directories
@ -2535,13 +2546,11 @@ class API():
if self.askToValidate(path):
window('plex_shouldStop', value="true")
path = None
window('plex_pathverified', value='true')
settings('plex_pathverified', value='true')
else:
path = None
elif forceCheck is False:
if window('plex_pathverified') != 'true':
window('plex_pathverified', value='true')
if settings('plex_pathverified') != 'true':
settings('plex_pathverified', value='true')
return path

View file

@ -1,21 +1,20 @@
# -*- coding: utf-8 -*-
###############################################################################
import json
import logging
import requests
import os
import urllib
from sqlite3 import OperationalError
from urllib import quote_plus, unquote
from threading import Thread
import Queue
import xbmc
import xbmcgui
import xbmcvfs
import image_cache_thread
from utils import window, settings, language as lang, kodiSQL
from utils import tryEncode, tryDecode, IfExists
from utils import window, settings, language as lang, kodiSQL, tryEncode, \
tryDecode, IfExists, ThreadMethods, ThreadMethodsAdditionalStop
# Disable annoying requests warnings
import requests.packages.urllib3
@ -27,162 +26,210 @@ log = logging.getLogger("PLEX."+__name__)
###############################################################################
class Artwork():
xbmc_host = 'localhost'
def setKodiWebServerDetails():
"""
Get the Kodi webserver details - used to set the texture cache
"""
xbmc_port = None
xbmc_username = None
xbmc_password = None
imageCacheThreads = []
imageCacheLimitThreads = 0
def __init__(self):
self.enableTextureCache = settings('enableTextureCache') == "true"
self.imageCacheLimitThreads = int(settings('imageCacheLimit'))
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5)
log.info("Using Image Cache Thread Count: %s" % self.imageCacheLimitThreads)
if not self.xbmc_port and self.enableTextureCache:
self.setKodiWebServerDetails()
self.userId = window('currUserId')
self.server = window('pms_server')
def double_urlencode(self, text):
text = self.single_urlencode(text)
text = self.single_urlencode(text)
return text
def single_urlencode(self, text):
text = urllib.urlencode({'blahblahblah': tryEncode(text)}) #urlencode needs a utf- string
text = text[13:]
return tryDecode(text) #return the result again as unicode
def setKodiWebServerDetails(self):
# Get the Kodi webserver details - used to set the texture cache
web_query = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserver"
}
web_query = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserver"
}
result = xbmc.executeJSONRPC(json.dumps(web_query))
result = json.loads(result)
try:
xbmc_webserver_enabled = result['result']['value']
except (KeyError, TypeError):
xbmc_webserver_enabled = False
if not xbmc_webserver_enabled:
# Enable the webserver, it is disabled
web_port = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.SetSettingValue",
"params": {
"setting": "services.webserverport",
"value": 8080
}
}
result = xbmc.executeJSONRPC(json.dumps(web_port))
self.xbmc_port = 8080
web_user = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.SetSettingValue",
"params": {
"setting": "services.webserver",
"value": True
}
}
result = xbmc.executeJSONRPC(json.dumps(web_user))
self.xbmc_username = "kodi"
# Webserver already enabled
}
result = xbmc.executeJSONRPC(json.dumps(web_query))
result = json.loads(result)
try:
xbmc_webserver_enabled = result['result']['value']
except (KeyError, TypeError):
xbmc_webserver_enabled = False
if not xbmc_webserver_enabled:
# Enable the webserver, it is disabled
web_port = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"method": "Settings.SetSettingValue",
"params": {
"setting": "services.webserverport"
"setting": "services.webserverport",
"value": 8080
}
}
result = xbmc.executeJSONRPC(json.dumps(web_port))
result = json.loads(result)
try:
self.xbmc_port = result['result']['value']
except (TypeError, KeyError):
pass
xbmc_port = 8080
web_user = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"method": "Settings.SetSettingValue",
"params": {
"setting": "services.webserverusername"
"setting": "services.webserver",
"value": True
}
}
result = xbmc.executeJSONRPC(json.dumps(web_user))
result = json.loads(result)
try:
self.xbmc_username = result['result']['value']
except TypeError:
pass
web_pass = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserverpassword"
}
xbmc_username = "kodi"
# Webserver already enabled
web_port = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserverport"
}
result = xbmc.executeJSONRPC(json.dumps(web_pass))
result = json.loads(result)
try:
self.xbmc_password = result['result']['value']
except TypeError:
pass
}
result = xbmc.executeJSONRPC(json.dumps(web_port))
result = json.loads(result)
try:
xbmc_port = result['result']['value']
except (TypeError, KeyError):
pass
web_user = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserverusername"
}
}
result = xbmc.executeJSONRPC(json.dumps(web_user))
result = json.loads(result)
try:
xbmc_username = result['result']['value']
except TypeError:
pass
web_pass = {
"jsonrpc": "2.0",
"id": 1,
"method": "Settings.GetSettingValue",
"params": {
"setting": "services.webserverpassword"
}
}
result = xbmc.executeJSONRPC(json.dumps(web_pass))
result = json.loads(result)
try:
xbmc_password = result['result']['value']
except TypeError:
pass
return (xbmc_port, xbmc_username, xbmc_password)
def double_urlencode(text):
return quote_plus(quote_plus(text))
def double_urldecode(text):
return unquote(unquote(text))
@ThreadMethodsAdditionalStop('plex_shouldStop')
@ThreadMethods
class Image_Cache_Thread(Thread):
xbmc_host = 'localhost'
xbmc_port, xbmc_username, xbmc_password = setKodiWebServerDetails()
sleep_between = 50
if settings('low_powered_device') == 'true':
# Low CPU, potentially issues with limited number of threads
# Hence let Kodi wait till download is successful
timeout = (35.1, 35.1)
else:
# High CPU, no issue with limited number of threads
timeout = (0.01, 0.01)
def __init__(self, queue):
self.queue = queue
Thread.__init__(self)
def threadSuspended(self):
# Overwrite method to add TWO additional suspends
return (self._threadSuspended or
window('suspend_LibraryThread') or
window('plex_dbScan'))
def run(self):
threadStopped = self.threadStopped
threadSuspended = self.threadSuspended
queue = self.queue
sleep_between = self.sleep_between
while not threadStopped():
# In the event the server goes offline
while threadSuspended():
# Set in service.py
if threadStopped():
# Abort was requested while waiting. We should exit
log.info("---===### Stopped Image_Cache_Thread ###===---")
return
xbmc.sleep(1000)
try:
url = queue.get(block=False)
except Queue.Empty:
xbmc.sleep(1000)
continue
sleep = 0
while True:
try:
requests.head(
url="http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, url),
auth=(self.xbmc_username, self.xbmc_password),
timeout=self.timeout)
except requests.Timeout:
# We don't need the result, only trigger Kodi to start the
# download. All is well
break
except requests.ConnectionError:
# Server thinks its a DOS attack, ('error 10053')
# Wait before trying again
if sleep > 5:
log.error('Repeatedly got ConnectionError for url %s'
% double_urldecode(url))
break
log.debug('Were trying too hard to download art, server '
'over-loaded. Sleep %s seconds before trying '
'again to download %s'
% (2**sleep, double_urldecode(url)))
xbmc.sleep((2**sleep)*1000)
sleep += 1
continue
except Exception as e:
log.error('Unknown exception for url %s: %s'
% (double_urldecode(url), e))
import traceback
log.error("Traceback:\n%s" % traceback.format_exc())
break
# We did not even get a timeout
break
queue.task_done()
log.debug('Cached art: %s' % double_urldecode(url))
# Sleep for a bit to reduce CPU strain
xbmc.sleep(sleep_between)
log.info("---===### Stopped Image_Cache_Thread ###===---")
class Artwork():
enableTextureCache = settings('enableTextureCache') == "true"
if enableTextureCache:
queue = Queue.Queue()
download_thread = Image_Cache_Thread(queue)
download_thread.start()
def fullTextureCacheSync(self):
# This method will sync all Kodi artwork to textures13.db
# and cache them locally. This takes diskspace!
import xbmcaddon
string = xbmcaddon.Addon().getLocalizedString
"""
This method will sync all Kodi artwork to textures13.db
and cache them locally. This takes diskspace!
"""
if not xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39250)):
"Image Texture Cache", lang(39250)):
return
log.info("Doing Image Cache Sync")
pdialog = xbmcgui.DialogProgress()
pdialog.create("PlexKodiConnect", "Image Cache Sync")
# ask to rest all existing or not
if xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39251), ""):
"Image Texture Cache", lang(39251), ""):
log.info("Resetting all cache data first")
# Remove all existing textures first
path = tryDecode(xbmc.translatePath("special://thumbnails/"))
@ -210,106 +257,36 @@ class Artwork():
if tableName != "version":
cursor.execute("DELETE FROM " + tableName)
connection.commit()
cursor.close()
connection.close()
# Cache all entries in video DB
connection = kodiSQL('video')
cursor = connection.cursor()
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
# dont include actors
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'")
result = cursor.fetchall()
total = len(result)
log.info("Image cache sync about to process %s images" % total)
cursor.close()
log.info("Image cache sync about to process %s video images" % total)
connection.close()
count = 0
for url in result:
if pdialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
message = "%s of %s (%s)" % (count, total, self.imageCacheThreads)
pdialog.update(percentage, "%s %s" % (lang(33045), message))
self.cacheTexture(url[0])
count += 1
# Cache all entries in music DB
connection = kodiSQL('music')
cursor = connection.cursor()
cursor.execute("SELECT url FROM art")
result = cursor.fetchall()
total = len(result)
log.info("Image cache sync about to process %s images" % total)
cursor.close()
count = 0
log.info("Image cache sync about to process %s music images" % total)
connection.close()
for url in result:
if pdialog.iscanceled():
break
percentage = int((float(count) / float(total))*100)
message = "%s of %s" % (count, total)
pdialog.update(percentage, "%s %s" % (lang(33045), message))
self.cacheTexture(url[0])
count += 1
pdialog.update(100, "%s %s" % (lang(33046), len(self.imageCacheThreads)))
log.info("Waiting for all threads to exit")
while len(self.imageCacheThreads):
for thread in self.imageCacheThreads:
if thread.is_finished:
self.imageCacheThreads.remove(thread)
pdialog.update(100, "%s %s" % (lang(33046), len(self.imageCacheThreads)))
log.info("Waiting for all threads to exit: %s" % len(self.imageCacheThreads))
xbmc.sleep(500)
pdialog.close()
def addWorkerImageCacheThread(self, url):
while True:
# removed finished
for thread in self.imageCacheThreads:
if thread.is_finished:
self.imageCacheThreads.remove(thread)
# add a new thread or wait and retry if we hit our limit
if len(self.imageCacheThreads) < self.imageCacheLimitThreads:
newThread = image_cache_thread.ImageCacheThread()
newThread.set_url(self.double_urlencode(url))
newThread.set_host(self.xbmc_host, self.xbmc_port)
newThread.set_auth(self.xbmc_username, self.xbmc_password)
newThread.start()
self.imageCacheThreads.append(newThread)
return
else:
log.info("Waiting for empty queue spot: %s" % len(self.imageCacheThreads))
xbmc.sleep(50)
def cacheTexture(self, url):
# Cache a single image url to the texture cache
if url and self.enableTextureCache:
log.debug("Processing: %s" % url)
self.queue.put(double_urlencode(url))
if not self.imageCacheLimitThreads:
# Add image to texture cache by simply calling it at the http endpoint
url = self.double_urlencode(url)
try: # Extreme short timeouts so we will have a exception.
response = requests.head(
url=("http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, url)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(0.01, 0.01))
# We don't need the result
except: pass
else:
self.addWorkerImageCacheThread(url)
def addArtwork(self, artwork, kodiId, mediaType, cursor):
# Kodi conversion table
kodiart = {
@ -328,7 +305,8 @@ class Artwork():
for art in artwork:
if art == "Backdrop":
# Backdrop entry is a list
# Process extra fanart for artwork downloader (fanart, fanart1, fanart2...)
# Process extra fanart for artwork downloader (fanart, fanart1,
# fanart2...)
backdrops = artwork[art]
backdropsNumber = len(backdrops)
@ -390,67 +368,58 @@ class Artwork():
cursor=cursor)
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
# Possible that the imageurl is an empty string
if imageUrl:
cacheimage = False
if not imageUrl:
# Possible that the imageurl is an empty string
return
query = ' '.join((
"SELECT url",
"FROM art",
"WHERE media_id = ?",
"AND media_type = ?",
"AND type = ?"
))
cursor.execute(query, (kodiId, mediaType, imageType,))
try:
# Update the artwork
url = cursor.fetchone()[0]
except TypeError:
# Add the artwork
log.debug("Adding Art Link for kodiId: %s (%s)"
% (kodiId, imageUrl))
query = (
'''
INSERT INTO art(media_id, media_type, type, url)
VALUES (?, ?, ?, ?)
'''
)
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
else:
if url == imageUrl:
# Only cache artwork if it changed
return
# Only for the main backdrop, poster
if (window('plex_initialScan') != "true" and
imageType in ("fanart", "poster")):
# Delete current entry before updating with the new one
self.deleteCachedArtwork(url)
log.debug("Updating Art url for %s kodiId %s %s -> (%s)"
% (imageType, kodiId, url, imageUrl))
query = ' '.join((
"SELECT url",
"FROM art",
"UPDATE art",
"SET url = ?",
"WHERE media_id = ?",
"AND media_type = ?",
"AND type = ?"
))
cursor.execute(query, (kodiId, mediaType, imageType,))
try: # Update the artwork
url = cursor.fetchone()[0]
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
except TypeError: # Add the artwork
cacheimage = True
log.debug("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl))
query = (
'''
INSERT INTO art(media_id, media_type, type, url)
VALUES (?, ?, ?, ?)
'''
)
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
else: # Only cache artwork if it changed
if url != imageUrl:
cacheimage = True
# Only for the main backdrop, poster
if (window('plex_initialScan') != "true" and
imageType in ("fanart", "poster")):
# Delete current entry before updating with the new one
self.deleteCachedArtwork(url)
log.info("Updating Art url for %s kodiId: %s (%s) -> (%s)"
% (imageType, kodiId, url, imageUrl))
query = ' '.join((
"UPDATE art",
"SET url = ?",
"WHERE media_id = ?",
"AND media_type = ?",
"AND type = ?"
))
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
# Cache fanart and poster in Kodi texture cache
if cacheimage:
self.cacheTexture(imageUrl)
# Cache fanart and poster in Kodi texture cache
self.cacheTexture(imageUrl)
def deleteArtwork(self, kodiId, mediaType, cursor):
query = ' '.join((
"SELECT url, type",
"SELECT url",
"FROM art",
"WHERE media_id = ?",
"AND media_type = ?"
@ -458,159 +427,29 @@ class Artwork():
cursor.execute(query, (kodiId, mediaType,))
rows = cursor.fetchall()
for row in rows:
url = row[0]
imageType = row[1]
if imageType in ("poster", "fanart"):
self.deleteCachedArtwork(url)
self.deleteCachedArtwork(row[0])
def deleteCachedArtwork(self, url):
# Only necessary to remove and apply a new backdrop or poster
connection = kodiSQL('texture')
cursor = connection.cursor()
try:
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
(url,))
cachedurl = cursor.fetchone()[0]
except TypeError:
log.info("Could not find cached url.")
except OperationalError:
log.info("Database is locked. Skip deletion process.")
else: # Delete thumbnail as well as the entry
else:
# Delete thumbnail as well as the entry
thumbnails = tryDecode(
xbmc.translatePath("special://thumbnails/%s" % cachedurl))
log.info("Deleting cached thumbnail: %s" % thumbnails)
xbmcvfs.delete(thumbnails)
log.debug("Deleting cached thumbnail: %s" % thumbnails)
try:
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
connection.commit()
except OperationalError:
log.debug("Issue deleting url from cache. Skipping.")
xbmcvfs.delete(thumbnails)
except Exception as e:
log.error('Could not delete cached artwork %s. Error: %s'
% (thumbnails, e))
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
connection.commit()
finally:
cursor.close()
def getPeopleArtwork(self, people):
# append imageurl if existing
for person in people:
personId = person['Id']
tag = person.get('PrimaryImageTag')
image = ""
if tag:
image = (
"%s/emby/Items/%s/Images/Primary?"
"MaxWidth=400&MaxHeight=400&Index=0&Tag=%s"
% (self.server, personId, tag))
person['imageurl'] = image
return people
def getUserArtwork(self, itemId, itemType):
# Load user information set by UserClient
image = ("%s/emby/Users/%s/Images/%s?Format=original"
% (self.server, itemId, itemType))
return image
def getAllArtwork(self, item, parentInfo=False):
itemid = item['Id']
artworks = item['ImageTags']
backdrops = item.get('BackdropImageTags', [])
maxHeight = 10000
maxWidth = 10000
customquery = ""
# if utils.settings('compressArt') == "true":
# customquery = "&Quality=90"
# if utils.settings('enableCoverArt') == "false":
# customquery += "&EnableImageEnhancers=false"
allartworks = {
'Primary': "",
'Art': "",
'Banner': "",
'Logo': "",
'Thumb': "",
'Disc': "",
'Backdrop': []
}
# Process backdrops
for index, tag in enumerate(backdrops):
artwork = (
"%s/emby/Items/%s/Images/Backdrop/%s?"
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
% (self.server, itemid, index, maxWidth, maxHeight, tag, customquery))
allartworks['Backdrop'].append(artwork)
# Process the rest of the artwork
for art in artworks:
# Filter backcover
if art != "BoxRear":
tag = artworks[art]
artwork = (
"%s/emby/Items/%s/Images/%s/0?"
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
% (self.server, itemid, art, maxWidth, maxHeight, tag, customquery))
allartworks[art] = artwork
# Process parent items if the main item is missing artwork
if parentInfo:
# Process parent backdrops
if not allartworks['Backdrop']:
parentId = item.get('ParentBackdropItemId')
if parentId:
# If there is a parentId, go through the parent backdrop list
parentbackdrops = item['ParentBackdropImageTags']
for index, tag in enumerate(parentbackdrops):
artwork = (
"%s/emby/Items/%s/Images/Backdrop/%s?"
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
% (self.server, parentId, index, maxWidth, maxHeight, tag, customquery))
allartworks['Backdrop'].append(artwork)
# Process the rest of the artwork
parentartwork = ['Logo', 'Art', 'Thumb']
for parentart in parentartwork:
if not allartworks[parentart]:
parentId = item.get('Parent%sItemId' % parentart)
if parentId:
parentTag = item['Parent%sImageTag' % parentart]
artwork = (
"%s/emby/Items/%s/Images/%s/0?"
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
% (self.server, parentId, parentart,
maxWidth, maxHeight, parentTag, customquery))
allartworks[parentart] = artwork
# Parent album works a bit differently
if not allartworks['Primary']:
parentId = item.get('AlbumId')
if parentId and item.get('AlbumPrimaryImageTag'):
parentTag = item['AlbumPrimaryImageTag']
artwork = (
"%s/emby/Items/%s/Images/Primary/0?"
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
% (self.server, parentId, maxWidth, maxHeight, parentTag, customquery))
allartworks['Primary'] = artwork
return allartworks
connection.close()

View file

@ -372,4 +372,31 @@ class Embydb_Functions():
query = "DELETE FROM emby WHERE emby_id LIKE ?"
self.embycursor.execute(query, (plexid+"%",))
def itemsByType(self, plextype):
"""
Returns a list of dictionaries for all Kodi DB items present for
plextype. One dict is of the type
{
'plexId': the Plex id
'kodiId': the Kodi id
'kodi_type': e.g. 'movie', 'tvshow'
'plex_type': e.g. 'Movie', 'Series', the input plextype
}
"""
query = ' '.join((
"SELECT emby_id, kodi_id, media_type",
"FROM emby",
"WHERE emby_type = ?",
))
self.embycursor.execute(query, (plextype, ))
result = []
for row in self.embycursor.fetchall():
result.append({
'plexId': row[0],
'kodiId': row[1],
'kodi_type': row[2],
'plex_type': plextype
})
return result

View file

@ -275,7 +275,6 @@ def doMainListing():
# addDirectoryItem("Add user to session", "plugin://plugin.video.plexkodiconnect/?mode=adduser")
addDirectoryItem(lang(39203), "plugin://plugin.video.plexkodiconnect/?mode=refreshplaylist")
addDirectoryItem(lang(39204), "plugin://plugin.video.plexkodiconnect/?mode=manualsync")
xbmcplugin.endOfDirectory(int(sys.argv[1]))
@ -1363,7 +1362,7 @@ def enterPMS():
settings('plex_machineIdentifier', '')
else:
settings('plex_machineIdentifier', machineIdentifier)
log.info('Setting new PMS to https %s, ip %s, port %s, machineIdentifier '
log.info('Set new PMS to https %s, ip %s, port %s, machineIdentifier %s'
% (https, ip, port, machineIdentifier))
settings('https', value=https)
settings('ipaddress', value=ip)

View file

@ -47,19 +47,13 @@ class ImageCacheThread(threading.Thread):
self.xbmc_password = password
def run(self):
log.debug("Image Caching Thread Processing: %s", self.url_to_process)
try:
response = requests.head(
url=(
"http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, self.urlToProcess)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(5, 5))
url=("http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, self.url_to_process)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(5, 5))
# We don't need the result
except Exception:
pass
log.debug("Image Caching Thread Exited")
self.is_finished = True

View file

@ -417,6 +417,15 @@ class InitialSetup():
if settings('InstallQuestionsAnswered') == 'true':
return
# Is your Kodi installed on a low-powered device like a Raspberry Pi?
# If yes, then we will reduce the strain on Kodi to prevent it from
# crashing.
if dialog.yesno(heading=addonName, line1=lang(39072)):
settings('low_powered_device', value="true")
settings('syncThreadNumber', value="1")
else:
settings('low_powered_device', value="false")
# Additional settings where the user needs to choose
# Direct paths (\\NAS\mymovie.mkv) or addon (http)?
goToSettings = False
@ -463,13 +472,6 @@ class InitialSetup():
log.debug("User opted to use FanArtTV")
settings('FanartTV', value="true")
# Is your Kodi installed on a low-powered device like a Raspberry Pi?
# If yes, then we will reduce the strain on Kodi to prevent it from
# crashing.
if dialog.yesno(heading=addonName, line1=lang(39072)):
log.debug('User thinks that PKC runs on a raspi or similar')
settings('imageCacheLimit', value='1')
# Make sure that we only ask these questions upon first installation
settings('InstallQuestionsAnswered', value='true')

View file

@ -3,7 +3,7 @@
###############################################################################
import logging
import urllib
from urllib import urlencode
from ntpath import dirname
from datetime import datetime
@ -11,8 +11,8 @@ import xbmc
import xbmcgui
import artwork
from utils import settings, window, kodiSQL, CatchExceptions
from utils import tryEncode, tryDecode
from utils import tryEncode, tryDecode, settings, window, kodiSQL, \
CatchExceptions
import embydb_functions as embydb
import kodidb_functions as kodidb
@ -65,6 +65,28 @@ class Items(object):
self.kodiconn.close()
return self
@CatchExceptions(warnuser=True)
def getfanart(self, item, kodiId, mediaType, allartworks=None):
"""
"""
API = PlexAPI.API(item)
if allartworks is None:
allartworks = API.getAllArtwork()
self.artwork.addArtwork(API.getFanartArtwork(allartworks),
kodiId,
mediaType,
self.kodicursor)
# Also get artwork for collections/movie sets
if mediaType == 'movie':
for setname in API.getCollections():
log.debug('Getting artwork for movie set %s' % setname)
setid = self.kodi_db.createBoxset(setname)
self.artwork.addArtwork(API.getSetArtwork(),
setid,
"set",
self.kodicursor)
self.kodi_db.assignBoxset(setid, kodiId)
def itemsbyId(self, items, process, pdialog=None):
# Process items by itemid. Process can be added, update, userdata, remove
embycursor = self.embycursor
@ -394,7 +416,7 @@ class Movies(Items):
'dbid': movieid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
filename = "%s?%s" % (path, urlencode(params))
playurl = filename
# movie table:
@ -408,17 +430,32 @@ class Movies(Items):
% (itemid, title))
# Update the movie entry
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
"c16 = ?, c18 = ?, c19 = ?, c21 = ?, c22 = ?, c23 = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating, writer,
year, imdb, sorttitle, runtime, mpaa, genre, director, title, studio, trailer,
country, playurl, pathid, movieid))
if self.kodiversion > 16:
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?,"
"c06 = ?, c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?,"
"c14 = ?, c15 = ?, c16 = ?, c18 = ?, c19 = ?, c21 = ?,"
"c22 = ?, c23 = ?, premiered = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline,
votecount, rating, writer, year, imdb, sorttitle, runtime,
mpaa, genre, director, title, studio, trailer, country,
playurl, pathid, year, movieid))
else:
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?,"
"c06 = ?, c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?,"
"c14 = ?, c15 = ?, c16 = ?, c18 = ?, c19 = ?, c21 = ?,"
"c22 = ?, c23 = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline,
votecount, rating, writer, year, imdb, sorttitle, runtime,
mpaa, genre, director, title, studio, trailer, country,
playurl, pathid, movieid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
@ -469,16 +506,13 @@ class Movies(Items):
# Process countries
self.kodi_db.addCountries(movieid, countries, "movie")
# Process cast
people = API.getPeopleList()
self.kodi_db.addPeople(movieid, people, "movie")
self.kodi_db.addPeople(movieid, API.getPeopleList(), "movie")
# Process genres
self.kodi_db.addGenres(movieid, genres, "movie")
# Process artwork
allartworks = API.getAllArtwork()
artwork.addArtwork(allartworks, movieid, "movie", kodicursor)
artwork.addArtwork(API.getAllArtwork(), movieid, "movie", kodicursor)
# Process stream details
streams = API.getMediaStreams()
self.kodi_db.addStreams(fileid, streams, runtime)
self.kodi_db.addStreams(fileid, API.getMediaStreams(), runtime)
# Process studios
self.kodi_db.addStudios(movieid, studios, "movie")
# Process tags: view, Plex collection tags
@ -488,7 +522,7 @@ class Movies(Items):
tags.append("Favorite movies")
self.kodi_db.addTags(movieid, tags, "movie")
# Add any sets from Plex collection tags
self.kodi_db.addSets(movieid, collections, kodicursor, API)
self.kodi_db.addSets(movieid, collections, kodicursor)
# Process playstates
self.kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
@ -906,7 +940,7 @@ class TVShows(Items):
'dbid': episodeid,
'mode': "play"
}
filename = "%s?%s" % (path, tryDecode(urllib.urlencode(params)))
filename = "%s?%s" % (path, tryDecode(urlencode(params)))
playurl = filename
parentPathId = self.kodi_db.addPath(
'plugin://plugin.video.plexkodiconnect.tvshows/')
@ -1730,7 +1764,7 @@ class Music(Items):
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, songid, index, artist_name))
kodicursor.execute(query, (artistid, songid, index, artist_name))
# Verify if album artist exists
album_artists = []

View file

@ -508,6 +508,48 @@ class Kodidb_Functions():
self.artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", self.cursor)
def existingArt(self, kodiId, mediaType, refresh=False):
"""
For kodiId, returns an artwork dict with already existing art from
the Kodi db
"""
# Only get EITHER poster OR thumb (should have same URL)
kodiToPKC = {
'banner': 'Banner',
'clearart': 'Art',
'clearlogo': 'Logo',
'discart': 'Disc',
'landscape': 'Thumb',
'thumb': 'Primary'
}
# BoxRear yet unused
result = {'BoxRear': ''}
for art in kodiToPKC:
query = ' '.join((
"SELECT url",
"FROM art",
"WHERE media_id = ?",
"AND media_type = ?",
"AND type = ?"
))
self.cursor.execute(query, (kodiId, mediaType, art,))
try:
url = self.cursor.fetchone()[0]
except TypeError:
url = ""
result[kodiToPKC[art]] = url
# There may be several fanart URLs saved
query = ' '.join((
"SELECT url",
"FROM art",
"WHERE media_id = ?",
"AND media_type = ?",
"AND type LIKE ?"
))
data = self.cursor.execute(query, (kodiId, mediaType, "fanart%",))
result['Backdrop'] = [d[0] for d in data]
return result
def addGenres(self, kodiid, genres, mediatype):
@ -1180,15 +1222,9 @@ class Kodidb_Functions():
))
self.cursor.execute(query, (kodiid, mediatype, tag_id,))
def addSets(self, movieid, collections, kodicursor, API):
def addSets(self, movieid, collections, kodicursor):
for setname in collections:
setid = self.createBoxset(setname)
# Process artwork
if settings('setFanartTV') == 'true':
self.artwork.addArtwork(API.getSetArtwork(),
setid,
"set",
kodicursor)
self.assignBoxset(setid, movieid)
def createBoxset(self, boxsetname):

View file

@ -254,7 +254,7 @@ class KodiMonitor(xbmc.Monitor):
# Save currentFile for cleanup later and to be able to access refs
window('plex_lastPlayedFiled', value=currentFile)
window('Plex_currently_playing_itemid', value=plexid)
window('plex_currently_playing_itemid', value=plexid)
window("emby_%s.itemid" % tryEncode(currentFile), value=plexid)
log.info('Finish playback startup')

View file

@ -5,6 +5,7 @@
import logging
from threading import Thread, Lock
import Queue
from random import shuffle
import xbmc
import xbmcgui
@ -62,7 +63,7 @@ class ThreadedGetMetadata(Thread):
try:
self.queue.get(block=False)
except Queue.Empty:
xbmc.sleep(50)
xbmc.sleep(10)
continue
else:
self.queue.task_done()
@ -73,7 +74,7 @@ class ThreadedGetMetadata(Thread):
try:
self.out_queue.get(block=False)
except Queue.Empty:
xbmc.sleep(50)
xbmc.sleep(10)
continue
else:
self.out_queue.task_done()
@ -93,7 +94,7 @@ class ThreadedGetMetadata(Thread):
updateItem = queue.get(block=False)
# Empty queue
except Queue.Empty:
xbmc.sleep(100)
xbmc.sleep(10)
continue
# Download Metadata
plexXML = PF.GetPlexMetadata(updateItem['itemId'])
@ -155,7 +156,7 @@ class ThreadedProcessMetadata(Thread):
try:
self.queue.get(block=False)
except Queue.Empty:
xbmc.sleep(100)
xbmc.sleep(10)
continue
else:
self.queue.task_done()
@ -175,7 +176,7 @@ class ThreadedProcessMetadata(Thread):
try:
updateItem = queue.get(block=False)
except Queue.Empty:
xbmc.sleep(50)
xbmc.sleep(10)
continue
# Do the work
plexitem = updateItem['XML']
@ -250,11 +251,106 @@ class ThreadedShowSyncInfo(Thread):
processMetadataProgress,
viewName))
# Sleep for x milliseconds
xbmc.sleep(500)
xbmc.sleep(200)
dialog.close()
log.debug('Dialog Infobox thread terminated')
@ThreadMethodsAdditionalSuspend('suspend_LibraryThread')
@ThreadMethodsAdditionalStop('plex_shouldStop')
@ThreadMethods
class ProcessFanartThread(Thread):
"""
Threaded download of additional fanart in the background
Input:
queue Queue.Queue() object that you will need to fill with
dicts of the following form:
{
'itemId': the Plex id as a string
'class': the itemtypes class, e.g. 'Movies'
'mediaType': the kodi media type, e.g. 'movie'
'refresh': True/False if true, will overwrite any 3rd party
fanart. If False, will only get missing
}
"""
def __init__(self, queue):
self.queue = queue
Thread.__init__(self)
def run(self):
threadStopped = self.threadStopped
threadSuspended = self.threadSuspended
queue = self.queue
log.info("---===### Starting FanartSync ###===---")
while not threadStopped():
# In the event the server goes offline
while threadSuspended() or window('plex_dbScan'):
# Set in service.py
if threadStopped():
# Abort was requested while waiting. We should exit
log.info("---===### Stopped FanartSync ###===---")
return
xbmc.sleep(1000)
# grabs Plex item from queue
try:
item = queue.get(block=False)
except Queue.Empty:
xbmc.sleep(200)
continue
if item['refresh'] is True:
# Leave the Plex art untouched
allartworks = None
else:
with embydb.GetEmbyDB() as emby_db:
try:
kodiId = emby_db.getItem_byId(item['itemId'])[0]
except TypeError:
log.error('Could not get Kodi id for plex id %s'
% item['itemId'])
queue.task_done()
continue
with kodidb.GetKodiDB('video') as kodi_db:
allartworks = kodi_db.existingArt(kodiId,
item['mediaType'])
# Check if we even need to get additional art
needsupdate = False
for key, value in allartworks.iteritems():
if not value and not key == 'BoxRear':
needsupdate = True
break
if needsupdate is False:
log.debug('Already got all art for Plex id %s'
% item['itemId'])
queue.task_done()
continue
log.debug('Getting additional fanart for Plex id %s'
% item['itemId'])
# Download Metadata
xml = PF.GetPlexMetadata(item['itemId'])
if xml is None:
# Did not receive a valid XML - skip that item for now
log.warn("Could not get metadata for %s. Skipping that item "
"for now" % item['itemId'])
queue.task_done()
continue
elif xml == 401:
log.warn('HTTP 401 returned by PMS. Too much strain? '
'Cancelling sync for now')
# Kill remaining items in queue (for main thread to cont.)
queue.task_done()
continue
# Do the work
with getattr(itemtypes, item['class'])() as cls:
cls.getfanart(xml[0], kodiId, item['mediaType'], allartworks)
# signals to queue job is done
log.debug('Done getting fanart for Plex id %s' % item['itemId'])
queue.task_done()
log.info("---===### Stopped FanartSync ###===---")
@ThreadMethodsAdditionalSuspend('suspend_LibraryThread')
@ThreadMethodsAdditionalStop('plex_shouldStop')
@ThreadMethods
@ -275,6 +371,9 @@ class LibrarySync(Thread):
self.queue = queue
self.itemsToProcess = []
self.sessionKeys = []
self.fanartqueue = Queue.Queue()
if settings('FanartTV') == 'true':
self.fanartthread = ProcessFanartThread(self.fanartqueue)
# How long should we wait at least to process new/changed PMS items?
self.saftyMargin = int(settings('saftyMargin'))
@ -285,6 +384,7 @@ class LibrarySync(Thread):
self.vnodes = videonodes.VideoNodes()
self.dialog = xbmcgui.Dialog()
self.syncThreadNumber = int(settings('syncThreadNumber'))
self.installSyncDone = settings('SyncInstallRunDone') == 'true'
self.showDbSync = settings('dbSyncIndicator') == 'true'
self.enableMusic = settings('enableMusic') == "true"
@ -292,9 +392,6 @@ class LibrarySync(Thread):
'enableBackgroundSync') == "true"
self.limitindex = int(settings('limitindex'))
if settings('plex_pathverified') == 'true':
window('plex_pathverified', value='true')
# Just in case a time sync goes wrong
self.timeoffset = int(settings('kodiplextimeoffset'))
window('kodiplextimeoffset', value=str(self.timeoffset))
@ -760,6 +857,7 @@ class LibrarySync(Thread):
'viewName': xxx,
'viewId': xxx,
'title': xxx
'mediaType': xxx, e.g. 'movie', 'episode'
self.allPlexElementsId APPENDED(!!) dict
= {itemid: checksum}
@ -779,12 +877,15 @@ class LibrarySync(Thread):
# Only update if movie is not in Kodi or checksum is
# different
if kodi_checksum != plex_checksum:
self.updatelist.append({'itemId': itemId,
'itemType': itemType,
'method': method,
'viewName': viewName,
'viewId': viewId,
'title': title})
self.updatelist.append({
'itemId': itemId,
'itemType': itemType,
'method': method,
'viewName': viewName,
'viewId': viewId,
'title': title,
'mediaType': item.attrib.get('type')
})
else:
# Initial or repair sync: get all Plex movies
for item in xml:
@ -796,12 +897,15 @@ class LibrarySync(Thread):
plex_checksum = ("K%s%s"
% (itemId, item.attrib.get('updatedAt', '')))
self.allPlexElementsId[itemId] = plex_checksum
self.updatelist.append({'itemId': itemId,
'itemType': itemType,
'method': method,
'viewName': viewName,
'viewId': viewId,
'title': title})
self.updatelist.append({
'itemId': itemId,
'itemType': itemType,
'method': method,
'viewName': viewName,
'viewId': viewId,
'title': title,
'mediaType': item.attrib.get('type')
})
def GetAndProcessXMLs(self, itemType, showProgress=True):
"""
@ -836,15 +940,16 @@ class LibrarySync(Thread):
# Populate queue: GetMetadata
for updateItem in self.updatelist:
getMetadataQueue.put(updateItem)
# Spawn GetMetadata thread for downloading
# Spawn GetMetadata threads for downloading
threads = []
thread = ThreadedGetMetadata(getMetadataQueue,
processMetadataQueue,
getMetadataLock,
processMetadataLock)
thread.setDaemon(True)
thread.start()
threads.append(thread)
for i in range(min(self.syncThreadNumber, itemNumber)):
thread = ThreadedGetMetadata(getMetadataQueue,
processMetadataQueue,
getMetadataLock,
processMetadataLock)
thread.setDaemon(True)
thread.start()
threads.append(thread)
log.info("%s download threads spawned" % len(threads))
# Spawn one more thread to process Metadata, once downloaded
thread = ThreadedProcessMetadata(processMetadataQueue,
@ -887,6 +992,18 @@ class LibrarySync(Thread):
except:
pass
log.info("Sync threads finished")
if (settings('FanartTV') == 'true' and
itemType in ('Movies', 'TVShows')):
# Save to queue for later processing
typus = {'Movies': 'movie', 'TVShows': 'tvshow'}[itemType]
for item in self.updatelist:
if item['mediaType'] in ('movie', 'tvshow'):
self.fanartqueue.put({
'itemId': item['itemId'],
'class': itemType,
'mediaType': typus,
'refresh': False
})
self.updatelist = []
@LogTime
@ -1284,7 +1401,17 @@ class LibrarySync(Thread):
if item['state'] == 9:
successful = self.process_deleteditems(item)
else:
successful = self.process_newitems(item)
successful, item = self.process_newitems(item)
if successful and settings('FanartTV') == 'true':
if item['mediatype'] in ('movie', 'show'):
mediaType = {'movie': 'Movie'}[item['mediatype']]
cls = {'movie': 'Movies'}[item['mediatype']]
self.fanartqueue.put({
'itemId': item['ratingKey'],
'class': cls,
'mediaType': mediaType,
'refresh': False
})
if successful is True:
deleteListe.append(i)
else:
@ -1310,13 +1437,16 @@ class LibrarySync(Thread):
def process_newitems(self, item):
ratingKey = item['ratingKey']
xml = PF.GetPlexMetadata(ratingKey)
if xml in (None, 401):
log.error('Could not download data for %s, skipping' % ratingKey)
return False
try:
mediatype = xml[0].attrib['type']
except (IndexError, KeyError, TypeError):
log.error('Could not download metadata for %s' % ratingKey)
return False, item
log.debug("Processing new/updated PMS item: %s" % ratingKey)
viewtag = xml.attrib.get('librarySectionTitle')
viewid = xml.attrib.get('librarySectionID')
mediatype = xml[0].attrib.get('type')
# Attach mediatype for later
item['mediatype'] = mediatype
if mediatype == 'movie':
self.videoLibUpdate = True
with itemtypes.Movies() as movie:
@ -1335,7 +1465,7 @@ class LibrarySync(Thread):
music.add_updateSong(xml[0],
viewtag=viewtag,
viewid=viewid)
return True
return True, item
def process_deleteditems(self, item):
if item.get('type') == 1:
@ -1362,10 +1492,19 @@ class LibrarySync(Thread):
"processing queue" for later
"""
for item in data:
typus = item.get('type')
state = item.get('state')
if 'tv.plex' in item.get('identifier', ''):
# Ommit Plex DVR messages - the Plex IDs are not corresponding
# (DVR ratingKeys are not unique and might correspond to a
# movie or episode)
continue
typus = int(item.get('type', 0))
state = int(item.get('state', 0))
if state == 9 or typus in (1, 4, 10):
itemId = item.get('itemID')
# Only process deleted items OR movies, episodes, tracks/songs
itemId = str(item.get('itemID', '0'))
if itemId == '0':
log.warn('Received malformed PMS message: %s' % item)
continue
# Have we already added this element?
for existingItem in self.itemsToProcess:
if existingItem['ratingKey'] == itemId:
@ -1400,7 +1539,7 @@ class LibrarySync(Thread):
sessionKey = item.get('sessionKey')
# Do we already have a sessionKey stored?
if sessionKey not in self.sessionKeys:
if window('plex_serverowned') == 'false':
if settings('plex_serverowned') == 'false':
# Not our PMS, we are not authorized to get the
# sessions
# On the bright side, it must be us playing :-)
@ -1419,7 +1558,10 @@ class LibrarySync(Thread):
continue
currSess = self.sessionKeys[sessionKey]
if window('plex_serverowned') != 'false':
if window('plex_currently_playing_itemid') == ratingKey:
# Don't update what we already know
continue
if settings('plex_serverowned') != 'false':
# Identify the user - same one as signed on with PKC? Skip
# update if neither session's username nor userid match
# (Owner sometime's returns id '1', not always)
@ -1484,6 +1626,30 @@ class LibrarySync(Thread):
with itemFkt() as Fkt:
Fkt.updatePlaystate(item)
def fanartSync(self, refresh=False):
"""
Checks all Plex movies and TV shows whether they still need fanart
refresh=True Force refresh all external fanart
"""
items = []
typus = {
'Movie': 'Movies',
'Series': 'TVShows'
}
with embydb.GetEmbyDB() as emby_db:
for plextype in typus:
items.extend(emby_db.itemsByType(plextype))
# Shuffle the list to not always start out identically
shuffle(items)
for item in items:
self.fanartqueue.put({
'itemId': item['plexId'],
'mediaType': item['kodi_type'],
'class': typus[item['plex_type']],
'refresh': refresh
})
def run(self):
try:
self.run_internal()
@ -1527,6 +1693,9 @@ class LibrarySync(Thread):
if self.enableMusic:
advancedSettingsXML()
if settings('FanartTV') == 'true':
self.fanartthread.start()
while not threadStopped():
# In the event the server goes offline
@ -1640,6 +1809,21 @@ class LibrarySync(Thread):
forced=True,
icon="error")
window('plex_dbScan', clear=True)
elif window('plex_runLibScan') == 'fanart':
window('plex_runLibScan', clear=True)
# Only look for missing fanart (No)
# or refresh all fanart (Yes)
self.fanartSync(refresh=self.dialog.yesno(
heading=addonName,
line1=lang(39223),
nolabel=lang(39224),
yeslabel=lang(39225)))
elif window('plex_runLibScan') == 'del_textures':
window('plex_runLibScan', clear=True)
window('plex_dbScan', value="true")
import artwork
artwork.Artwork().fullTextureCacheSync()
window('plex_dbScan', clear=True)
else:
now = getUnixTimestamp()
if (now - lastSync > fullSyncInterval and
@ -1666,9 +1850,7 @@ class LibrarySync(Thread):
# Only do this once every 10 seconds
if now - lastProcessing > 10:
lastProcessing = now
window('plex_dbScan', value="true")
processItems()
window('plex_dbScan', clear=True)
# See if there is a PMS message we need to handle
try:
message = queue.get(block=False)
@ -1677,10 +1859,8 @@ class LibrarySync(Thread):
continue
# Got a message from PMS; process it
else:
window('plex_dbScan', value="true")
processMessage(message)
queue.task_done()
window('plex_dbScan', clear=True)
# NO sleep!
continue
else:

View file

@ -11,7 +11,6 @@ import xbmc
import xbmcgui
import xbmcplugin
import artwork
import playutils as putils
import playlist
from utils import window, settings, tryEncode, tryDecode
@ -39,7 +38,6 @@ class PlaybackUtils():
self.userid = window('currUserId')
self.server = window('pms_server')
self.artwork = artwork.Artwork()
if self.API.getType() == 'track':
self.pl = playlist.Playlist(typus='music')
else:
@ -208,6 +206,7 @@ class PlaybackUtils():
(homeScreen and not sizePlaylist)):
# Playlist was created just now, play it.
log.info("Play playlist.")
xbmcplugin.endOfDirectory(int(sys.argv[1]), True, False, False)
xbmc.Player().play(kodiPl, startpos=startPos)
else:

View file

@ -305,7 +305,7 @@ class Player(xbmc.Player):
self.stopAll()
window('Plex_currently_playing_itemid', clear=True)
window('plex_currently_playing_itemid', clear=True)
window('plex_customplaylist', clear=True)
window('plex_customplaylist.seektime', clear=True)
window('plex_customplaylist.seektime', clear=True)

View file

@ -102,7 +102,7 @@ class SubscriptionManager:
while not keyid:
if count > 300:
break
keyid = window('Plex_currently_playing_itemid')
keyid = window('plex_currently_playing_itemid')
xbmc.sleep(100)
count += 1
if keyid:
@ -149,7 +149,7 @@ class SubscriptionManager:
self.cleanup()
# Don't tell anyone if we don't know a Plex ID and are still playing
# (e.g. no stop called). Used for e.g. PVR/TV without PKC usage
if (not window('Plex_currently_playing_itemid')
if (not window('plex_currently_playing_itemid')
and not self.lastplayers):
return True
players = self.js.getPlayers()

View file

@ -174,26 +174,9 @@ class UserClient(threading.Thread):
window('plex_machineIdentifier', value=self.machineIdentifier)
window('plex_servername', value=self.servername)
window('plex_authenticated', value='true')
window('plex_serverowned', value=settings('plex_serverowned'))
window('useDirectPaths', value='true'
if settings('useDirectPaths') == "1" else 'false')
window('replaceSMB', value='true'
if settings('replaceSMB') == "true" else 'false')
window('remapSMB', value='true'
if settings('remapSMB') == "true" else 'false')
if window('remapSMB') == 'true':
items = ('movie', 'tv', 'music')
for item in items:
# Normalize! Get rid of potential (back)slashes at the end
org = settings('remapSMB%sOrg' % item)
new = settings('remapSMB%sNew' % item)
if org.endswith('\\') or org.endswith('/'):
org = org[:-1]
if new.endswith('\\') or new.endswith('/'):
new = new[:-1]
window('remapSMB%sOrg' % item, value=org)
window('remapSMB%sNew' % item, value=new)
# Start DownloadUtils session
doUtils.startSession(reset=True)

View file

@ -182,7 +182,7 @@ def kodiSQL(media_type="video"):
else:
dbPath = getKodiVideoDBPath()
connection = sqlite3.connect(dbPath)
connection = sqlite3.connect(dbPath, timeout=15.0)
return connection
def getKodiVideoDBPath():

View file

@ -19,29 +19,35 @@
<setting id="accessToken" type="text" visible="false" default="" />
</category>
<category label="plex.tv"><!-- plex.tv -->
<category label="Plex">
<setting type="lsep" label="plex.tv"/>
<setting id="plex_status" label="39071" type="text" default="Not logged in to plex.tv" enable="false" /><!-- Current plex.tv status: -->
<setting id="plexLogin" label="Plex user:" type="text" default="" enable="false" />
<setting type="sep" text=""/>
<setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup -->
<setting label="39209" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=togglePlexTV)" option="close" />
<setting id="plexLogin" label="plex.tv username" type="text" default="" visible="false" />
<setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" />
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
<setting id="plexHomeSize" type="number" default="1" visible="false" />
<setting type="lsep" label="39008" />
<setting id="plexCompanion" label="39004" type="bool" default="true" />
<setting id="deviceNameOpt" label="30504" type="bool" default="false" />
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
<setting id="companionPort" label="39005" type="number" default="3005" option="int" visible="eq(-3,true)"/>
<setting id="plex_restricteduser" type="bool" default="false" visible="false"/>
</category>
<category label="30506"><!-- Sync Options -->
<setting type="lsep" label="30537" /><!-- Restart if you make changes -->
<setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/>
<setting id="dbSyncIndicator" label="30507" type="bool" default="true" />
<setting type="sep" /><!-- show syncing progress -->
<setting id="dbSyncIndicator" label="30507" type="bool" default="true" /><!-- show syncing progress -->
<setting type="sep" />
<setting id="low_powered_device" type="bool" label="30543" default="true" /> <!-- Installation on low-powered device? (e.g. Raspberry Pi) -->
<setting id="syncThreadNumber" type="slider" label="39003" default="10" option="int" range="1,1,20"/><!-- Limit download sync threads (recommended for rpi: 1) -->
<setting id="limitindex" type="number" label="30515" default="200" option="int" /><!-- Maximum items to request from the server at once -->
<setting id="enableTextureCache" label="30512" type="bool" default="true" /> <!-- Force Artwork Caching -->
<setting id="imageCacheLimit" type="enum" label="30513" values="Disabled|5|10|15|20|25" default="5" visible="eq(-1,true)" subsetting="true" /> <!-- Limit artwork cache threads -->
<setting id="serverSync" type="bool" label="30514" default="true" visible="false"/><!-- Enable fast startup (requires server plugin) -->
<setting type="lsep" label="39052" /><!-- Background Sync -->
<setting id="enableBackgroundSync" type="bool" label="39026" default="true" visible="true"/>
<setting id="saftyMargin" type="slider" label="39051" default="60" option="int" range="10,1,300" visible="eq(-1,true)"/>
@ -49,8 +55,6 @@
<setting id="dbSyncScreensaver" type="bool" label="39062" default="false" /><!--Sync when screensaver is deactivated-->
<setting type="lsep" label="30538" /><!-- Complete Re-Sync necessary -->
<setting id="FanartTV" label="30539" type="bool" default="false" /><!-- Download additional art from FanArtTV -->
<setting id="setFanartTV" label="30540" type="bool" default="true" /><!-- Download movie set/collection art from FanArtTV -->
<setting id="enableMusic" type="bool" label="30509" default="true" />
<setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="true"/> <!-- Playback mode -->
@ -104,6 +108,12 @@
<setting id="bestTrailer" type="bool" label="30542" default="true" />
</category>
<category label="30544"><!-- artwork -->
<setting id="enableTextureCache" label="30512" type="bool" default="true" /> <!-- Force Artwork Caching -->
<setting id="FanartTV" label="30539" type="bool" default="false" /><!-- Download additional art from FanArtTV -->
<setting label="39222" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=fanart)" option="close" visible="eq(-1,true)" /> <!-- Look for missing fanart on FanartTV now -->
<setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache now -->
</category>
<!--
<category label="30235" visible="false">
<setting id="enableCoverArt" type="bool" label="30157" default="true" visible="false"/>
@ -116,16 +126,6 @@
<setting id="newmusictime" type="number" label="30533" visible="false" default="2" option="int" subsetting="true" />
</category>
-->
<category label="Plex Companion">
<setting type="lsep" label="39008" />
<setting id="plexCompanion" label="39004" type="bool" default="true" />
<setting id="deviceNameOpt" label="30504" type="bool" default="false" />
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
<setting id="companionPort" label="39005" type="number" default="3005" option="int" visible="eq(-3,true)"/>
<setting id="plex_restricteduser" type="bool" default="false" visible="false"/>
</category>
<category label="39073"><!-- Appearance Tweaks -->
<setting id="connectMsg" type="bool" label="30249" default="true" />
@ -144,7 +144,6 @@
<setting id="logLevel" type="enum" label="30004" values="Disabled|Info|Debug" default="1" />
<setting id="startupDelay" type="number" label="30529" default="0" option="int" />
<setting label="39018" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=repair)" option="close" /> <!-- Repair local database (force update all content) -->
<setting label="39020" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=texturecache)" option="close" /> <!-- Cache all images to Kodi texture cache -->
<setting label="30535" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=deviceid)" /><!-- Reset device id uuid -->
<setting label="39021" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect/?mode=thememedia)" option="close" visible="false" /> <!-- Sync Plex Theme Media to Kodi -->
<setting type="lsep" label="39049" /><!-- Nothing works? Try a full reset -->

View file

@ -85,6 +85,8 @@ class Service():
log.warn("%s Version: %s" % (addonName, self.clientInfo.getVersion()))
log.warn("Using plugin paths: %s"
% (settings('useDirectPaths') != "true"))
log.warn("Using a low powered device: %s"
% settings('low_powered_device'))
log.warn("Log Level: %s" % logLevel)
# Reset window props for profile switch
@ -97,9 +99,6 @@ class Service():
"plex_runLibScan", "plex_username", "pms_token", "plex_token",
"pms_server", "plex_machineIdentifier", "plex_servername",
"plex_authenticated", "PlexUserImage", "useDirectPaths",
"replaceSMB", "remapSMB", "remapSMBmovieOrg", "remapSMBtvOrg",
"remapSMBmusicOrg", "remapSMBmovieNew", "remapSMBtvNew",
"remapSMBmusicNew", "remapSMBphotoOrg", "remapSMBphotoNew",
"suspend_LibraryThread", "plex_terminateNow",
"kodiplextimeoffset", "countError", "countUnauthorized"
]
@ -129,7 +128,7 @@ class Service():
initialsetup.InitialSetup().setup()
# Queue for background sync
queue = Queue.Queue(maxsize=200)
queue = Queue.Queue()
connectMsg = True if settings('connectMsg') == 'true' else False