Merge pull request #1259 from croneter/beta-version

Bump master
This commit is contained in:
croneter 2021-01-02 13:18:13 +01:00 committed by GitHub
commit 5eb1c2aacd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 532 additions and 342 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.12.3" provider-name="croneter">
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.12.6" provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" />
@ -83,7 +83,24 @@
<summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary>
<description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description>
<disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer>
<news>version 2.12.3:
<news>version 2.12.6:
- Fix rare KeyError when using PKC widgets
- Fix suspension of artwork caching and PKC becoming unresponsive
- Update translations
- Versions 2.12.4 and 2.12.5 for everyone
version 2.12.5 (beta only):
- Greatly improve matching logic for The Movie Database if Plex does not provide an appropriate id
- Fix high transcoding resolutions not being available for Win10
- Fix rare playback progress report failing and KeyError: u'containerKey'
- Fix rare KeyError: None when trying to sync playlists
- Fix TypeError when canceling Plex sync section dialog
version 2.12.4 (beta only):
- Hopefully fix freeze during sync: Don't assign multiple sets/collections for a specific movie
- Support metadata provider ids (e.g. for IMDB) for the new Plex Movie Agent
version 2.12.3:
- Fix playback failing due to caching of subtitles with non-ascii chars
- Fix ValueError: invalid literal for int() with base 10 during show sync
- Fix UnboundLocalError when certain Plex sections are deleted or being un-synched

View File

@ -1,3 +1,20 @@
version 2.12.6:
- Fix rare KeyError when using PKC widgets
- Fix suspension of artwork caching and PKC becoming unresponsive
- Update translations
- Versions 2.12.4 and 2.12.5 for everyone
version 2.12.5 (beta only):
- Greatly improve matching logic for The Movie Database if Plex does not provide an appropriate id
- Fix high transcoding resolutions not being available for Win10
- Fix rare playback progress report failing and KeyError: u'containerKey'
- Fix rare KeyError: None when trying to sync playlists
- Fix TypeError when canceling Plex sync section dialog
version 2.12.4 (beta only):
- Hopefully fix freeze during sync: Don't assign multiple sets/collections for a specific movie
- Support metadata provider ids (e.g. for IMDB) for the new Plex Movie Agent
version 2.12.3:
- Fix playback failing due to caching of subtitles with non-ascii chars
- Fix ValueError: invalid literal for int() with base 10 during show sync

View File

@ -950,6 +950,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Nahrazovat speciální znaky v cestě (např. z mezery na %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -954,6 +954,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Escape special characters in path (e.g. space to %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -966,6 +966,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Sonderzeichen im Pfad escapen (z.B. Leerzeichen zu %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Sichere Zeichen für http(s), dav(s) und (s)ftp urls"
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -1,6 +1,6 @@
# XBMC Media Center language file
# Translators:
# Croneter None <croneter@gmail.com>, 2019
# Croneter None <croneter@gmail.com>, 2020
#
msgid ""
msgstr ""
@ -8,7 +8,7 @@ msgstr ""
"Report-Msgid-Bugs-To: croneter@gmail.com\n"
"POT-Creation-Date: 2017-04-15 13:13+0000\n"
"PO-Revision-Date: 2017-04-30 08:30+0000\n"
"Last-Translator: Croneter None <croneter@gmail.com>, 2019\n"
"Last-Translator: Croneter None <croneter@gmail.com>, 2020\n"
"Language-Team: Spanish (Argentina) (https://www.transifex.com/croneter/teams/73837/es_AR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -935,7 +935,7 @@ msgid ""
"Kodi cannot locate the file %s. Please verify your PKC settings. Stop "
"syncing?"
msgstr ""
"Kodi no puede localizer el archive %s. Por favoer verificar sus ajustes de "
"Kodi no puede localizer el archivo %s. Por favor verificar sus ajustes de "
"PKC. ¿Detener la sincronización?"
# Pop-up on initial sync
@ -964,7 +964,12 @@ msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Escapar caracteres especiales en la ruta (i.e. espacio a %20)"
msgstr "Escapar caracteres especiales en la ruta (p. ej. espacio a %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Caracteres seguros para urls http(s), dav(s) y (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
@ -1430,7 +1435,7 @@ msgid ""
"The current Kodi version is not supported by PKC. Please consult the Plex "
"forum."
msgstr ""
"La version actual de Kodi no está soportada por PKC. Por favor consultar el"
"La versión actual de Kodi no está soportada por PKC. Por favor consultar el"
" fórum de Plex."
msgctxt "#39405"
@ -1474,7 +1479,7 @@ msgstr "Sagas"
msgctxt "#39502"
msgid "PKC On Deck (faster)"
msgstr "Tablero de PKC (mas rapido)"
msgstr "On Deck de PKC (más rápido)"
msgctxt "#39600"
msgid ""
@ -1616,8 +1621,8 @@ msgid ""
"Do you want to replace your custom user ratings with an indicator of how "
"many versions of a media item you posses?"
msgstr ""
"¿Quiere reemplazar su valoración personalizada con cuántas versione posee de"
" un elemento de medios?"
"¿Quiere reemplazar su valoración personalizada por cuántas versiones posee "
"de un elemento de medios?"
# In PKC Settings under Sync
msgctxt "#39719"

View File

@ -2,6 +2,7 @@
# Translators:
# Dani <danichispa@gmail.com>, 2019
# Bartolome Soriano <bsoriano@gmail.com>, 2019
# Croneter None <croneter@gmail.com>, 2020
#
msgid ""
msgstr ""
@ -9,7 +10,7 @@ msgstr ""
"Report-Msgid-Bugs-To: croneter@gmail.com\n"
"POT-Creation-Date: 2017-04-15 13:13+0000\n"
"PO-Revision-Date: 2017-04-30 08:30+0000\n"
"Last-Translator: Bartolome Soriano <bsoriano@gmail.com>, 2019\n"
"Last-Translator: Croneter None <croneter@gmail.com>, 2020\n"
"Language-Team: Spanish (Spain) (https://www.transifex.com/croneter/teams/73837/es_ES/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"

View File

@ -1,6 +1,6 @@
# XBMC Media Center language file
# Translators:
# Croneter None <croneter@gmail.com>, 2019
# Croneter None <croneter@gmail.com>, 2020
#
msgid ""
msgstr ""
@ -8,7 +8,7 @@ msgstr ""
"Report-Msgid-Bugs-To: croneter@gmail.com\n"
"POT-Creation-Date: 2017-04-15 13:13+0000\n"
"PO-Revision-Date: 2017-04-30 08:30+0000\n"
"Last-Translator: Croneter None <croneter@gmail.com>, 2019\n"
"Last-Translator: Croneter None <croneter@gmail.com>, 2020\n"
"Language-Team: Spanish (Mexico) (https://www.transifex.com/croneter/teams/73837/es_MX/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -935,7 +935,7 @@ msgid ""
"Kodi cannot locate the file %s. Please verify your PKC settings. Stop "
"syncing?"
msgstr ""
"Kodi no puede localizer el archive %s. Por favoer verificar sus ajustes de "
"Kodi no puede localizer el archivo %s. Por favor verificar sus ajustes de "
"PKC. ¿Detener la sincronización?"
# Pop-up on initial sync
@ -964,7 +964,12 @@ msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Escapar caracteres especiales en la ruta (i.e. espacio a %20)"
msgstr "Escapar caracteres especiales en la ruta (p. ej. espacio a %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Caracteres seguros para urls http(s), dav(s) y (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
@ -1430,7 +1435,7 @@ msgid ""
"The current Kodi version is not supported by PKC. Please consult the Plex "
"forum."
msgstr ""
"La version actual de Kodi no está soportada por PKC. Por favor consultar el"
"La versión actual de Kodi no está soportada por PKC. Por favor consultar el"
" fórum de Plex."
msgctxt "#39405"
@ -1474,7 +1479,7 @@ msgstr "Sagas"
msgctxt "#39502"
msgid "PKC On Deck (faster)"
msgstr "Tablero de PKC (mas rapido)"
msgstr "On Deck de PKC (más rápido)"
msgctxt "#39600"
msgid ""
@ -1616,8 +1621,8 @@ msgid ""
"Do you want to replace your custom user ratings with an indicator of how "
"many versions of a media item you posses?"
msgstr ""
"¿Quiere reemplazar su valoración personalizada con cuántas versione posee de"
" un elemento de medios?"
"¿Quiere reemplazar su valoración personalizada por cuántas versiones posee "
"de un elemento de medios?"
# In PKC Settings under Sync
msgctxt "#39719"

View File

@ -975,6 +975,11 @@ msgstr ""
"Echapper les caractères spéciaux dans le chemin (ex: %20 au lieu des "
"espaces)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Caractères sûrs pour les urls http(s), dav(s) et (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -979,6 +979,11 @@ msgstr ""
"Echapper les caractères spéciaux dans le chemin (ex: %20 au lieu des "
"espaces)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Caractères sûrs pour les urls http(s), dav(s) et (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -968,6 +968,11 @@ msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
"A speciális karakterek feloldása az elérési útban (pl. szóköz helyett %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -967,6 +967,11 @@ msgstr ""
"Esegui l'escape dei caratteri speciali nel percorso (es. \"spazio\" "
"trasformato in \"%20\")"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -963,6 +963,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Kaita specialių simbolių kelyje (pvz., tarpas %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -946,6 +946,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"
@ -1100,7 +1105,7 @@ msgstr "Uzspiest atjaunošanu Kodi ādiņai apturot atskaņošanu"
# PKC Settings - Appearance Tweaks
msgctxt "#39066"
msgid "Recently Added: Also show already watched movies"
msgstr ""
msgstr "Nesen Pievienots: Rādīt arī jau skatītas filmas"
# PKC Settings - Connection
msgctxt "#39067"
@ -1110,7 +1115,7 @@ msgstr "Tavs pašreizējais Plex Media Serveris:"
# PKC Settings - Connection
msgctxt "#39068"
msgid "Manually enter Plex Media Server address"
msgstr ""
msgstr "Pats ievadi Plex Media Server adresi"
# PKC Settings - Connection
msgctxt "#39069"
@ -1173,22 +1178,22 @@ msgstr ""
# Button text for choosing PKC mode
msgctxt "#39081"
msgid "Add-on Paths"
msgstr ""
msgstr "Spraudņu Ceļš"
# Button text for choosing PKC mode
msgctxt "#39082"
msgid "Direct Paths"
msgstr ""
msgstr "Tiešie Ceļi"
# Dialog for manually entering PMS
msgctxt "#39083"
msgid "Enter PMS IP or URL"
msgstr ""
msgstr "Ievadi PMS IP vai URL"
# Dialog for manually entering PMS
msgctxt "#39084"
msgid "Enter PMS port"
msgstr ""
msgstr "Ievadi PMS portu"
# PKC settings - Appearance Tweaks
msgctxt "#39085"
@ -1291,23 +1296,23 @@ msgstr "Tikai trūkstošo"
# Message in the PKC settings if user has not logged in to plex.tv
msgctxt "#39226"
msgid "Not logged in to plex.tv"
msgstr ""
msgstr "Nav pieteicies plex.tv"
# Message in the PKC settings if user is logged in to plex.tv
msgctxt "#39227"
msgid "Logged in to plex.tv"
msgstr ""
msgstr "Pieteicies plex.tv"
# Message in the PKC settings to display the plex.tv username
msgctxt "#39228"
msgid "Plex admin user"
msgstr ""
msgstr "Plex admin user"
# Error message if user could not log in; the actual user name will be
# appended at the end of the string
msgctxt "#39229"
msgid "Login failed with plex.tv for user"
msgstr ""
msgstr "Lietotāja pieteikšanās plex.tv neizdevās"
# Message in the PKC settings to display the plex.tv username
msgctxt "#39230"
@ -1472,7 +1477,7 @@ msgstr ""
# Addon Disclaimer
msgctxt "#39705"
msgid "Use at your own risk"
msgstr ""
msgstr "Lieto uz savu atbildību"
# If user gets prompted to choose between several subtitles to burn in
msgctxt "#39706"
@ -1530,7 +1535,7 @@ msgstr "Sinhronizēt"
# Shown during sync process
msgctxt "#39715"
msgid "Synching playlists"
msgstr ""
msgstr "Sinhronizē spēļsarakstus"
# Error message if an xml, e.g. advancedsettings.xml cannot be parsed (xml is
# screwed up; formated the wrong way). Do NOT replace {0} and {1}!

View File

@ -957,6 +957,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Pas speciale tekens aan in pad (b.v. spatie naar %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -951,6 +951,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Unngå spesielle tegn i stier (eksempel mellomrom til %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -945,6 +945,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -948,6 +948,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -961,6 +961,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Преобразуйте специальные символы в пути. (например пробел в %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -951,6 +951,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Omkoda specialtecken i sökväg (exempelvis mellanslag som %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -957,6 +957,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr "Замінювати спеціальні символи у шляхах (наприклад, пробіл у %20)"
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "Безпечні символи для URL-адрес http(s), dav(s) та (s)ftp"
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"
@ -1528,7 +1533,7 @@ msgstr "Використовуйте на свій ризик"
# If user gets prompted to choose between several subtitles to burn in
msgctxt "#39706"
msgid "Don't burn-in any subtitle"
msgstr ""
msgstr "Не виводити жодних субтитрів"
# If user gets prompted to choose between several audio/subtitle tracks and
# language is unknown

View File

@ -918,6 +918,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -916,6 +916,11 @@ msgctxt "#39036"
msgid "Escape special characters in path (e.g. space to %20)"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr ""
# PKC Settings - Customize Paths
msgctxt "#39037"
msgid "Original Plex MOVIE path to replace:"

View File

@ -82,7 +82,7 @@ class ImageCachingThread(backgroundthread.KillableThread):
for url in self._url_generator(kind, kodi_type):
if self.should_suspend() or self.should_cancel():
return False
cache_url(url)
cache_url(url, self.should_suspend)
# Toggles Image caching completed to Yes
utils.settings('plex_status_image_caching', value=utils.lang(107))
return True
@ -95,7 +95,7 @@ class ImageCachingThread(backgroundthread.KillableThread):
break
def cache_url(url):
def cache_url(url, should_suspend=None):
url = double_urlencode(url)
sleeptime = 0
while True:
@ -113,11 +113,11 @@ def cache_url(url):
# download. All is well
break
except requests.ConnectionError:
if app.APP.stop_pkc:
# Kodi terminated
if app.APP.stop_pkc or (should_suspend and should_suspend()):
break
# Server thinks its a DOS attack, ('error 10053')
# Wait before trying again
# OR: Kodi refuses Webserver connection (no password set)
if sleeptime > 5:
LOG.error('Repeatedly got ConnectionError for url %s',
double_urldecode(url))

View File

@ -145,3 +145,27 @@ class ItemBase(object):
encountered by PKC
"""
return section_id in app.SYNC.section_ids
def update_provider_ids(self, api, kodi_id):
"""
Updates the unique metadata provider ids (such as the IMDB id). Returns
a dict of the Kodi unique ids
"""
# We might have an old provider id stored!
self.kodidb.remove_uniqueid(kodi_id, api.kodi_type)
return self.add_provider_ids(api, kodi_id)
def add_provider_ids(self, api, kodi_id):
"""
Adds the unique ids for all metadata providers to the Kodi database,
such as IMDB or The Movie Database TMDB.
Returns a dict of the Kodi ids: {<provider>: <kodi_unique_id>}
"""
kodi_unique_ids = api.guids.copy()
for provider, provider_id in api.guids.iteritems():
kodi_unique_ids[provider] = self.kodidb.add_uniqueid(
kodi_id,
api.kodi_type,
provider_id,
provider)
return kodi_unique_ids

View File

@ -56,19 +56,7 @@ class Movie(ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('imdb') is not None:
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_MOVIE,
'imdb',
api.provider('imdb'))
elif api.provider('tmdb') is not None:
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_MOVIE,
'tmdb',
api.provider('tmdb'))
else:
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_MOVIE)
uniqueid = -1
unique_id = self.update_provider_ids(api, kodi_id)
self.kodidb.modify_people(kodi_id,
v.KODI_TYPE_MOVIE,
api.people())
@ -86,18 +74,7 @@ class Movie(ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('imdb') is not None:
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_MOVIE,
api.provider('imdb'),
"imdb")
elif api.provider('tmdb') is not None:
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_MOVIE,
api.provider('tmdb'),
"tmdb")
else:
uniqueid = -1
unique_id = self.add_provider_ids(api, kodi_id)
self.kodidb.add_people(kodi_id,
v.KODI_TYPE_MOVIE,
api.people())
@ -106,6 +83,8 @@ class Movie(ItemBase):
kodi_id,
v.KODI_TYPE_MOVIE)
unique_id = self._prioritize_provider_id(unique_id)
# Update Kodi's main entry
self.kodidb.add_movie(kodi_id,
file_id,
@ -117,7 +96,7 @@ class Movie(ItemBase):
rating_id,
api.list_to_string(api.writers()),
api.year(),
uniqueid,
unique_id,
api.sorttitle(),
api.runtime(),
api.content_rating(),
@ -140,39 +119,7 @@ class Movie(ItemBase):
self.kodidb.modify_streams(file_id, api.mediastreams(), api.runtime())
self.kodidb.modify_studios(kodi_id, v.KODI_TYPE_MOVIE, api.studios())
tags = [section_name]
if api.collections():
for plex_set_id, set_name in api.collections():
set_api = None
tags.append(set_name)
# Add any sets from Plex collection tags
kodi_set_id = self.kodidb.create_collection(set_name)
self.kodidb.assign_collection(kodi_set_id, kodi_id)
if not app.SYNC.artwork:
# Rest below is to get collection artwork
continue
if children is None:
# e.g. when added via websocket
LOG.debug('Costly looking up Plex collection %s: %s',
plex_set_id, set_name)
for index, coll_plex_id in api.collections_match(section_id):
# Get Plex artwork for collections - a pain
if index == plex_set_id:
set_xml = PF.GetPlexMetadata(coll_plex_id)
try:
set_xml.attrib
except AttributeError:
LOG.error('Could not get set metadata %s',
coll_plex_id)
continue
set_api = API(set_xml[0])
break
elif plex_set_id in children:
# Provided by get_metadata thread
set_api = API(children[plex_set_id][0])
if set_api:
self.kodidb.modify_artwork(set_api.artwork(),
kodi_set_id,
v.KODI_TYPE_SET)
self._process_collections(api, tags, kodi_id, section_id, children)
self.kodidb.modify_tags(kodi_id, v.KODI_TYPE_MOVIE, tags)
# Process playstate
self.kodidb.set_resume(file_id,
@ -246,3 +193,52 @@ class Movie(ItemBase):
db_item['kodi_type'],
api.userrating())
return True
def _process_collections(self, api, tags, kodi_id, section_id, children):
for plex_set_id, set_name in api.collections():
set_api = None
tags.append(set_name)
# Add any sets from Plex collection tags
kodi_set_id = self.kodidb.create_collection(set_name)
self.kodidb.assign_collection(kodi_set_id, kodi_id)
if not app.SYNC.artwork:
# Rest below is to get collection artwork
# TODO: continue instead of break (see TODO/break below)
break
if children is None:
# e.g. when added via websocket
LOG.debug('Costly looking up Plex collection %s: %s',
plex_set_id, set_name)
for index, coll_plex_id in api.collections_match(section_id):
# Get Plex artwork for collections - a pain
if index == plex_set_id:
set_xml = PF.GetPlexMetadata(coll_plex_id)
try:
set_xml.attrib
except AttributeError:
LOG.error('Could not get set metadata %s',
coll_plex_id)
continue
set_api = API(set_xml[0])
break
elif plex_set_id in children:
# Provided by get_metadata thread
set_api = API(children[plex_set_id][0])
if set_api:
self.kodidb.modify_artwork(set_api.artwork(),
kodi_set_id,
v.KODI_TYPE_SET)
# TODO: Once Kodi (19?) supports SEVERAL sets/collections per
# movie, support that. For now, we only take the very first
# collection/set that Plex returns
break
@staticmethod
def _prioritize_provider_id(unique_ids):
"""
Prioritize which ID ends up in the SHOW table (there can only be 1)
tvdb > imdb > tmdb
"""
return unique_ids.get('imdb',
unique_ids.get('tmdb',
unique_ids.get('tvdb')))

View File

@ -194,19 +194,8 @@ class Show(TvShowMixin, ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('tvdb') is not None:
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_SHOW,
'tvdb',
api.provider('tvdb'))
elif api.provider('tmdb') is not None:
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_SHOW,
'tmdb',
api.provider('tmdb'))
else:
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
uniqueid = -1
unique_id = self._prioritize_provider_id(
self.update_provider_ids(api, kodi_id))
self.kodidb.modify_people(kodi_id,
v.KODI_TYPE_SHOW,
api.people())
@ -221,7 +210,7 @@ class Show(TvShowMixin, ItemBase):
api.premiere_date(),
api.list_to_string(api.genres()),
api.title(),
uniqueid,
unique_id,
api.content_rating(),
api.list_to_string(api.studios()),
api.sorttitle(),
@ -236,18 +225,8 @@ class Show(TvShowMixin, ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('tvdb'):
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_SHOW,
api.provider('tvdb'),
'tvdb')
if api.provider('tmdb'):
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_SHOW,
api.provider('tmdb'),
'tmdb')
else:
uniqueid = -1
unique_id = self._prioritize_provider_id(
self.add_provider_ids(api, kodi_id))
self.kodidb.add_people(kodi_id,
v.KODI_TYPE_SHOW,
api.people())
@ -263,7 +242,7 @@ class Show(TvShowMixin, ItemBase):
api.premiere_date(),
api.list_to_string(api.genres()),
api.title(),
uniqueid,
unique_id,
api.content_rating(),
api.list_to_string(api.studios()),
api.sorttitle())
@ -281,6 +260,15 @@ class Show(TvShowMixin, ItemBase):
kodi_pathid=kodi_pathid,
last_sync=self.last_sync)
@staticmethod
def _prioritize_provider_id(unique_ids):
"""
Prioritize which ID ends up in the SHOW table (there can only be 1)
tvdb > imdb > tmdb
"""
return unique_ids.get('tvdb',
unique_ids.get('imdb',
unique_ids.get('tmdb')))
class Season(TvShowMixin, ItemBase):
def add_update(self, xml, section_name=None, section_id=None,
@ -459,19 +447,8 @@ class Episode(TvShowMixin, ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('tvdb'):
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_EPISODE,
'tvdb',
api.provider('tvdb'))
elif api.provider('tmdb'):
uniqueid = self.kodidb.update_uniqueid(kodi_id,
v.KODI_TYPE_EPISODE,
'tmdb',
api.provider('tmdb'))
else:
self.kodidb.remove_uniqueid(kodi_id, v.KODI_TYPE_EPISODE)
uniqueid = -1
unique_id = self._prioritize_provider_id(
self.update_provider_ids(api, kodi_id))
self.kodidb.modify_people(kodi_id,
v.KODI_TYPE_EPISODE,
api.people())
@ -493,7 +470,7 @@ class Episode(TvShowMixin, ItemBase):
airs_before_episode,
fullpath,
kodi_pathid,
uniqueid,
unique_id,
kodi_fileid, # and NOT kodi_fileid_2
parent_id,
api.userrating(),
@ -539,18 +516,8 @@ class Episode(TvShowMixin, ItemBase):
"default",
api.rating(),
api.votecount())
if api.provider('tvdb'):
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_EPISODE,
api.provider('tvdb'),
"tvdb")
elif api.provider('tmdb'):
uniqueid = self.kodidb.add_uniqueid(kodi_id,
v.KODI_TYPE_EPISODE,
api.provider('tmdb'),
"tmdb")
else:
uniqueid = -1
unique_id = self._prioritize_provider_id(
self.add_provider_ids(api, kodi_id))
self.kodidb.add_people(kodi_id,
v.KODI_TYPE_EPISODE,
api.people())
@ -575,7 +542,7 @@ class Episode(TvShowMixin, ItemBase):
airs_before_episode,
fullpath,
kodi_pathid,
uniqueid,
unique_id,
parent_id,
api.userrating())
self.kodidb.set_resume(kodi_fileid,
@ -605,3 +572,13 @@ class Episode(TvShowMixin, ItemBase):
self.kodidb.modify_streams(kodi_fileid, # and NOT kodi_fileid_2
api.mediastreams(),
api.runtime())
@staticmethod
def _prioritize_provider_id(unique_ids):
"""
Prioritize which ID ends up in the SHOW table (there can only be 1)
tvdb > imdb > tmdb
"""
return unique_ids.get('tvdb',
unique_ids.get('imdb',
unique_ids.get('tmdb')))

View File

@ -578,7 +578,7 @@ def _choose_libraries(sections):
selectable_sections,
preselect=preselected,
useDetails=False)
if selectable_sections is None:
if selected_sections is None:
LOG.info('User chose not to select which libraries to sync')
return False
index = 0

View File

@ -265,19 +265,17 @@ def get_resolution():
"""
chosen = utils.settings('transcoderVideoQualities')
res = {
'0': '420x420',
'1': '576x320',
'2': '720x480',
'3': '1024x768',
'4': '1280x720',
'5': '1280x720',
'0': '720x480',
'1': '1024x768',
'2': '1280x720',
'3': '1280x720',
'4': '1920x1080',
'5': '1920x1080',
'6': '1920x1080',
'7': '1920x1080',
'8': '1920x1080',
'9': '1920x1080',
'10': '1920x1080',
'11': '3840x2160',
'12': '3840x2160'
'9': '3840x2160',
'10': '3840x2160'
}
return res[chosen]

View File

@ -357,6 +357,10 @@ def sync_plex_playlist(playlist=None, xml=None, plex_id=None):
if api.playlist_type() == v.PLEX_TYPE_PHOTO_PLAYLIST:
# Not supported by Kodi
return False
elif api.playlist_type() is None:
# Encountered in logs, seems to be a malformed answer
LOG.error('Playlist type is missing: %s', api.xml.attrib)
return False
name = api.title()
typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX[api.playlist_type()]
if (not app.SYNC.enable_music and typus == v.PLEX_PLAYLIST_TYPE_AUDIO):

View File

@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
from logging import getLogger
from re import sub
from ..kodi_db import KodiVideoDB, KodiMusicDB
from ..downloadutils import DownloadUtils as DU
from .. import utils, variables as v, app
from . import fanart_lookup
LOG = getLogger('PLEX.api')
@ -186,9 +187,9 @@ class Artwork(object):
# Always seek collection's ids since not provided by PMS
if collection is False:
if media_type == v.PLEX_TYPE_MOVIE:
media_id = self.provider('imdb')
media_id = self.guids.get('imdb')
elif media_type == v.PLEX_TYPE_SHOW:
media_id = self.provider('tvdb')
media_id = self.guids.get('tvdb')
if media_id is not None:
return media_id, None, None
LOG.info('Plex did not provide ID for IMDB or TVDB. Start '
@ -196,154 +197,10 @@ class Artwork(object):
else:
LOG.debug('Start movie set/collection lookup on themoviedb with %s',
item.get('title', ''))
api_key = utils.settings('themoviedbAPIKey')
if media_type == v.PLEX_TYPE_SHOW:
media_type = 'tv'
title = self.title()
# if the title has the year in remove it as tmdb cannot deal with it...
# replace e.g. 'The Americans (2015)' with 'The Americans'
title = sub(r'\s*\(\d{4}\)$', '', title, count=1)
url = 'https://api.themoviedb.org/3/search/%s' % media_type
parameters = {
'api_key': api_key,
'language': v.KODILANGUAGE,
'query': title.encode('utf-8')
}
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('test')
except AttributeError:
LOG.warning('Could not download data from FanartTV')
return
if not data.get('results'):
LOG.info('No match found on themoviedb for type: %s, title: %s',
media_type, title)
return
year = item.get('year')
match_found = None
# find year match
if year:
for entry in data['results']:
if year in entry.get('first_air_date', ''):
match_found = entry
break
elif year in entry.get('release_date', ''):
match_found = entry
break
# find exact match based on title, if we haven't found a year match
if match_found is None:
LOG.info('No themoviedb match found using year %s', year)
replacements = (
' ',
'-',
'&',
',',
':',
';'
)
for entry in data['results']:
name = entry.get('name', entry.get('title', ''))
original_name = entry.get('original_name', '')
title_alt = title.lower()
name_alt = name.lower()
org_name_alt = original_name.lower()
for replace_string in replacements:
title_alt = title_alt.replace(replace_string, '')
name_alt = name_alt.replace(replace_string, '')
org_name_alt = org_name_alt.replace(replace_string, '')
if name == title or original_name == title:
# match found for exact title name
match_found = entry
break
elif (name.split(' (')[0] == title or title_alt == name_alt or
title_alt == org_name_alt):
# match found with substituting some stuff
match_found = entry
break
# if a match was not found, we accept the closest match from TMDB
if match_found is None and data.get('results'):
LOG.info('Using very first match from themoviedb')
match_found = entry = data.get('results')[0]
if match_found is None:
LOG.info('Still no themoviedb match for type: %s, title: %s, '
'year: %s', media_type, title, year)
LOG.debug('themoviedb answer was %s', data['results'])
return
LOG.info('Found themoviedb match for %s: %s',
item.get('title'), match_found)
tmdb_id = str(entry.get('id', ''))
if tmdb_id == '':
LOG.error('No themoviedb ID found, aborting')
return
if media_type == 'multi' and entry.get('media_type'):
media_type = entry.get('media_type')
name = entry.get('name', entry.get('title'))
# lookup external tmdb_id and perform artwork lookup on fanart.tv
parameters = {'api_key': api_key}
if media_type == 'movie':
url = 'https://api.themoviedb.org/3/movie/%s' % tmdb_id
parameters['append_to_response'] = 'videos'
elif media_type == 'tv':
url = 'https://api.themoviedb.org/3/tv/%s' % tmdb_id
parameters['append_to_response'] = 'external_ids,videos'
media_id, poster, background = None, None, None
for language in [v.KODILANGUAGE, 'en']:
parameters['language'] = language
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('test')
except AttributeError:
LOG.warning('Could not download %s with parameters %s',
url, parameters)
continue
if collection is False:
if data.get('imdb_id'):
media_id = str(data.get('imdb_id'))
break
if (data.get('external_ids') and
data['external_ids'].get('tvdb_id')):
media_id = str(data['external_ids']['tvdb_id'])
break
else:
if not data.get('belongs_to_collection'):
continue
media_id = data.get('belongs_to_collection').get('id')
if not media_id:
continue
media_id = str(media_id)
LOG.debug('Retrieved collections tmdb id %s for %s',
media_id, title)
url = 'https://api.themoviedb.org/3/collection/%s' % media_id
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('poster_path')
except AttributeError:
LOG.debug('Could not find TheMovieDB poster paths for %s'
' in the language %s', title, language)
continue
if not poster and data.get('poster_path'):
poster = ('https://image.tmdb.org/t/p/original%s' %
data.get('poster_path'))
if not background and data.get('backdrop_path'):
background = ('https://image.tmdb.org/t/p/original%s' %
data.get('backdrop_path'))
return media_id, poster, background
return fanart_lookup.external_item_id(self.title(),
self.year(),
self.plex_type,
collection)
def lookup_fanart_tv(self, media_id, artworks):
"""

View File

@ -13,6 +13,9 @@ from .. import widgets
LOG = getLogger('PLEX.api')
METADATA_PROVIDERS = (('imdb', utils.REGEX_IMDB),
('tvdb', utils.REGEX_TVDB),
('tmdb', utils.REGEX_TMDB))
class Base(object):
"""
@ -40,6 +43,7 @@ class Base(object):
self._writers = []
self._producers = []
self._locations = []
self._guids = {}
self._coll_match = None
# Plex DB attributes
self._section_id = None
@ -131,6 +135,11 @@ class Base(object):
self.check_db()
return self._fanart_synced
@property
def guids(self):
self._scan_children()
return self._guids
def check_db(self, plexdb=None):
"""
Check's whether we synched this item to Kodi. If so, then retrieve the
@ -457,6 +466,24 @@ class Base(object):
elif child.tag == 'Collection':
self._collections.append((cast(int, child.get('id')),
child.get('tag')))
elif child.tag == 'Guid':
guid = child.get('id')
guid = guid.split('://', 1)
self._guids[guid[0]] = guid[1]
# Plex Movie agent (legacy) or "normal" Plex tv show agent
if not self._guids:
guid = self.xml.get('guid')
if not guid:
return
for provider, regex in METADATA_PROVIDERS:
provider_id = regex.findall(guid)
try:
self._guids[provider] = provider_id[0]
except IndexError:
pass
else:
# There will only ever be one entry
break
def cast(self):
"""
@ -544,33 +571,6 @@ class Base(object):
'writer': [(x, ) for x in self._writers]
}
def provider(self, providername=None):
"""
providername: e.g. 'imdb', 'tvdb'
Return IMDB, e.g. "tt0903624". Returns None if not found
"""
item = self.xml.get('guid')
if not item:
return
if providername == 'imdb':
regex = utils.REGEX_IMDB
elif providername == 'tvdb':
# originally e.g. com.plexapp.agents.thetvdb://276564?lang=en
regex = utils.REGEX_TVDB
elif providername == 'tmdb':
# originally e.g. com.plexapp.agents.themoviedb://603?lang=en
regex = utils.REGEX_TMDB
else:
raise NotImplementedError('Not implemented: %s' % providername)
provider = regex.findall(item)
try:
provider = provider[0]
except IndexError:
provider = None
return provider
def extras(self):
"""
Returns an iterator for etree elements for each extra, e.g. trailers

View File

@ -0,0 +1,182 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
from logging import getLogger
from re import sub
from string import punctuation
from ..downloadutils import DownloadUtils as DU
from .. import utils, variables as v
LOG = getLogger('PLEX.api.fanartlookup')
API_KEY = utils.settings('themoviedbAPIKey')
# How far apart can a video's airing date be (in years)
YEARS_APART = 1
# levenshtein_distance_ratio() returns a value between 0 (no match) and 1 (full
# match). What's the threshold?
LEVENSHTEIN_RATIO_THRESHOLD = 0.95
# Which character should we ignore when matching video titles?
EXCLUDE_CHARS = set(punctuation)
def external_item_id(title, year, plex_type, collection):
LOG.debug('Start identifying %s (%s, %s)', title, year, plex_type)
year = int(year) if year else None
media_type = 'tv' if plex_type == v.PLEX_TYPE_SHOW else plex_type
# if the title has the year in remove it as tmdb cannot deal with it...
# replace e.g. 'The Americans (2015)' with 'The Americans'
title = sub(r'\s*\(\d{4}\)$', '', title, count=1)
url = 'https://api.themoviedb.org/3/search/%s' % media_type
parameters = {
'api_key': API_KEY,
'language': v.KODILANGUAGE,
'query': title.encode('utf-8')
}
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data = data['results']
except (AttributeError, KeyError, TypeError):
LOG.debug('No match found on themoviedb for %s (%s, %s)',
title, year, media_type)
return
LOG.debug('themoviedb returned results: %s', data)
# Some entries don't contain a title or id - get rid of them
data = [x for x in data if 'title' in x and 'id' in x]
# Get rid of all results that do NOT have a matching release year
if year:
data = [x for x in data if __year_almost_matches(year, x)]
if not data:
LOG.debug('Empty results returned by themoviedb for %s (%s, %s)',
title, year, media_type)
return
# Calculate how similar the titles are
title = sanitize_string(title)
for entry in data:
entry['match_score'] = levenshtein_distance_ratio(
sanitize_string(entry['title']), title)
# (one of the possibly many) best match using levenshtein distance ratio
entry = max(data, key=lambda x: x['match_score'])
if entry['match_score'] < LEVENSHTEIN_RATIO_THRESHOLD:
LOG.debug('Best themoviedb match not good enough: %s', entry)
return
# Check if we got several matches. If so, take the most popular one
best_matches = [x for x in data if
x['match_score'] == entry['match_score']
and 'popularity' in x]
entry = max(best_matches, key=lambda x: x['popularity'])
LOG.debug('Found themoviedb match: %s', entry)
# lookup external tmdb_id and perform artwork lookup on fanart.tv
tmdb_id = entry.get('id')
parameters = {'api_key': API_KEY}
if media_type == 'movie':
url = 'https://api.themoviedb.org/3/movie/%s' % tmdb_id
parameters['append_to_response'] = 'videos'
elif media_type == 'tv':
url = 'https://api.themoviedb.org/3/tv/%s' % tmdb_id
parameters['append_to_response'] = 'external_ids,videos'
media_id, poster, background = None, None, None
for language in (v.KODILANGUAGE, 'en'):
parameters['language'] = language
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('test')
except AttributeError:
LOG.warning('Could not download %s with parameters %s',
url, parameters)
continue
if collection is False:
if data.get('imdb_id'):
media_id = str(data.get('imdb_id'))
break
if (data.get('external_ids') and
data['external_ids'].get('tvdb_id')):
media_id = str(data['external_ids']['tvdb_id'])
break
else:
if not data.get('belongs_to_collection'):
continue
media_id = data.get('belongs_to_collection').get('id')
if not media_id:
continue
media_id = str(media_id)
LOG.debug('Retrieved collections tmdb id %s for %s',
media_id, title)
url = 'https://api.themoviedb.org/3/collection/%s' % media_id
data = DU().downloadUrl(url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('poster_path')
except AttributeError:
LOG.debug('Could not find TheMovieDB poster paths for %s'
' in the language %s', title, language)
continue
if not poster and data.get('poster_path'):
poster = ('https://image.tmdb.org/t/p/original%s' %
data.get('poster_path'))
if not background and data.get('backdrop_path'):
background = ('https://image.tmdb.org/t/p/original%s' %
data.get('backdrop_path'))
return media_id, poster, background
def __year_almost_matches(year, entry):
try:
entry_year = int(entry['release_date'][0:4])
except (KeyError, ValueError):
return True
return abs(year - entry_year) <= YEARS_APART
def sanitize_string(s):
s = s.lower().strip()
# Get rid of chars in EXCLUDE_CHARS
s = ''.join(character for character in s if character not in EXCLUDE_CHARS)
# Get rid of multiple spaces
s = ' '.join(s.split())
return s
def levenshtein_distance_ratio(s, t):
"""
Calculates levenshtein distance ratio between two strings.
The more similar the strings, the closer the result will be to 1.
The farther disjunct the string, the closer the result to 0
https://www.datacamp.com/community/tutorials/fuzzy-string-python
"""
# Initialize matrix of zeros
rows = len(s) + 1
cols = len(t) + 1
distance = [[0 for x in range(cols)] for y in range(rows)]
# Populate matrix of zeros with the indeces of each character of both strings
for i in range(1, rows):
for k in range(1,cols):
distance[i][0] = i
distance[0][k] = k
# Iterate over the matrix to compute the cost of deletions,insertions and/or substitutions
for col in range(1, cols):
for row in range(1, rows):
if s[row-1] == t[col-1]:
cost = 0 # If the characters are the same in the two strings in a given position [i,j] then the cost is 0
else:
# In order to align the results with those of the Python Levenshtein package, if we choose to calculate the ratio
# the cost of a substitution is 2. If we calculate just distance, then the cost of a substitution is 1.
cost = 2
distance[row][col] = min(distance[row-1][col] + 1, # Cost of deletions
distance[row][col-1] + 1, # Cost of insertions
distance[row-1][col-1] + cost) # Cost of substitutions
return ((len(s)+len(t)) - distance[row][col]) / (len(s)+len(t))

View File

@ -92,6 +92,9 @@ class PlexCompanion(backgroundthread.KillableThread):
@staticmethod
def _process_alexa(data):
if 'key' not in data or 'containerKey' not in data:
LOG.error('Received malformed Alexa data: %s', data)
return
xml = PF.GetPlexMetadata(data['key'])
try:
xml[0].attrib
@ -147,6 +150,9 @@ class PlexCompanion(backgroundthread.KillableThread):
@staticmethod
def _process_playlist(data):
if 'containerKey' not in data:
LOG.error('Received malformed playlist data: %s', data)
return
# Get the playqueue ID
_, container_key, query = PF.ParseContainerKey(data['containerKey'])
try:
@ -179,6 +185,9 @@ class PlexCompanion(backgroundthread.KillableThread):
"""
Plex Companion client adjusted audio or subtitle stream
"""
if 'type' not in data:
LOG.error('Received malformed stream data: %s', data)
return
playqueue = PQ.get_playqueue_from_type(
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
pos = js.get_position(playqueue.playlistid)
@ -201,6 +210,9 @@ class PlexCompanion(backgroundthread.KillableThread):
"""
example data: {'playQueueID': '8475', 'commandID': '11'}
"""
if 'playQueueID' not in data:
LOG.error('Received malformed refresh data: %s', data)
return
xml = PL.get_pms_playqueue(data['playQueueID'])
if xml is None:
return

View File

@ -131,7 +131,11 @@ def _generate_content(api):
# Item is synched to the Kodi db - let's use that info
# (will thus e.g. include additional artwork or metadata)
item = js.item_details(api.kodi_id, api.kodi_type)
else:
# In rare cases, Kodi's JSON reply does not provide 'title' plus potentially
# other fields - let's use the PMS answer to be safe
# See https://github.com/croneter/PlexKodiConnect/issues/1129
if not api.kodi_id or 'title' not in item:
cast = [{
'name': x[0],
'thumbnail': x[1],
@ -168,8 +172,9 @@ def _generate_content(api):
'trailer': api.trailer(),
'tvshowtitle': api.show_title(),
'uniqueid': {
'imdbnumber': api.provider('imdb') or '',
'tvdb_id': api.provider('tvdb') or ''
'imdbnumber': api.guids.get('imdb') or '',
'tvdb_id': api.guids.get('tvdb') or '',
'tmdb_id': api.guids.get('tmdb') or ''
},
'votes': '0', # [str]!
'writer': api.writers(), # list of [str]

View File

@ -112,7 +112,7 @@
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" visible="false"/>
<setting type="sep" />
<setting id="playType" type="enum" label="30002" values="Try Direct Path|Direct Play|Direct Stream|Force Transcode" default="0" />
<setting id="transcoderVideoQualities" type="enum" label="30160" values="420x420, 320kbps|576x320, 720kbps|720x480, 1.5Mbps|1024x768, 2Mbps|720p, 3Mbps|720p, 4Mbps|1080p, 8Mbps|1080p, 10Mbps|1080p, 12Mbps|1080p, 20Mbps|1080p, 40Mbps|4K, 35Mbps|4K, 50Mbps" default="10" /><!-- Video Quality if Transcoding necessary -->
<setting id="transcoderVideoQualities" type="enum" label="30160" values="720x480, 1.5Mbps|1024x768, 2Mbps|720p, 3Mbps|720p, 4Mbps|1080p, 8Mbps|1080p, 10Mbps|1080p, 12Mbps|1080p, 20Mbps|1080p, 40Mbps|4K, 35Mbps|4K, 50Mbps" default="10" /><!-- Video Quality if Transcoding necessary -->
<setting id="auto_adjust_transcode_quality" label="30161" type="bool" default="false" /><!-- Auto-adjust transcoding quality (deactivate for Chromecast) -->
<setting id="maxVideoQualities" type="enum" label="30143" values="320kbps|720kbps|1.5Mbps|2Mbps|3Mbps|4Mbps|8Mbps|10Mbps|12Mbps|20Mbps|40Mbps|deactivated" default="11" /><!-- Always transcode if video bitrate is above -->
<setting id="transcodeH265" type="enum" label="30522" default="0" values="Disabled (default)|480p (and higher)|720p (and higher)|1080p (and higher)|4K" /><!-- Force transcode h265/HEVC -->