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. 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 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) [ ![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 ### 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. 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. 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 ### 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!! - 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). - 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"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" <addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect" name="PlexKodiConnect"
version="1.3.3" version="1.4.2"
provider-name="croneter"> provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="2.1.0"/> <import addon="xbmc.python" version="2.1.0"/>
@ -19,10 +19,10 @@
<platform>all</platform> <platform>all</platform>
<language>en</language> <language>en</language>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license> <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> <website>https://github.com/croneter/PlexKodiConnect</website>
<source></source> <source>https://github.com/croneter/PlexKodiConnect</source>
<summary lang="en"></summary> <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> </extension>
</addon> </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 version 1.3.3
- 1.3.1 and 1.3.2 for everyone - 1.3.1 and 1.3.2 for everyone
- Fix direct play & transcoding subtitles, finally! - Fix direct play & transcoding subtitles, finally!

View file

@ -10,6 +10,7 @@ import urlparse
import xbmc import xbmc
import xbmcaddon import xbmcaddon
import xbmcgui import xbmcgui
import xbmcplugin
_addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect') _addon = xbmcaddon.Addon(id='plugin.video.plexkodiconnect')
@ -99,6 +100,10 @@ class Main():
entrypoint.getVideoFiles(plexid, plexpath) entrypoint.getVideoFiles(plexid, plexpath)
return 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 # Called by e.g. 3rd party plugin video extras
if ("/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0] or if ("/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0] or
"/Extras" in sys.argv[2]): "/Extras" in sys.argv[2]):
@ -156,22 +161,20 @@ class Main():
"Unable to run the sync, the add-on is not connected " "Unable to run the sync, the add-on is not connected "
"to a Plex server.") "to a Plex server.")
log.error("Not connected to a PMS.") log.error("Not connected to a PMS.")
return
else: else:
if mode == 'repair': if mode == 'repair':
utils.window('plex_runLibScan', value="repair") utils.window('plex_runLibScan', value="repair")
log.warn("Requesting repair lib sync") log.info("Requesting repair lib sync")
elif mode == 'manualsync': elif mode == 'manualsync':
log.warn("Requesting full library scan") log.info("Requesting full library scan")
utils.window('plex_runLibScan', value="full") utils.window('plex_runLibScan', value="full")
elif mode == "texturecache": elif mode == "texturecache":
import artwork utils.window('plex_runLibScan', value='del_textures')
artwork.Artwork().fullTextureCacheSync()
else: else:
entrypoint.doMainListing() entrypoint.doMainListing()
# Prevent Kodi from hanging in an infinite loop waiting for more
xbmcplugin.endOfDirectory(int(sys.argv[1]))
if __name__ == "__main__": if __name__ == "__main__":
log.info('plugin.video.plexkodiconnect started') 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="30536">Users must log in every time Kodi restarts</string>
<string id="30537">RESTART KODI IF YOU MAKE ANY CHANGES</string> <string id="30537">RESTART KODI IF YOU MAKE ANY CHANGES</string>
<string id="30538">Complete Re-Sync necessary</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="30540">Download movie set/collection art from FanArtTV</string>
<string id="30541">Don't ask to pick a certain stream/quality</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="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 --> <!-- service add-on -->
<string id="33000">Welcome</string> <string id="33000">Welcome</string>
@ -348,12 +349,12 @@
<string id="39000">- Number of trailers to play before a movie</string> <string id="39000">- Number of trailers to play before a movie</string>
<string id="39001">Boost audio when transcoding</string> <string id="39001">Boost audio when transcoding</string>
<string id="39002">Burnt-in subtitle size</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="39004">Enable Plex Companion</string>
<string id="39005">Plex Companion Port (change only if needed)</string> <string id="39005">Plex Companion Port (change only if needed)</string>
<string id="39006">Activate Plex Companion debug log</string> <string id="39006">Activate Plex Companion debug log</string>
<string id="39007">Activate Plex Companion GDM 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="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="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> <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="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="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="39021">[COLOR yellow]Sync Emby Theme Media to Kodi[/COLOR]</string>
<string id="39022">local</string> <string id="39022">local</string>
<string id="39023">Failed to authenticate. Did you login to plex.tv?</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="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="39059">Recently Added: Append show title to episode</string>
<string id="39060">Recently Added: Append season- and episode-number SxxExx</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="39062">Sync when screensaver is deactivated</string>
<string id="39063">Force Transcode Hi10P</string> <string id="39063">Force Transcode Hi10P</string>
<string id="39064">Recently Added: Also show already watched episodes</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="39219">Abort (Yes) or save address anyway (No)?</string>
<string id="39220">connected</string> <string id="39220">connected</string>
<string id="39221">plex.tv toggle successful</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 --> <!-- 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> <string id="39251">Reset all existing cache data first?</string>
<!-- Plex PlexAPI.py --> <!-- 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="30536">Benutzer müssen sich bei jedem Neustart von Kodi neu anmelden</string>
<string id="30537">BEI ÄNDERUNGEN KODI NEU STARTEN</string> <string id="30537">BEI ÄNDERUNGEN KODI NEU STARTEN</string>
<string id="30538">Komplette Neusynchronisierung nötig</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="30540">FanArtTV Film-Sets/Collections Bilder herunterladen</string>
<string id="30541">Nicht fragen, welcher Stream/Qualität gespielt wird</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="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="30014">Verbindung</string>
<string id="30015">Netzwerk</string> <string id="30015">Netzwerk</string>
@ -286,12 +288,12 @@
<string id="39000">- Anzahl abzuspielender Trailer vor einem Film</string> <string id="39000">- Anzahl abzuspielender Trailer vor einem Film</string>
<string id="39001">Audio Verstärkung (audio boost) wenn transkodiert wird</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="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="39004">Plex Companion aktivieren</string>
<string id="39005">Plex Companion Port (nur bei Bedarf ändern)</string> <string id="39005">Plex Companion Port (nur bei Bedarf ändern)</string>
<string id="39006">Plex Companion debug log aktivieren</string> <string id="39006">Plex Companion debug log aktivieren</string>
<string id="39007">Plex Companion GDM 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="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="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> <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="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="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="39021">[COLOR yellow]Plex Themes zu Kodi synchronisieren[/COLOR]</string>
<string id="39022">lokal</string> <string id="39022">lokal</string>
<string id="39023">Plex Media Server Authentifizierung fehlgeschlagen. Haben Sie sich bei plex.tv eingeloggt?</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="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="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="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="39062">Sync wenn Bildschirmschoner deaktiviert wird</string>
<string id="39063">Hi10p Codec Transkodierung erzwingen</string> <string id="39063">Hi10p Codec Transkodierung erzwingen</string>
<string id="39064">"Zuletzt hinzugefügt": gesehene Folgen anzeigen</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="39219">Abbrechen (Ja) oder PMS Adresse trotzdem speichern (Nein)?</string>
<string id="39220">verbunden</string> <string id="39220">verbunden</string>
<string id="39221">plex.tv wechsel OK</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 --> <!-- 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> <string id="39251">Sollen erst alle bestehenden Bilder im Cache gelöscht werden?</string>
<!-- Plex PlexAPI.py --> <!-- Plex PlexAPI.py -->

View file

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

View file

@ -1,21 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################### ###############################################################################
import json import json
import logging import logging
import requests import requests
import os import os
import urllib from urllib import quote_plus, unquote
from sqlite3 import OperationalError from threading import Thread
import Queue
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcvfs import xbmcvfs
import image_cache_thread from utils import window, settings, language as lang, kodiSQL, tryEncode, \
from utils import window, settings, language as lang, kodiSQL tryDecode, IfExists, ThreadMethods, ThreadMethodsAdditionalStop
from utils import tryEncode, tryDecode, IfExists
# Disable annoying requests warnings # Disable annoying requests warnings
import requests.packages.urllib3 import requests.packages.urllib3
@ -27,51 +26,18 @@ log = logging.getLogger("PLEX."+__name__)
############################################################################### ###############################################################################
class Artwork(): def setKodiWebServerDetails():
xbmc_host = 'localhost' """
Get the Kodi webserver details - used to set the texture cache
"""
xbmc_port = None xbmc_port = None
xbmc_username = None xbmc_username = None
xbmc_password = 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 = { web_query = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.GetSettingValue", "method": "Settings.GetSettingValue",
"params": { "params": {
"setting": "services.webserver" "setting": "services.webserver"
} }
} }
@ -81,108 +47,189 @@ class Artwork():
xbmc_webserver_enabled = result['result']['value'] xbmc_webserver_enabled = result['result']['value']
except (KeyError, TypeError): except (KeyError, TypeError):
xbmc_webserver_enabled = False xbmc_webserver_enabled = False
if not xbmc_webserver_enabled: if not xbmc_webserver_enabled:
# Enable the webserver, it is disabled # Enable the webserver, it is disabled
web_port = { web_port = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.SetSettingValue", "method": "Settings.SetSettingValue",
"params": { "params": {
"setting": "services.webserverport", "setting": "services.webserverport",
"value": 8080 "value": 8080
} }
} }
result = xbmc.executeJSONRPC(json.dumps(web_port)) result = xbmc.executeJSONRPC(json.dumps(web_port))
self.xbmc_port = 8080 xbmc_port = 8080
web_user = { web_user = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.SetSettingValue", "method": "Settings.SetSettingValue",
"params": { "params": {
"setting": "services.webserver", "setting": "services.webserver",
"value": True "value": True
} }
} }
result = xbmc.executeJSONRPC(json.dumps(web_user)) result = xbmc.executeJSONRPC(json.dumps(web_user))
self.xbmc_username = "kodi" xbmc_username = "kodi"
# Webserver already enabled # Webserver already enabled
web_port = { web_port = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.GetSettingValue", "method": "Settings.GetSettingValue",
"params": { "params": {
"setting": "services.webserverport" "setting": "services.webserverport"
} }
} }
result = xbmc.executeJSONRPC(json.dumps(web_port)) result = xbmc.executeJSONRPC(json.dumps(web_port))
result = json.loads(result) result = json.loads(result)
try: try:
self.xbmc_port = result['result']['value'] xbmc_port = result['result']['value']
except (TypeError, KeyError): except (TypeError, KeyError):
pass pass
web_user = { web_user = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.GetSettingValue", "method": "Settings.GetSettingValue",
"params": { "params": {
"setting": "services.webserverusername" "setting": "services.webserverusername"
} }
} }
result = xbmc.executeJSONRPC(json.dumps(web_user)) result = xbmc.executeJSONRPC(json.dumps(web_user))
result = json.loads(result) result = json.loads(result)
try: try:
self.xbmc_username = result['result']['value'] xbmc_username = result['result']['value']
except TypeError: except TypeError:
pass pass
web_pass = { web_pass = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "Settings.GetSettingValue", "method": "Settings.GetSettingValue",
"params": { "params": {
"setting": "services.webserverpassword" "setting": "services.webserverpassword"
} }
} }
result = xbmc.executeJSONRPC(json.dumps(web_pass)) result = xbmc.executeJSONRPC(json.dumps(web_pass))
result = json.loads(result) result = json.loads(result)
try: try:
self.xbmc_password = result['result']['value'] xbmc_password = result['result']['value']
except TypeError: except TypeError:
pass 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): def fullTextureCacheSync(self):
# This method will sync all Kodi artwork to textures13.db """
# and cache them locally. This takes diskspace! This method will sync all Kodi artwork to textures13.db
import xbmcaddon and cache them locally. This takes diskspace!
string = xbmcaddon.Addon().getLocalizedString """
if not xbmcgui.Dialog().yesno( if not xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39250)): "Image Texture Cache", lang(39250)):
return return
log.info("Doing Image Cache Sync") log.info("Doing Image Cache Sync")
pdialog = xbmcgui.DialogProgress()
pdialog.create("PlexKodiConnect", "Image Cache Sync")
# ask to rest all existing or not # ask to rest all existing or not
if xbmcgui.Dialog().yesno( if xbmcgui.Dialog().yesno(
"Image Texture Cache", string(39251), ""): "Image Texture Cache", lang(39251), ""):
log.info("Resetting all cache data first") log.info("Resetting all cache data first")
# Remove all existing textures first # Remove all existing textures first
path = tryDecode(xbmc.translatePath("special://thumbnails/")) path = tryDecode(xbmc.translatePath("special://thumbnails/"))
@ -210,105 +257,35 @@ class Artwork():
if tableName != "version": if tableName != "version":
cursor.execute("DELETE FROM " + tableName) cursor.execute("DELETE FROM " + tableName)
connection.commit() connection.commit()
cursor.close() connection.close()
# Cache all entries in video DB # Cache all entries in video DB
connection = kodiSQL('video') connection = kodiSQL('video')
cursor = connection.cursor() 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() result = cursor.fetchall()
total = len(result) total = len(result)
log.info("Image cache sync about to process %s images" % total) log.info("Image cache sync about to process %s video images" % total)
cursor.close() connection.close()
count = 0
for url in result: 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]) self.cacheTexture(url[0])
count += 1
# Cache all entries in music DB # Cache all entries in music DB
connection = kodiSQL('music') connection = kodiSQL('music')
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT url FROM art") cursor.execute("SELECT url FROM art")
result = cursor.fetchall() result = cursor.fetchall()
total = len(result) total = len(result)
log.info("Image cache sync about to process %s images" % total) log.info("Image cache sync about to process %s music images" % total)
cursor.close() connection.close()
count = 0
for url in result: 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]) 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): def cacheTexture(self, url):
# Cache a single image url to the texture cache # Cache a single image url to the texture cache
if url and self.enableTextureCache: if url and self.enableTextureCache:
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): def addArtwork(self, artwork, kodiId, mediaType, cursor):
# Kodi conversion table # Kodi conversion table
@ -328,7 +305,8 @@ class Artwork():
for art in artwork: for art in artwork:
if art == "Backdrop": if art == "Backdrop":
# Backdrop entry is a list # 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] backdrops = artwork[art]
backdropsNumber = len(backdrops) backdropsNumber = len(backdrops)
@ -390,12 +368,11 @@ class Artwork():
cursor=cursor) cursor=cursor)
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor): def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
if not imageUrl:
# Possible that the imageurl is an empty string # Possible that the imageurl is an empty string
if imageUrl: return
cacheimage = False
query = ' '.join(( query = ' '.join((
"SELECT url", "SELECT url",
"FROM art", "FROM art",
"WHERE media_id = ?", "WHERE media_id = ?",
@ -403,37 +380,32 @@ class Artwork():
"AND type = ?" "AND type = ?"
)) ))
cursor.execute(query, (kodiId, mediaType, imageType,)) cursor.execute(query, (kodiId, mediaType, imageType,))
try: # Update the artwork try:
# Update the artwork
url = cursor.fetchone()[0] url = cursor.fetchone()[0]
except TypeError:
except TypeError: # Add the artwork # Add the artwork
cacheimage = True log.debug("Adding Art Link for kodiId: %s (%s)"
log.debug("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl)) % (kodiId, imageUrl))
query = ( query = (
''' '''
INSERT INTO art(media_id, media_type, type, url) INSERT INTO art(media_id, media_type, type, url)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
''' '''
) )
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl)) cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
else:
else: # Only cache artwork if it changed if url == imageUrl:
if url != imageUrl: # Only cache artwork if it changed
cacheimage = True return
# Only for the main backdrop, poster # Only for the main backdrop, poster
if (window('plex_initialScan') != "true" and if (window('plex_initialScan') != "true" and
imageType in ("fanart", "poster")): imageType in ("fanart", "poster")):
# Delete current entry before updating with the new one # Delete current entry before updating with the new one
self.deleteCachedArtwork(url) self.deleteCachedArtwork(url)
log.debug("Updating Art url for %s kodiId %s %s -> (%s)"
log.info("Updating Art url for %s kodiId: %s (%s) -> (%s)"
% (imageType, kodiId, url, imageUrl)) % (imageType, kodiId, url, imageUrl))
query = ' '.join(( query = ' '.join((
"UPDATE art", "UPDATE art",
"SET url = ?", "SET url = ?",
"WHERE media_id = ?", "WHERE media_id = ?",
@ -443,14 +415,11 @@ class Artwork():
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType)) cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
# Cache fanart and poster in Kodi texture cache # Cache fanart and poster in Kodi texture cache
if cacheimage:
self.cacheTexture(imageUrl) self.cacheTexture(imageUrl)
def deleteArtwork(self, kodiId, mediaType, cursor): def deleteArtwork(self, kodiId, mediaType, cursor):
query = ' '.join(( query = ' '.join((
"SELECT url",
"SELECT url, type",
"FROM art", "FROM art",
"WHERE media_id = ?", "WHERE media_id = ?",
"AND media_type = ?" "AND media_type = ?"
@ -458,159 +427,29 @@ class Artwork():
cursor.execute(query, (kodiId, mediaType,)) cursor.execute(query, (kodiId, mediaType,))
rows = cursor.fetchall() rows = cursor.fetchall()
for row in rows: for row in rows:
self.deleteCachedArtwork(row[0])
url = row[0]
imageType = row[1]
if imageType in ("poster", "fanart"):
self.deleteCachedArtwork(url)
def deleteCachedArtwork(self, url): def deleteCachedArtwork(self, url):
# Only necessary to remove and apply a new backdrop or poster # Only necessary to remove and apply a new backdrop or poster
connection = kodiSQL('texture') connection = kodiSQL('texture')
cursor = connection.cursor() cursor = connection.cursor()
try: try:
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,)) cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
(url,))
cachedurl = cursor.fetchone()[0] cachedurl = cursor.fetchone()[0]
except TypeError: except TypeError:
log.info("Could not find cached url.") log.info("Could not find cached url.")
else:
except OperationalError: # Delete thumbnail as well as the entry
log.info("Database is locked. Skip deletion process.")
else: # Delete thumbnail as well as the entry
thumbnails = tryDecode( thumbnails = tryDecode(
xbmc.translatePath("special://thumbnails/%s" % cachedurl)) xbmc.translatePath("special://thumbnails/%s" % cachedurl))
log.info("Deleting cached thumbnail: %s" % thumbnails) log.debug("Deleting cached thumbnail: %s" % thumbnails)
xbmcvfs.delete(thumbnails)
try: try:
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,)) cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
connection.commit() connection.commit()
except OperationalError:
log.debug("Issue deleting url from cache. Skipping.")
finally: finally:
cursor.close() connection.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

View file

@ -373,3 +373,30 @@ class Embydb_Functions():
query = "DELETE FROM emby WHERE emby_id LIKE ?" query = "DELETE FROM emby WHERE emby_id LIKE ?"
self.embycursor.execute(query, (plexid+"%",)) 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("Add user to session", "plugin://plugin.video.plexkodiconnect/?mode=adduser")
addDirectoryItem(lang(39203), "plugin://plugin.video.plexkodiconnect/?mode=refreshplaylist") addDirectoryItem(lang(39203), "plugin://plugin.video.plexkodiconnect/?mode=refreshplaylist")
addDirectoryItem(lang(39204), "plugin://plugin.video.plexkodiconnect/?mode=manualsync") addDirectoryItem(lang(39204), "plugin://plugin.video.plexkodiconnect/?mode=manualsync")
xbmcplugin.endOfDirectory(int(sys.argv[1])) xbmcplugin.endOfDirectory(int(sys.argv[1]))
@ -1363,7 +1362,7 @@ def enterPMS():
settings('plex_machineIdentifier', '') settings('plex_machineIdentifier', '')
else: else:
settings('plex_machineIdentifier', machineIdentifier) 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)) % (https, ip, port, machineIdentifier))
settings('https', value=https) settings('https', value=https)
settings('ipaddress', value=ip) settings('ipaddress', value=ip)

View file

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

View file

@ -417,6 +417,15 @@ class InitialSetup():
if settings('InstallQuestionsAnswered') == 'true': if settings('InstallQuestionsAnswered') == 'true':
return 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 # Additional settings where the user needs to choose
# Direct paths (\\NAS\mymovie.mkv) or addon (http)? # Direct paths (\\NAS\mymovie.mkv) or addon (http)?
goToSettings = False goToSettings = False
@ -463,13 +472,6 @@ class InitialSetup():
log.debug("User opted to use FanArtTV") log.debug("User opted to use FanArtTV")
settings('FanartTV', value="true") 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 # Make sure that we only ask these questions upon first installation
settings('InstallQuestionsAnswered', value='true') settings('InstallQuestionsAnswered', value='true')

View file

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

View file

@ -508,6 +508,48 @@ class Kodidb_Functions():
self.artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", self.cursor) 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): def addGenres(self, kodiid, genres, mediatype):
@ -1180,15 +1222,9 @@ class Kodidb_Functions():
)) ))
self.cursor.execute(query, (kodiid, mediatype, tag_id,)) 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: for setname in collections:
setid = self.createBoxset(setname) setid = self.createBoxset(setname)
# Process artwork
if settings('setFanartTV') == 'true':
self.artwork.addArtwork(API.getSetArtwork(),
setid,
"set",
kodicursor)
self.assignBoxset(setid, movieid) self.assignBoxset(setid, movieid)
def createBoxset(self, boxsetname): 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 # Save currentFile for cleanup later and to be able to access refs
window('plex_lastPlayedFiled', value=currentFile) 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) window("emby_%s.itemid" % tryEncode(currentFile), value=plexid)
log.info('Finish playback startup') log.info('Finish playback startup')

View file

@ -5,6 +5,7 @@
import logging import logging
from threading import Thread, Lock from threading import Thread, Lock
import Queue import Queue
from random import shuffle
import xbmc import xbmc
import xbmcgui import xbmcgui
@ -62,7 +63,7 @@ class ThreadedGetMetadata(Thread):
try: try:
self.queue.get(block=False) self.queue.get(block=False)
except Queue.Empty: except Queue.Empty:
xbmc.sleep(50) xbmc.sleep(10)
continue continue
else: else:
self.queue.task_done() self.queue.task_done()
@ -73,7 +74,7 @@ class ThreadedGetMetadata(Thread):
try: try:
self.out_queue.get(block=False) self.out_queue.get(block=False)
except Queue.Empty: except Queue.Empty:
xbmc.sleep(50) xbmc.sleep(10)
continue continue
else: else:
self.out_queue.task_done() self.out_queue.task_done()
@ -93,7 +94,7 @@ class ThreadedGetMetadata(Thread):
updateItem = queue.get(block=False) updateItem = queue.get(block=False)
# Empty queue # Empty queue
except Queue.Empty: except Queue.Empty:
xbmc.sleep(100) xbmc.sleep(10)
continue continue
# Download Metadata # Download Metadata
plexXML = PF.GetPlexMetadata(updateItem['itemId']) plexXML = PF.GetPlexMetadata(updateItem['itemId'])
@ -155,7 +156,7 @@ class ThreadedProcessMetadata(Thread):
try: try:
self.queue.get(block=False) self.queue.get(block=False)
except Queue.Empty: except Queue.Empty:
xbmc.sleep(100) xbmc.sleep(10)
continue continue
else: else:
self.queue.task_done() self.queue.task_done()
@ -175,7 +176,7 @@ class ThreadedProcessMetadata(Thread):
try: try:
updateItem = queue.get(block=False) updateItem = queue.get(block=False)
except Queue.Empty: except Queue.Empty:
xbmc.sleep(50) xbmc.sleep(10)
continue continue
# Do the work # Do the work
plexitem = updateItem['XML'] plexitem = updateItem['XML']
@ -250,11 +251,106 @@ class ThreadedShowSyncInfo(Thread):
processMetadataProgress, processMetadataProgress,
viewName)) viewName))
# Sleep for x milliseconds # Sleep for x milliseconds
xbmc.sleep(500) xbmc.sleep(200)
dialog.close() dialog.close()
log.debug('Dialog Infobox thread terminated') 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') @ThreadMethodsAdditionalSuspend('suspend_LibraryThread')
@ThreadMethodsAdditionalStop('plex_shouldStop') @ThreadMethodsAdditionalStop('plex_shouldStop')
@ThreadMethods @ThreadMethods
@ -275,6 +371,9 @@ class LibrarySync(Thread):
self.queue = queue self.queue = queue
self.itemsToProcess = [] self.itemsToProcess = []
self.sessionKeys = [] 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? # How long should we wait at least to process new/changed PMS items?
self.saftyMargin = int(settings('saftyMargin')) self.saftyMargin = int(settings('saftyMargin'))
@ -285,6 +384,7 @@ class LibrarySync(Thread):
self.vnodes = videonodes.VideoNodes() self.vnodes = videonodes.VideoNodes()
self.dialog = xbmcgui.Dialog() self.dialog = xbmcgui.Dialog()
self.syncThreadNumber = int(settings('syncThreadNumber'))
self.installSyncDone = settings('SyncInstallRunDone') == 'true' self.installSyncDone = settings('SyncInstallRunDone') == 'true'
self.showDbSync = settings('dbSyncIndicator') == 'true' self.showDbSync = settings('dbSyncIndicator') == 'true'
self.enableMusic = settings('enableMusic') == "true" self.enableMusic = settings('enableMusic') == "true"
@ -292,9 +392,6 @@ class LibrarySync(Thread):
'enableBackgroundSync') == "true" 'enableBackgroundSync') == "true"
self.limitindex = int(settings('limitindex')) self.limitindex = int(settings('limitindex'))
if settings('plex_pathverified') == 'true':
window('plex_pathverified', value='true')
# Just in case a time sync goes wrong # Just in case a time sync goes wrong
self.timeoffset = int(settings('kodiplextimeoffset')) self.timeoffset = int(settings('kodiplextimeoffset'))
window('kodiplextimeoffset', value=str(self.timeoffset)) window('kodiplextimeoffset', value=str(self.timeoffset))
@ -760,6 +857,7 @@ class LibrarySync(Thread):
'viewName': xxx, 'viewName': xxx,
'viewId': xxx, 'viewId': xxx,
'title': xxx 'title': xxx
'mediaType': xxx, e.g. 'movie', 'episode'
self.allPlexElementsId APPENDED(!!) dict self.allPlexElementsId APPENDED(!!) dict
= {itemid: checksum} = {itemid: checksum}
@ -779,12 +877,15 @@ class LibrarySync(Thread):
# Only update if movie is not in Kodi or checksum is # Only update if movie is not in Kodi or checksum is
# different # different
if kodi_checksum != plex_checksum: if kodi_checksum != plex_checksum:
self.updatelist.append({'itemId': itemId, self.updatelist.append({
'itemId': itemId,
'itemType': itemType, 'itemType': itemType,
'method': method, 'method': method,
'viewName': viewName, 'viewName': viewName,
'viewId': viewId, 'viewId': viewId,
'title': title}) 'title': title,
'mediaType': item.attrib.get('type')
})
else: else:
# Initial or repair sync: get all Plex movies # Initial or repair sync: get all Plex movies
for item in xml: for item in xml:
@ -796,12 +897,15 @@ class LibrarySync(Thread):
plex_checksum = ("K%s%s" plex_checksum = ("K%s%s"
% (itemId, item.attrib.get('updatedAt', ''))) % (itemId, item.attrib.get('updatedAt', '')))
self.allPlexElementsId[itemId] = plex_checksum self.allPlexElementsId[itemId] = plex_checksum
self.updatelist.append({'itemId': itemId, self.updatelist.append({
'itemId': itemId,
'itemType': itemType, 'itemType': itemType,
'method': method, 'method': method,
'viewName': viewName, 'viewName': viewName,
'viewId': viewId, 'viewId': viewId,
'title': title}) 'title': title,
'mediaType': item.attrib.get('type')
})
def GetAndProcessXMLs(self, itemType, showProgress=True): def GetAndProcessXMLs(self, itemType, showProgress=True):
""" """
@ -836,8 +940,9 @@ class LibrarySync(Thread):
# Populate queue: GetMetadata # Populate queue: GetMetadata
for updateItem in self.updatelist: for updateItem in self.updatelist:
getMetadataQueue.put(updateItem) getMetadataQueue.put(updateItem)
# Spawn GetMetadata thread for downloading # Spawn GetMetadata threads for downloading
threads = [] threads = []
for i in range(min(self.syncThreadNumber, itemNumber)):
thread = ThreadedGetMetadata(getMetadataQueue, thread = ThreadedGetMetadata(getMetadataQueue,
processMetadataQueue, processMetadataQueue,
getMetadataLock, getMetadataLock,
@ -887,6 +992,18 @@ class LibrarySync(Thread):
except: except:
pass pass
log.info("Sync threads finished") 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 = [] self.updatelist = []
@LogTime @LogTime
@ -1284,7 +1401,17 @@ class LibrarySync(Thread):
if item['state'] == 9: if item['state'] == 9:
successful = self.process_deleteditems(item) successful = self.process_deleteditems(item)
else: 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: if successful is True:
deleteListe.append(i) deleteListe.append(i)
else: else:
@ -1310,13 +1437,16 @@ class LibrarySync(Thread):
def process_newitems(self, item): def process_newitems(self, item):
ratingKey = item['ratingKey'] ratingKey = item['ratingKey']
xml = PF.GetPlexMetadata(ratingKey) xml = PF.GetPlexMetadata(ratingKey)
if xml in (None, 401): try:
log.error('Could not download data for %s, skipping' % ratingKey) mediatype = xml[0].attrib['type']
return False 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) log.debug("Processing new/updated PMS item: %s" % ratingKey)
viewtag = xml.attrib.get('librarySectionTitle') viewtag = xml.attrib.get('librarySectionTitle')
viewid = xml.attrib.get('librarySectionID') viewid = xml.attrib.get('librarySectionID')
mediatype = xml[0].attrib.get('type') # Attach mediatype for later
item['mediatype'] = mediatype
if mediatype == 'movie': if mediatype == 'movie':
self.videoLibUpdate = True self.videoLibUpdate = True
with itemtypes.Movies() as movie: with itemtypes.Movies() as movie:
@ -1335,7 +1465,7 @@ class LibrarySync(Thread):
music.add_updateSong(xml[0], music.add_updateSong(xml[0],
viewtag=viewtag, viewtag=viewtag,
viewid=viewid) viewid=viewid)
return True return True, item
def process_deleteditems(self, item): def process_deleteditems(self, item):
if item.get('type') == 1: if item.get('type') == 1:
@ -1362,10 +1492,19 @@ class LibrarySync(Thread):
"processing queue" for later "processing queue" for later
""" """
for item in data: for item in data:
typus = item.get('type') if 'tv.plex' in item.get('identifier', ''):
state = item.get('state') # 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): 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? # Have we already added this element?
for existingItem in self.itemsToProcess: for existingItem in self.itemsToProcess:
if existingItem['ratingKey'] == itemId: if existingItem['ratingKey'] == itemId:
@ -1400,7 +1539,7 @@ class LibrarySync(Thread):
sessionKey = item.get('sessionKey') sessionKey = item.get('sessionKey')
# Do we already have a sessionKey stored? # Do we already have a sessionKey stored?
if sessionKey not in self.sessionKeys: 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 # Not our PMS, we are not authorized to get the
# sessions # sessions
# On the bright side, it must be us playing :-) # On the bright side, it must be us playing :-)
@ -1419,7 +1558,10 @@ class LibrarySync(Thread):
continue continue
currSess = self.sessionKeys[sessionKey] 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 # Identify the user - same one as signed on with PKC? Skip
# update if neither session's username nor userid match # update if neither session's username nor userid match
# (Owner sometime's returns id '1', not always) # (Owner sometime's returns id '1', not always)
@ -1484,6 +1626,30 @@ class LibrarySync(Thread):
with itemFkt() as Fkt: with itemFkt() as Fkt:
Fkt.updatePlaystate(item) 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): def run(self):
try: try:
self.run_internal() self.run_internal()
@ -1527,6 +1693,9 @@ class LibrarySync(Thread):
if self.enableMusic: if self.enableMusic:
advancedSettingsXML() advancedSettingsXML()
if settings('FanartTV') == 'true':
self.fanartthread.start()
while not threadStopped(): while not threadStopped():
# In the event the server goes offline # In the event the server goes offline
@ -1640,6 +1809,21 @@ class LibrarySync(Thread):
forced=True, forced=True,
icon="error") icon="error")
window('plex_dbScan', clear=True) 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: else:
now = getUnixTimestamp() now = getUnixTimestamp()
if (now - lastSync > fullSyncInterval and if (now - lastSync > fullSyncInterval and
@ -1666,9 +1850,7 @@ class LibrarySync(Thread):
# Only do this once every 10 seconds # Only do this once every 10 seconds
if now - lastProcessing > 10: if now - lastProcessing > 10:
lastProcessing = now lastProcessing = now
window('plex_dbScan', value="true")
processItems() processItems()
window('plex_dbScan', clear=True)
# See if there is a PMS message we need to handle # See if there is a PMS message we need to handle
try: try:
message = queue.get(block=False) message = queue.get(block=False)
@ -1677,10 +1859,8 @@ class LibrarySync(Thread):
continue continue
# Got a message from PMS; process it # Got a message from PMS; process it
else: else:
window('plex_dbScan', value="true")
processMessage(message) processMessage(message)
queue.task_done() queue.task_done()
window('plex_dbScan', clear=True)
# NO sleep! # NO sleep!
continue continue
else: else:

View file

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

View file

@ -305,7 +305,7 @@ class Player(xbmc.Player):
self.stopAll() self.stopAll()
window('Plex_currently_playing_itemid', clear=True) window('plex_currently_playing_itemid', clear=True)
window('plex_customplaylist', clear=True) window('plex_customplaylist', clear=True)
window('plex_customplaylist.seektime', clear=True) window('plex_customplaylist.seektime', 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: while not keyid:
if count > 300: if count > 300:
break break
keyid = window('Plex_currently_playing_itemid') keyid = window('plex_currently_playing_itemid')
xbmc.sleep(100) xbmc.sleep(100)
count += 1 count += 1
if keyid: if keyid:
@ -149,7 +149,7 @@ class SubscriptionManager:
self.cleanup() self.cleanup()
# Don't tell anyone if we don't know a Plex ID and are still playing # 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 # (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): and not self.lastplayers):
return True return True
players = self.js.getPlayers() players = self.js.getPlayers()

View file

@ -174,26 +174,9 @@ class UserClient(threading.Thread):
window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_machineIdentifier', value=self.machineIdentifier)
window('plex_servername', value=self.servername) window('plex_servername', value=self.servername)
window('plex_authenticated', value='true') window('plex_authenticated', value='true')
window('plex_serverowned', value=settings('plex_serverowned'))
window('useDirectPaths', value='true' window('useDirectPaths', value='true'
if settings('useDirectPaths') == "1" else 'false') 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 # Start DownloadUtils session
doUtils.startSession(reset=True) doUtils.startSession(reset=True)

View file

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

View file

@ -19,29 +19,35 @@
<setting id="accessToken" type="text" visible="false" default="" /> <setting id="accessToken" type="text" visible="false" default="" />
</category> </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="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 type="sep" text=""/>
<setting id="myplexlogin" label="39025" type="bool" default="true" /> <!-- Log into plex.tv on startup --> <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 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="plexhome" label="Plex home in use" type="bool" default="" visible="false" />
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" /> <setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
<setting id="plexHomeSize" type="number" default="1" 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>
<category label="30506"><!-- Sync Options --> <category label="30506"><!-- Sync Options -->
<setting type="lsep" label="30537" /><!-- Restart if you make changes --> <setting type="lsep" label="30537" /><!-- Restart if you make changes -->
<setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/> <setting id="syncEmptyShows" type="bool" label="30508" default="false" visible="false"/>
<setting id="dbSyncIndicator" label="30507" type="bool" default="true" /> <setting id="dbSyncIndicator" label="30507" type="bool" default="true" /><!-- show syncing progress -->
<setting type="sep" /><!-- 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="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 type="lsep" label="39052" /><!-- Background Sync -->
<setting id="enableBackgroundSync" type="bool" label="39026" default="true" visible="true"/> <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)"/> <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 id="dbSyncScreensaver" type="bool" label="39062" default="false" /><!--Sync when screensaver is deactivated-->
<setting type="lsep" label="30538" /><!-- Complete Re-Sync necessary --> <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="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 --> <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" /> <setting id="bestTrailer" type="bool" label="30542" default="true" />
</category> </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"> <category label="30235" visible="false">
<setting id="enableCoverArt" type="bool" label="30157" default="true" 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" /> <setting id="newmusictime" type="number" label="30533" visible="false" default="2" option="int" subsetting="true" />
</category> </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 --> <category label="39073"><!-- Appearance Tweaks -->
<setting id="connectMsg" type="bool" label="30249" default="true" /> <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="logLevel" type="enum" label="30004" values="Disabled|Info|Debug" default="1" />
<setting id="startupDelay" type="number" label="30529" default="0" option="int" /> <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="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="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 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 --> <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("%s Version: %s" % (addonName, self.clientInfo.getVersion()))
log.warn("Using plugin paths: %s" log.warn("Using plugin paths: %s"
% (settings('useDirectPaths') != "true")) % (settings('useDirectPaths') != "true"))
log.warn("Using a low powered device: %s"
% settings('low_powered_device'))
log.warn("Log Level: %s" % logLevel) log.warn("Log Level: %s" % logLevel)
# Reset window props for profile switch # Reset window props for profile switch
@ -97,9 +99,6 @@ class Service():
"plex_runLibScan", "plex_username", "pms_token", "plex_token", "plex_runLibScan", "plex_username", "pms_token", "plex_token",
"pms_server", "plex_machineIdentifier", "plex_servername", "pms_server", "plex_machineIdentifier", "plex_servername",
"plex_authenticated", "PlexUserImage", "useDirectPaths", "plex_authenticated", "PlexUserImage", "useDirectPaths",
"replaceSMB", "remapSMB", "remapSMBmovieOrg", "remapSMBtvOrg",
"remapSMBmusicOrg", "remapSMBmovieNew", "remapSMBtvNew",
"remapSMBmusicNew", "remapSMBphotoOrg", "remapSMBphotoNew",
"suspend_LibraryThread", "plex_terminateNow", "suspend_LibraryThread", "plex_terminateNow",
"kodiplextimeoffset", "countError", "countUnauthorized" "kodiplextimeoffset", "countError", "countUnauthorized"
] ]
@ -129,7 +128,7 @@ class Service():
initialsetup.InitialSetup().setup() initialsetup.InitialSetup().setup()
# Queue for background sync # Queue for background sync
queue = Queue.Queue(maxsize=200) queue = Queue.Queue()
connectMsg = True if settings('connectMsg') == 'true' else False connectMsg = True if settings('connectMsg') == 'true' else False