Merge branch 'develop' into translations
This commit is contained in:
commit
4dc42ce7cf
16 changed files with 369 additions and 227 deletions
|
@ -1,5 +1,5 @@
|
||||||
[![stable version](https://img.shields.io/badge/stable_version-1.7.7-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip)
|
[![stable version](https://img.shields.io/badge/stable_version-1.7.7-blue.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect/bin/repository.plexkodiconnect/repository.plexkodiconnect-1.0.0.zip)
|
||||||
[![beta version](https://img.shields.io/badge/beta_version-1.7.8-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip)
|
[![beta version](https://img.shields.io/badge/beta_version-1.7.9-red.svg?maxAge=60&style=flat) ](https://dl.bintray.com/croneter/PlexKodiConnect_BETA/bin-BETA/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.0.zip)
|
||||||
|
|
||||||
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
||||||
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
||||||
|
|
31
addon.xml
31
addon.xml
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.8" provider-name="croneter">
|
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.9" provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
<import addon="script.module.requests" version="2.3.0" />
|
<import addon="script.module.requests" version="2.3.0" />
|
||||||
|
@ -24,33 +24,38 @@
|
||||||
<email></email>
|
<email></email>
|
||||||
<source>https://github.com/croneter/PlexKodiConnect</source>
|
<source>https://github.com/croneter/PlexKodiConnect</source>
|
||||||
<summary lang="en_GB">Native Integration of Plex into Kodi</summary>
|
<summary lang="en_GB">Native Integration of Plex into Kodi</summary>
|
||||||
<summary lang="en_US">Native Integration of Plex into Kodi</summary>
|
|
||||||
<summary lang="en_NZ">Native Integration of Plex into Kodi</summary>
|
|
||||||
<summary lang="en_AU">Native Integration of Plex into Kodi</summary>
|
|
||||||
<summary lang="cs_CZ">Úplná integrace Plexu do Kodi</summary>
|
<summary lang="cs_CZ">Úplná integrace Plexu do Kodi</summary>
|
||||||
<summary lang="de_DE">Komplette Integration von Plex in Kodi</summary>
|
<summary lang="de_DE">Komplette Integration von Plex in Kodi</summary>
|
||||||
<summary lang="da_DK">Indbygget Integration af Plex i Kodi</summary>
|
<summary lang="da_DK">Indbygget Integration af Plex i Kodi</summary>
|
||||||
<summary lang="nl_NL">Directe integratie van Plex in Kodi</summary>
|
<summary lang="nl_NL">Directe integratie van Plex in Kodi</summary>
|
||||||
<summary lang="zh_TW">將Plex直接內嵌至Kodi</summary>
|
<summary lang="zh_TW">將Plex直接內嵌至Kodi</summary>
|
||||||
<description lang="en_GB">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).</description>
|
<summary lang="es_ES">Integración Nativa de Plex en Kodi</summary>
|
||||||
<description lang="en_US">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).</description>
|
<description lang="en_GB">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>
|
||||||
<description lang="en_NZ">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).</description>
|
|
||||||
<description lang="en_AU">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).</description>
|
|
||||||
<description lang="cs_CZ">Připojte Kodi ke svému Plex Media Serveru. Tento doplněk předpokládá, že spravujete veškerá svá videa pomocí Plexu (nikoliv pomocí Kodi). Můžete přijít o data uložená ve video a hudební databázi Kodi (tento doplněk je přímo mění).</description>
|
<description lang="cs_CZ">Připojte Kodi ke svému Plex Media Serveru. Tento doplněk předpokládá, že spravujete veškerá svá videa pomocí Plexu (nikoliv pomocí Kodi). Můžete přijít o data uložená ve video a hudební databázi Kodi (tento doplněk je přímo mění).</description>
|
||||||
<description lang="de_DE">Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert).</description>
|
<description lang="de_DE">Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert).</description>
|
||||||
<description lang="da_DK">Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem).</description>
|
<description lang="da_DK">Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem).</description>
|
||||||
<description lang="nl_NL">Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen.</description>
|
<description lang="nl_NL">Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen.</description>
|
||||||
<description lang="zh_TW">連接Kodi至您的Plex媒體伺服器.這個外掛程式假設你使用Plex伺服器管理您所有的媒體(完全沒用Kodi管理).您可能會失去已經儲存在Kodi資料庫中的視頻及音樂的資料(這個外掛程式會直接更改它們).</description>
|
<description lang="zh_TW">連接Kodi至您的Plex媒體伺服器.這個外掛程式假設你使用Plex伺服器管理您所有的媒體(完全沒用Kodi管理).您可能會失去已經儲存在Kodi資料庫中的視頻及音樂的資料(這個外掛程式會直接更改它們).</description>
|
||||||
|
<description lang="es_ES">Conecte Kodi a su Plex Media Server. Este plugin asume que usted gestiona todos sus vídeos con Plex (y ninguno con Kodi). Puede perder datos ya guardados en las bases de datos de vídeo y música de Kodi (ya que este plugin las actualiza directamente). ¡Úselo a su propio riesgo!</description>
|
||||||
<disclaimer lang="en_GB">Use at your own risk</disclaimer>
|
<disclaimer lang="en_GB">Use at your own risk</disclaimer>
|
||||||
<disclaimer lang="en_US">Use at your own risk</disclaimer>
|
|
||||||
<disclaimer lang="en_NZ">Use at your own risk</disclaimer>
|
|
||||||
<disclaimer lang="en_AU">Use at your own risk</disclaimer>
|
|
||||||
<disclaimer lang="cs_CZ">Používejte na vlastní nebezpečí</disclaimer>
|
<disclaimer lang="cs_CZ">Používejte na vlastní nebezpečí</disclaimer>
|
||||||
<disclaimer lang="de_DE">Benutze auf eigene Gefahr</disclaimer>
|
<disclaimer lang="de_DE">Benutze auf eigene Gefahr</disclaimer>
|
||||||
<disclaimer lang="da_DK">Brug på eget ansvar</disclaimer>
|
<disclaimer lang="da_DK">Brug på eget ansvar</disclaimer>
|
||||||
<disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer>
|
<disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer>
|
||||||
<disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer>
|
<disclaimer lang="zh_TW">使用風險由您自己承擔</disclaimer>
|
||||||
<news>version 1.7.8 (beta only)
|
<disclaimer lang="es_ES">Usar a su propio riesgo</disclaimer>
|
||||||
|
<news>version 1.7.9 (beta only)
|
||||||
|
- Big transcoding overhaul
|
||||||
|
- Fix for not detecting external subtitle language
|
||||||
|
- Change Plex transcoding profile to Android
|
||||||
|
- Use Kodi video cache setting for transcoding
|
||||||
|
- Fix TheTVDB ID for TV shows
|
||||||
|
- Account for missing IMDB ids for movies
|
||||||
|
- Account for missing TheTVDB ids
|
||||||
|
- Fix UnicodeDecodeError on user switch
|
||||||
|
- Update English, Spanish and German
|
||||||
|
|
||||||
|
version 1.7.8 (beta only)
|
||||||
- Fix IMDB id for movies (resync by going to the PKC settings, Advanced, then Repair Local Database)
|
- Fix IMDB id for movies (resync by going to the PKC settings, Advanced, then Repair Local Database)
|
||||||
- Increase timeouts for PMS, should fix some connection issues
|
- Increase timeouts for PMS, should fix some connection issues
|
||||||
- Move translations to new strings.po system
|
- Move translations to new strings.po system
|
||||||
|
@ -70,7 +75,7 @@ version 1.7.5
|
||||||
- Dutch translation, thanks @mvanbaak
|
- Dutch translation, thanks @mvanbaak
|
||||||
|
|
||||||
version 1.7.4 (beta only)
|
version 1.7.4 (beta only)
|
||||||
- Show menu item only for appropriate Kodi library: Be careful to start video content through Videos -> Video Addons -> ... and pictures through Pictures -> Picture Addons -> ...
|
- Show menu item only for appropriate Kodi library: Be careful to start video content through Videos - Video Addons - ... and pictures through Pictures - Picture Addons - ...
|
||||||
- Fix playback error popup when using Alexa
|
- Fix playback error popup when using Alexa
|
||||||
- New Italian translations, thanks @nikkux, @chicco83
|
- New Italian translations, thanks @nikkux, @chicco83
|
||||||
- Update translations
|
- Update translations
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
version 1.7.9 (beta only)
|
||||||
|
- Big transcoding overhaul
|
||||||
|
- Fix for not detecting external subtitle language
|
||||||
|
- Change Plex transcoding profile to Android
|
||||||
|
- Use Kodi video cache setting for transcoding
|
||||||
|
- Fix TheTVDB ID for TV shows
|
||||||
|
- Account for missing IMDB ids for movies
|
||||||
|
- Account for missing TheTVDB ids
|
||||||
|
- Fix UnicodeDecodeError on user switch
|
||||||
|
- Update English, Spanish and German
|
||||||
|
|
||||||
version 1.7.8 (beta only)
|
version 1.7.8 (beta only)
|
||||||
- Fix IMDB id for movies (resync by going to the PKC settings, Advanced, then Repair Local Database)
|
- Fix IMDB id for movies (resync by going to the PKC settings, Advanced, then Repair Local Database)
|
||||||
- Increase timeouts for PMS, should fix some connection issues
|
- Increase timeouts for PMS, should fix some connection issues
|
||||||
|
|
|
@ -1851,7 +1851,6 @@ msgctxt "#39702"
|
||||||
msgid "Browse by folder"
|
msgid "Browse by folder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
||||||
# For use with addon.xml (PKC metadata for Kodi, e.g. description)
|
# For use with addon.xml (PKC metadata for Kodi, e.g. description)
|
||||||
# Addon Summary
|
# Addon Summary
|
||||||
msgctxt "#39703"
|
msgctxt "#39703"
|
||||||
|
@ -1869,3 +1868,29 @@ msgstr ""
|
||||||
msgctxt "#39705"
|
msgctxt "#39705"
|
||||||
msgid "Use at your own risk"
|
msgid "Use at your own risk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
||||||
|
# If user gets prompted to choose between several subtitles. Leave the number one at the beginning of the string!
|
||||||
|
msgctxt "#39706"
|
||||||
|
msgid "1 No subtitles"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# If user gets prompted to choose between several audio/subtitle tracks and language is unknown
|
||||||
|
msgctxt "#39707"
|
||||||
|
msgid "unknown"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# If user gets prompted to choose between several subtitles and Plex adds the "default" flag
|
||||||
|
msgctxt "#39708"
|
||||||
|
msgid "Default"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# If user gets prompted to choose between several subtitles and Plex adds the "forced" flag
|
||||||
|
msgctxt "#39709"
|
||||||
|
msgid "Forced"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# If user gets prompted to choose between several subtitles the subtitle cannot be downloaded (has no 'key' attribute from the PMS), the subtitle needs to be burned in
|
||||||
|
msgctxt "#39710"
|
||||||
|
msgid "burn-in"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -43,7 +43,7 @@ from os import path as os_path
|
||||||
|
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
from xbmc import sleep, executebuiltin
|
from xbmc import sleep, executebuiltin
|
||||||
from xbmcvfs import exists
|
from xbmcvfs import exists, mkdirs
|
||||||
|
|
||||||
import clientinfo as client
|
import clientinfo as client
|
||||||
from downloadutils import DownloadUtils
|
from downloadutils import DownloadUtils
|
||||||
|
@ -2258,37 +2258,37 @@ class API():
|
||||||
return url
|
return url
|
||||||
|
|
||||||
# For Transcoding
|
# For Transcoding
|
||||||
|
headers = {
|
||||||
|
'X-Plex-Platform': 'Android',
|
||||||
|
'X-Plex-Platform-Version': '7.0',
|
||||||
|
'X-Plex-Product': 'Plex for Android',
|
||||||
|
'X-Plex-Version': '5.8.0.475'
|
||||||
|
}
|
||||||
# Path/key to VIDEO item of xml PMS response is needed, not part
|
# Path/key to VIDEO item of xml PMS response is needed, not part
|
||||||
path = self.item.attrib['key']
|
path = self.item.attrib['key']
|
||||||
transcodePath = self.server + \
|
transcodePath = self.server + \
|
||||||
'/video/:/transcode/universal/start.m3u8?'
|
'/video/:/transcode/universal/start.m3u8?'
|
||||||
args = {
|
args = {
|
||||||
|
'audioBoost': settings('audioBoost'),
|
||||||
|
'autoAdjustQuality': 0,
|
||||||
|
'directPlay': 0,
|
||||||
|
'directStream': 1,
|
||||||
'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
|
'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
|
||||||
'session': window('plex_client_Id'),
|
'session': window('plex_client_Id'),
|
||||||
'fastSeek': 1,
|
'fastSeek': 1,
|
||||||
'path': path,
|
'path': path,
|
||||||
'mediaIndex': self.mediastream,
|
'mediaIndex': self.mediastream,
|
||||||
'partIndex': self.part,
|
'partIndex': self.part,
|
||||||
|
'hasMDE': 1,
|
||||||
|
'location': 'lan',
|
||||||
|
'subtitleSize': settings('subtitleSize')
|
||||||
# 'copyts': 1,
|
# 'copyts': 1,
|
||||||
# 'offset': 0, # Resume point
|
# 'offset': 0, # Resume point
|
||||||
}
|
}
|
||||||
# Seem like PHT to let the PMS use the transcoding profile
|
# Look like Android to let the PMS use the transcoding profile
|
||||||
xargs['X-Plex-Device'] = 'Plex Home Theater'
|
xargs.update(headers)
|
||||||
# Currently not used!
|
log.debug("Setting transcode quality to: %s" % quality)
|
||||||
if action == "DirectStream":
|
args.update(quality)
|
||||||
argsUpdate = {
|
|
||||||
'directPlay': '0',
|
|
||||||
'directStream': '1',
|
|
||||||
}
|
|
||||||
args.update(argsUpdate)
|
|
||||||
elif action == 'Transcode':
|
|
||||||
argsUpdate = {
|
|
||||||
'directPlay': '0',
|
|
||||||
'directStream': '0'
|
|
||||||
}
|
|
||||||
log.debug("Setting transcode quality to: %s" % quality)
|
|
||||||
args.update(quality)
|
|
||||||
args.update(argsUpdate)
|
|
||||||
url = transcodePath + urlencode(xargs) + '&' + urlencode(args)
|
url = transcodePath + urlencode(xargs) + '&' + urlencode(args)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
@ -2301,24 +2301,58 @@ class API():
|
||||||
return
|
return
|
||||||
kodiindex = 0
|
kodiindex = 0
|
||||||
for stream in mediastreams:
|
for stream in mediastreams:
|
||||||
index = stream.attrib['id']
|
|
||||||
# Since plex returns all possible tracks together, have to pull
|
# Since plex returns all possible tracks together, have to pull
|
||||||
# only external subtitles.
|
# only external subtitles - only for these a 'key' exists
|
||||||
|
if stream.attrib.get('streamType') != "3":
|
||||||
|
# Not a subtitle
|
||||||
|
continue
|
||||||
|
# Only set for additional external subtitles NOT lying beside video
|
||||||
key = stream.attrib.get('key')
|
key = stream.attrib.get('key')
|
||||||
# IsTextSubtitleStream if true, is available to download from plex.
|
# Only set for dedicated subtitle files lying beside video
|
||||||
if stream.attrib.get('streamType') == "3" and key:
|
# ext = stream.attrib.get('format')
|
||||||
# Direct stream
|
if key:
|
||||||
url = ("%s%s" % (self.server, key))
|
# We do know the language - temporarily download
|
||||||
url = self.addPlexCredentialsToUrl(url)
|
if stream.attrib.get('languageCode') is not None:
|
||||||
|
path = self.download_external_subtitles(
|
||||||
|
"{server}%s" % key,
|
||||||
|
"subtitle.%s.%s" % (stream.attrib['languageCode'],
|
||||||
|
stream.attrib['codec']))
|
||||||
|
# We don't know the language - no need to download
|
||||||
|
else:
|
||||||
|
path = self.addPlexCredentialsToUrl(
|
||||||
|
"%s%s" % (self.server, key))
|
||||||
# map external subtitles for mapping
|
# map external subtitles for mapping
|
||||||
mapping[kodiindex] = index
|
mapping[kodiindex] = stream.attrib['id']
|
||||||
externalsubs.append(url)
|
externalsubs.append(path)
|
||||||
kodiindex += 1
|
kodiindex += 1
|
||||||
mapping = dumps(mapping)
|
mapping = dumps(mapping)
|
||||||
window('plex_%s.indexMapping' % playurl, value=mapping)
|
window('plex_%s.indexMapping' % playurl, value=mapping)
|
||||||
log.info('Found external subs: %s' % externalsubs)
|
log.info('Found external subs: %s' % externalsubs)
|
||||||
return externalsubs
|
return externalsubs
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def download_external_subtitles(url, filename):
|
||||||
|
"""
|
||||||
|
One cannot pass the subtitle language for ListItems. Workaround; will
|
||||||
|
download the subtitle at url to the Kodi PKC directory in a temp dir
|
||||||
|
|
||||||
|
Returns the path to the downloaded subtitle or None
|
||||||
|
"""
|
||||||
|
if not exists(v.EXTERNAL_SUBTITLE_TEMP_PATH):
|
||||||
|
mkdirs(v.EXTERNAL_SUBTITLE_TEMP_PATH)
|
||||||
|
path = os_path.join(v.EXTERNAL_SUBTITLE_TEMP_PATH, filename)
|
||||||
|
r = DownloadUtils().downloadUrl(url, return_response=True)
|
||||||
|
try:
|
||||||
|
r.status_code
|
||||||
|
except AttributeError:
|
||||||
|
log.error('Could not temporarily download subtitle %s' % url)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
r.encoding = 'utf-8'
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(r.content)
|
||||||
|
return path
|
||||||
|
|
||||||
def GetKodiPremierDate(self):
|
def GetKodiPremierDate(self):
|
||||||
"""
|
"""
|
||||||
Takes Plex' originallyAvailableAt of the form "yyyy-mm-dd" and returns
|
Takes Plex' originallyAvailableAt of the form "yyyy-mm-dd" and returns
|
||||||
|
@ -2601,6 +2635,7 @@ class API():
|
||||||
|
|
||||||
# Append external subtitles to stream
|
# Append external subtitles to stream
|
||||||
playmethod = window('%s.playmethod' % plexitem)
|
playmethod = window('%s.playmethod' % plexitem)
|
||||||
|
# Direct play automatically appends external
|
||||||
|
# BUT: Plex may add additional subtitles NOT lying right next to video
|
||||||
if playmethod in ("DirectStream", "DirectPlay"):
|
if playmethod in ("DirectStream", "DirectPlay"):
|
||||||
subtitles = self.externalSubs(playurl)
|
listitem.setSubtitles(self.externalSubs(playurl))
|
||||||
listitem.setSubtitles(subtitles)
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ class DownloadUtils():
|
||||||
|
|
||||||
def downloadUrl(self, url, action_type="GET", postBody=None,
|
def downloadUrl(self, url, action_type="GET", postBody=None,
|
||||||
parameters=None, authenticate=True, headerOptions=None,
|
parameters=None, authenticate=True, headerOptions=None,
|
||||||
verifySSL=True, timeout=None):
|
verifySSL=True, timeout=None, return_response=False):
|
||||||
"""
|
"""
|
||||||
Override SSL check with verifySSL=False
|
Override SSL check with verifySSL=False
|
||||||
|
|
||||||
|
@ -169,7 +169,8 @@ class DownloadUtils():
|
||||||
401, ... integer if PMS answered with HTTP error 401
|
401, ... integer if PMS answered with HTTP error 401
|
||||||
(unauthorized) or other http error codes
|
(unauthorized) or other http error codes
|
||||||
xml xml etree root object, if applicable
|
xml xml etree root object, if applicable
|
||||||
JSON json() object, if applicable
|
json json() object, if applicable
|
||||||
|
<response-object> if return_response=True is set (200, 201 only)
|
||||||
"""
|
"""
|
||||||
kwargs = {'timeout': self.timeout}
|
kwargs = {'timeout': self.timeout}
|
||||||
if authenticate is True:
|
if authenticate is True:
|
||||||
|
@ -290,6 +291,9 @@ class DownloadUtils():
|
||||||
elif r.status_code in (200, 201):
|
elif r.status_code in (200, 201):
|
||||||
# 200: OK
|
# 200: OK
|
||||||
# 201: Created
|
# 201: Created
|
||||||
|
if return_response is True:
|
||||||
|
# return the entire response object
|
||||||
|
return r
|
||||||
try:
|
try:
|
||||||
# xml response
|
# xml response
|
||||||
r = etree.fromstring(r.content)
|
r = etree.fromstring(r.content)
|
||||||
|
|
|
@ -6,7 +6,8 @@ import logging
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
from utils import settings, window, language as lang, tryEncode
|
from utils import settings, window, language as lang, tryEncode, \
|
||||||
|
get_advancessettings_xml_setting
|
||||||
import downloadutils
|
import downloadutils
|
||||||
from userclient import UserClient
|
from userclient import UserClient
|
||||||
|
|
||||||
|
@ -399,6 +400,16 @@ class InitialSetup():
|
||||||
log.info("Initial setup called.")
|
log.info("Initial setup called.")
|
||||||
dialog = self.dialog
|
dialog = self.dialog
|
||||||
|
|
||||||
|
# Get current Kodi video cache setting
|
||||||
|
cache = get_advancessettings_xml_setting(['cache', 'memorysize'])
|
||||||
|
if cache is not None:
|
||||||
|
cache = str(cache.text)
|
||||||
|
else:
|
||||||
|
# Kodi default cache
|
||||||
|
cache = '20971520'
|
||||||
|
log.info('Current Kodi video memory cache in bytes: %s' % cache)
|
||||||
|
settings('kodi_video_cache', value=cache)
|
||||||
|
|
||||||
# Optionally sign into plex.tv. Will not be called on very first run
|
# Optionally sign into plex.tv. Will not be called on very first run
|
||||||
# as plexToken will be ''
|
# as plexToken will be ''
|
||||||
settings('plex_status', value='Not logged in to plex.tv')
|
settings('plex_status', value='Not logged in to plex.tv')
|
||||||
|
|
|
@ -324,13 +324,16 @@ class Movies(Items):
|
||||||
votecount,
|
votecount,
|
||||||
rating_id)
|
rating_id)
|
||||||
# update new uniqueid Kodi 17
|
# update new uniqueid Kodi 17
|
||||||
uniqueid = self.kodi_db.get_uniqueid(movieid,
|
if imdb is not None:
|
||||||
v.KODI_TYPE_MOVIE)
|
uniqueid = self.kodi_db.get_uniqueid(movieid,
|
||||||
self.kodi_db.update_uniqueid(movieid,
|
v.KODI_TYPE_MOVIE)
|
||||||
v.KODI_TYPE_MOVIE,
|
self.kodi_db.update_uniqueid(movieid,
|
||||||
imdb,
|
v.KODI_TYPE_MOVIE,
|
||||||
"imdb",
|
imdb,
|
||||||
uniqueid)
|
"imdb",
|
||||||
|
uniqueid)
|
||||||
|
else:
|
||||||
|
uniqueid = -1
|
||||||
query = '''
|
query = '''
|
||||||
UPDATE movie
|
UPDATE movie
|
||||||
SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?,
|
SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?,
|
||||||
|
@ -372,12 +375,15 @@ class Movies(Items):
|
||||||
rating,
|
rating,
|
||||||
votecount)
|
votecount)
|
||||||
# add new uniqueid Kodi 17
|
# add new uniqueid Kodi 17
|
||||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
if imdb is not None:
|
||||||
self.kodi_db.add_uniqueid(uniqueid,
|
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||||
movieid,
|
self.kodi_db.add_uniqueid(uniqueid,
|
||||||
v.KODI_TYPE_MOVIE,
|
movieid,
|
||||||
imdb,
|
v.KODI_TYPE_MOVIE,
|
||||||
"imdb")
|
imdb,
|
||||||
|
"imdb")
|
||||||
|
else:
|
||||||
|
uniqueid = -1
|
||||||
query = '''
|
query = '''
|
||||||
INSERT INTO movie(idMovie, idFile, c00, c01, c02, c03,
|
INSERT INTO movie(idMovie, idFile, c00, c01, c02, c03,
|
||||||
c04, c05, c06, c07, c09, c10, c11, c12, c14, c15, c16,
|
c04, c05, c06, c07, c09, c10, c11, c12, c14, c15, c16,
|
||||||
|
@ -617,12 +623,16 @@ class TVShows(Items):
|
||||||
votecount,
|
votecount,
|
||||||
rating_id)
|
rating_id)
|
||||||
# update new uniqueid Kodi 17
|
# update new uniqueid Kodi 17
|
||||||
uniqueid = self.kodi_db.get_uniqueid(showid, v.KODI_TYPE_SHOW)
|
if tvdb is not None:
|
||||||
self.kodi_db.update_uniqueid(showid,
|
uniqueid = self.kodi_db.get_uniqueid(showid,
|
||||||
v.KODI_TYPE_SHOW,
|
v.KODI_TYPE_SHOW)
|
||||||
tvdb,
|
self.kodi_db.update_uniqueid(showid,
|
||||||
"tvdb",
|
v.KODI_TYPE_SHOW,
|
||||||
uniqueid)
|
tvdb,
|
||||||
|
"unknown",
|
||||||
|
uniqueid)
|
||||||
|
else:
|
||||||
|
uniqueid = -1
|
||||||
# Update the tvshow entry
|
# Update the tvshow entry
|
||||||
query = '''
|
query = '''
|
||||||
UPDATE tvshow
|
UPDATE tvshow
|
||||||
|
@ -631,8 +641,9 @@ class TVShows(Items):
|
||||||
WHERE idShow = ?
|
WHERE idShow = ?
|
||||||
'''
|
'''
|
||||||
kodicursor.execute(query, (title, plot, rating_id,
|
kodicursor.execute(query, (title, plot, rating_id,
|
||||||
premieredate, genre, title, tvdb,
|
premieredate, genre, title,
|
||||||
mpaa, studio, sorttitle, showid))
|
uniqueid, mpaa, studio, sorttitle,
|
||||||
|
showid))
|
||||||
else:
|
else:
|
||||||
# Update the tvshow entry
|
# Update the tvshow entry
|
||||||
query = '''
|
query = '''
|
||||||
|
@ -679,11 +690,15 @@ class TVShows(Items):
|
||||||
rating,
|
rating,
|
||||||
votecount)
|
votecount)
|
||||||
# add new uniqueid Kodi 17
|
# add new uniqueid Kodi 17
|
||||||
self.kodi_db.add_uniqueid(self.kodi_db.create_entry_uniqueid(),
|
if tvdb is not None:
|
||||||
showid,
|
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||||
v.KODI_TYPE_SHOW,
|
self.kodi_db.add_uniqueid(uniqueid,
|
||||||
tvdb,
|
showid,
|
||||||
"tvdb")
|
v.KODI_TYPE_SHOW,
|
||||||
|
tvdb,
|
||||||
|
"unknown")
|
||||||
|
else:
|
||||||
|
uniqueid = -1
|
||||||
# Create the tvshow entry
|
# Create the tvshow entry
|
||||||
query = '''
|
query = '''
|
||||||
INSERT INTO tvshow(
|
INSERT INTO tvshow(
|
||||||
|
@ -692,8 +707,8 @@ class TVShows(Items):
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
'''
|
'''
|
||||||
kodicursor.execute(query, (showid, title, plot, rating_id,
|
kodicursor.execute(query, (showid, title, plot, rating_id,
|
||||||
premieredate, genre, title, tvdb,
|
premieredate, genre, title,
|
||||||
mpaa, studio, sorttitle))
|
uniqueid, mpaa, studio, sorttitle))
|
||||||
else:
|
else:
|
||||||
# Create the tvshow entry
|
# Create the tvshow entry
|
||||||
query = '''
|
query = '''
|
||||||
|
|
|
@ -106,7 +106,8 @@ class KodiMonitor(Monitor):
|
||||||
log.error("Could not find itemid in plex database for a "
|
log.error("Could not find itemid in plex database for a "
|
||||||
"video library update")
|
"video library update")
|
||||||
else:
|
else:
|
||||||
# Stop from manually marking as watched unwatched, with actual playback.
|
# Stop from manually marking as watched unwatched, with
|
||||||
|
# actual playback.
|
||||||
if window('plex_skipWatched%s' % itemid) == "true":
|
if window('plex_skipWatched%s' % itemid) == "true":
|
||||||
# property is set in player.py
|
# property is set in player.py
|
||||||
window('plex_skipWatched%s' % itemid, clear=True)
|
window('plex_skipWatched%s' % itemid, clear=True)
|
||||||
|
@ -171,11 +172,11 @@ class KodiMonitor(Monitor):
|
||||||
|
|
||||||
# Try to get a Kodi ID
|
# Try to get a Kodi ID
|
||||||
# If PKC was used - native paths, not direct paths
|
# If PKC was used - native paths, not direct paths
|
||||||
plexid = window('plex_%s.itemid' % tryEncode(currentFile))
|
plex_id = window('plex_%s.itemid' % tryEncode(currentFile))
|
||||||
# Get rid of the '' if the window property was not set
|
# Get rid of the '' if the window property was not set
|
||||||
plexid = None if not plexid else plexid
|
plex_id = None if not plex_id else plex_id
|
||||||
kodiid = None
|
kodiid = None
|
||||||
if plexid is None:
|
if plex_id is None:
|
||||||
log.debug('Did not get Plex id from window properties')
|
log.debug('Did not get Plex id from window properties')
|
||||||
try:
|
try:
|
||||||
kodiid = data['item']['id']
|
kodiid = data['item']['id']
|
||||||
|
@ -183,30 +184,39 @@ class KodiMonitor(Monitor):
|
||||||
log.debug('Did not get a Kodi id from Kodi, darn')
|
log.debug('Did not get a Kodi id from Kodi, darn')
|
||||||
# For direct paths, if we're not streaming something
|
# For direct paths, if we're not streaming something
|
||||||
# When using Widgets, Kodi doesn't tell us shit so we need this hack
|
# When using Widgets, Kodi doesn't tell us shit so we need this hack
|
||||||
if (kodiid is None and plexid is None and typus != 'song'
|
if (kodiid is None and plex_id is None and typus != 'song'
|
||||||
and not currentFile.startswith('http')):
|
and not currentFile.startswith('http')):
|
||||||
(kodiid, typus) = get_kodiid_from_filename(currentFile)
|
(kodiid, typus) = get_kodiid_from_filename(currentFile)
|
||||||
if kodiid is None:
|
if kodiid is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if plexid is None:
|
if plex_id is None:
|
||||||
# Get Plex' item id
|
# Get Plex' item id
|
||||||
with plexdb.Get_Plex_DB() as plexcursor:
|
with plexdb.Get_Plex_DB() as plexcursor:
|
||||||
plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
|
plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus)
|
||||||
try:
|
try:
|
||||||
plexid = plex_dbitem[0]
|
plex_id = plex_dbitem[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
log.info("No Plex id returned for kodiid %s. Aborting playback"
|
log.info("No Plex id returned for kodiid %s. Aborting playback"
|
||||||
" report" % kodiid)
|
" report" % kodiid)
|
||||||
return
|
return
|
||||||
log.debug("Found Plex id %s for Kodi id %s for type %s"
|
log.debug("Found Plex id %s for Kodi id %s for type %s"
|
||||||
% (plexid, kodiid, typus))
|
% (plex_id, kodiid, typus))
|
||||||
|
|
||||||
|
# Switch subtitle tracks if applicable
|
||||||
|
subtitle = window('plex_%s.subtitle' % tryEncode(currentFile))
|
||||||
|
if window(tryEncode('plex_%s.playmethod' % currentFile)) \
|
||||||
|
== 'Transcode' and subtitle:
|
||||||
|
if window('plex_%s.subtitle' % currentFile) == 'None':
|
||||||
|
self.xbmcplayer.showSubtitles(False)
|
||||||
|
else:
|
||||||
|
self.xbmcplayer.setSubtitleStream(int(subtitle))
|
||||||
|
|
||||||
# Set some stuff if Kodi initiated playback
|
# Set some stuff if Kodi initiated playback
|
||||||
if ((settings('useDirectPaths') == "1" and not typus == "song")
|
if ((settings('useDirectPaths') == "1" and not typus == "song")
|
||||||
or
|
or
|
||||||
(typus == "song" and settings('enableMusic') == "true")):
|
(typus == "song" and settings('enableMusic') == "true")):
|
||||||
if self.StartDirectPath(plexid,
|
if self.StartDirectPath(plex_id,
|
||||||
typus,
|
typus,
|
||||||
tryEncode(currentFile)) is False:
|
tryEncode(currentFile)) is False:
|
||||||
log.error('Could not initiate monitoring; aborting')
|
log.error('Could not initiate monitoring; aborting')
|
||||||
|
@ -214,19 +224,19 @@ class KodiMonitor(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=plex_id)
|
||||||
window("plex_%s.itemid" % tryEncode(currentFile), value=plexid)
|
window("plex_%s.itemid" % tryEncode(currentFile), value=plex_id)
|
||||||
log.info('Finish playback startup')
|
log.info('Finish playback startup')
|
||||||
|
|
||||||
def StartDirectPath(self, plexid, type, currentFile):
|
def StartDirectPath(self, plex_id, type, currentFile):
|
||||||
"""
|
"""
|
||||||
Set some additional stuff if playback was initiated by Kodi, not PKC
|
Set some additional stuff if playback was initiated by Kodi, not PKC
|
||||||
"""
|
"""
|
||||||
xml = self.doUtils('{server}/library/metadata/%s' % plexid)
|
xml = self.doUtils('{server}/library/metadata/%s' % plex_id)
|
||||||
try:
|
try:
|
||||||
xml[0].attrib
|
xml[0].attrib
|
||||||
except:
|
except:
|
||||||
log.error('Did not receive a valid XML for plexid %s.' % plexid)
|
log.error('Did not receive a valid XML for plex_id %s.' % plex_id)
|
||||||
return False
|
return False
|
||||||
# Setup stuff, because playback was started by Kodi, not PKC
|
# Setup stuff, because playback was started by Kodi, not PKC
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
|
|
|
@ -75,10 +75,7 @@ class PlaybackUtils():
|
||||||
|
|
||||||
playmethod = window('plex_%s.playmethod' % playurl)
|
playmethod = window('plex_%s.playmethod' % playurl)
|
||||||
if playmethod == "Transcode":
|
if playmethod == "Transcode":
|
||||||
window('plex_%s.playmethod' % playurl, clear=True)
|
playutils.audioSubsPref(listitem, tryDecode(playurl))
|
||||||
playurl = tryEncode(playutils.audioSubsPref(
|
|
||||||
listitem, tryDecode(playurl)))
|
|
||||||
window('plex_%s.playmethod' % playurl, "Transcode")
|
|
||||||
listitem.setPath(playurl)
|
listitem.setPath(playurl)
|
||||||
api.set_playback_win_props(playurl, listitem)
|
api.set_playback_win_props(playurl, listitem)
|
||||||
result.listitem = listitem
|
result.listitem = listitem
|
||||||
|
@ -195,12 +192,7 @@ class PlaybackUtils():
|
||||||
# Would be using the direct path
|
# Would be using the direct path
|
||||||
log.debug("Adding contextmenu item for direct paths")
|
log.debug("Adding contextmenu item for direct paths")
|
||||||
if window('plex_%s.playmethod' % playurl) == "Transcode":
|
if window('plex_%s.playmethod' % playurl) == "Transcode":
|
||||||
window('plex_%s.playmethod' % playurl,
|
playutils.audioSubsPref(listitem, tryDecode(playurl))
|
||||||
clear=True)
|
|
||||||
playurl = tryEncode(playutils.audioSubsPref(
|
|
||||||
listitem, tryDecode(playurl)))
|
|
||||||
window('plex_%s.playmethod' % playurl,
|
|
||||||
value="Transcode")
|
|
||||||
api.CreateListItemFromPlexItem(listitem)
|
api.CreateListItemFromPlexItem(listitem)
|
||||||
api.set_playback_win_props(playurl, listitem)
|
api.set_playback_win_props(playurl, listitem)
|
||||||
api.set_listitem_artwork(listitem)
|
api.set_listitem_artwork(listitem)
|
||||||
|
@ -246,10 +238,7 @@ class PlaybackUtils():
|
||||||
# For transcoding only, ask for audio/subs pref
|
# For transcoding only, ask for audio/subs pref
|
||||||
if (window('plex_%s.playmethod' % playurl) == "Transcode" and
|
if (window('plex_%s.playmethod' % playurl) == "Transcode" and
|
||||||
not contextmenu_play):
|
not contextmenu_play):
|
||||||
window('plex_%s.playmethod' % playurl, clear=True)
|
playutils.audioSubsPref(listitem, tryDecode(playurl))
|
||||||
playurl = tryEncode(playutils.audioSubsPref(
|
|
||||||
listitem, tryDecode(playurl)))
|
|
||||||
window('plex_%s.playmethod' % playurl, value="Transcode")
|
|
||||||
|
|
||||||
listitem.setPath(playurl)
|
listitem.setPath(playurl)
|
||||||
api.set_playback_win_props(playurl, listitem)
|
api.set_playback_win_props(playurl, listitem)
|
||||||
|
|
|
@ -396,7 +396,8 @@ class Player(xbmc.Player):
|
||||||
'%s.type' % plex_item,
|
'%s.type' % plex_item,
|
||||||
'%s.runtime' % plex_item,
|
'%s.runtime' % plex_item,
|
||||||
'%s.playcount' % plex_item,
|
'%s.playcount' % plex_item,
|
||||||
'%s.playlistPosition' % plex_item
|
'%s.playlistPosition' % plex_item,
|
||||||
|
'%s.subtitle' % plex_item,
|
||||||
)
|
)
|
||||||
for item in cleanup:
|
for item in cleanup:
|
||||||
window(item, clear=True)
|
window(item, clear=True)
|
||||||
|
|
|
@ -3,14 +3,10 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from urllib import urlencode
|
from downloadutils import DownloadUtils
|
||||||
|
|
||||||
import xbmcgui
|
from utils import window, settings, tryEncode, language as lang, dialog
|
||||||
import xbmcvfs
|
|
||||||
|
|
||||||
from utils import window, settings, tryEncode, language as lang
|
|
||||||
import variables as v
|
import variables as v
|
||||||
|
|
||||||
import PlexAPI
|
import PlexAPI
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -26,6 +22,7 @@ class PlayUtils():
|
||||||
|
|
||||||
self.item = item
|
self.item = item
|
||||||
self.API = PlexAPI.API(item)
|
self.API = PlexAPI.API(item)
|
||||||
|
self.doUtils = DownloadUtils().downloadUrl
|
||||||
|
|
||||||
self.userid = window('currUserId')
|
self.userid = window('currUserId')
|
||||||
self.server = window('pms_server')
|
self.server = window('pms_server')
|
||||||
|
@ -62,7 +59,8 @@ class PlayUtils():
|
||||||
quality={
|
quality={
|
||||||
'maxVideoBitrate': self.get_bitrate(),
|
'maxVideoBitrate': self.get_bitrate(),
|
||||||
'videoResolution': self.get_resolution(),
|
'videoResolution': self.get_resolution(),
|
||||||
'videoQuality': '100'
|
'videoQuality': '100',
|
||||||
|
'mediaBufferSize': int(settings('kodi_video_cache'))/1024,
|
||||||
}))
|
}))
|
||||||
# Set playmethod property
|
# Set playmethod property
|
||||||
window('plex_%s.playmethod' % playurl, value="Transcode")
|
window('plex_%s.playmethod' % playurl, value="Transcode")
|
||||||
|
@ -115,28 +113,6 @@ class PlayUtils():
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
def fileExists(self):
|
|
||||||
|
|
||||||
if 'Path' not in self.item:
|
|
||||||
# File has no path defined in server
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Convert path to direct play
|
|
||||||
path = self.directPlay()
|
|
||||||
log.info("Verifying path: %s" % path)
|
|
||||||
|
|
||||||
if xbmcvfs.exists(path):
|
|
||||||
log.info("Path exists.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
elif ":" not in path:
|
|
||||||
log.info("Can't verify path, assumed linux. Still try to direct play.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
else:
|
|
||||||
log.info("Failed to find file.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def mustTranscode(self):
|
def mustTranscode(self):
|
||||||
"""
|
"""
|
||||||
Returns True if we need to transcode because
|
Returns True if we need to transcode because
|
||||||
|
@ -278,128 +254,150 @@ class PlayUtils():
|
||||||
return res[chosen]
|
return res[chosen]
|
||||||
|
|
||||||
def audioSubsPref(self, listitem, url, part=None):
|
def audioSubsPref(self, listitem, url, part=None):
|
||||||
dialog = xbmcgui.Dialog()
|
"""
|
||||||
# For transcoding only
|
For transcoding only
|
||||||
# Present the list of audio to select from
|
|
||||||
audioStreamsList = []
|
|
||||||
audioStreams = []
|
|
||||||
# audioStreamsChannelsList = []
|
|
||||||
subtitleStreamsList = []
|
|
||||||
subtitleStreams = ['1 No subtitles']
|
|
||||||
downloadableStreams = []
|
|
||||||
# selectAudioIndex = ""
|
|
||||||
selectSubsIndex = ""
|
|
||||||
playurlprefs = {}
|
|
||||||
|
|
||||||
# Set part where we're at
|
Called at the very beginning of play; used to change audio and subtitle
|
||||||
self.API.setPartNumber(part)
|
stream by a PUT request to the PMS
|
||||||
|
"""
|
||||||
|
# Set media and part where we're at
|
||||||
|
if self.API.mediastream is None:
|
||||||
|
self.API.getMediastreamNumber()
|
||||||
if part is None:
|
if part is None:
|
||||||
part = 0
|
part = 0
|
||||||
try:
|
try:
|
||||||
mediastreams = self.item[0][part]
|
mediastreams = self.item[self.API.mediastream][part]
|
||||||
except (TypeError, KeyError, IndexError):
|
except (TypeError, IndexError):
|
||||||
return url
|
log.error('Could not get media %s, part %s'
|
||||||
|
% (self.API.mediastream, part))
|
||||||
audioNum = 0
|
return
|
||||||
|
part_id = mediastreams.attrib['id']
|
||||||
|
audio_streams_list = []
|
||||||
|
audio_streams = []
|
||||||
|
subtitle_streams_list = []
|
||||||
|
# No subtitles as an option
|
||||||
|
subtitle_streams = [lang(39706)]
|
||||||
|
downloadable_streams = []
|
||||||
|
download_subs = []
|
||||||
|
# selectAudioIndex = ""
|
||||||
|
select_subs_index = ""
|
||||||
|
audio_numb = 0
|
||||||
# Remember 'no subtitles'
|
# Remember 'no subtitles'
|
||||||
subNum = 1
|
sub_num = 1
|
||||||
defaultSub = None
|
default_sub = None
|
||||||
|
|
||||||
for stream in mediastreams:
|
for stream in mediastreams:
|
||||||
# Since Plex returns all possible tracks together, have to sort
|
# Since Plex returns all possible tracks together, have to sort
|
||||||
# them.
|
# them.
|
||||||
index = stream.attrib.get('id')
|
index = stream.attrib.get('id')
|
||||||
type = stream.attrib.get('streamType')
|
typus = stream.attrib.get('streamType')
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
if type == "2":
|
if typus == "2":
|
||||||
codec = stream.attrib.get('codec')
|
codec = stream.attrib.get('codec')
|
||||||
channelLayout = stream.attrib.get('audioChannelLayout', "")
|
channelLayout = stream.attrib.get('audioChannelLayout', "")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
track = "%s %s - %s %s" % (audioNum+1, stream.attrib['language'], codec, channelLayout)
|
track = "%s %s - %s %s" % (audio_numb+1,
|
||||||
|
stream.attrib['language'],
|
||||||
|
codec,
|
||||||
|
channelLayout)
|
||||||
except:
|
except:
|
||||||
track = "%s 'unknown' - %s %s" % (audioNum+1, codec, channelLayout)
|
track = "%s %s - %s %s" % (audio_numb+1,
|
||||||
audioStreamsList.append(index)
|
lang(39707), # unknown
|
||||||
audioStreams.append(tryEncode(track))
|
codec,
|
||||||
audioNum += 1
|
channelLayout)
|
||||||
|
audio_streams_list.append(index)
|
||||||
|
audio_streams.append(tryEncode(track))
|
||||||
|
audio_numb += 1
|
||||||
|
|
||||||
# Subtitles
|
# Subtitles
|
||||||
elif type == "3":
|
elif typus == "3":
|
||||||
try:
|
try:
|
||||||
track = "%s %s" % (subNum+1, stream.attrib['language'])
|
track = "%s %s" % (sub_num+1, stream.attrib['language'])
|
||||||
except:
|
except KeyError:
|
||||||
track = "%s 'unknown' (%s)" % (subNum+1, stream.attrib.get('codec'))
|
track = "%s %s (%s)" % (sub_num+1,
|
||||||
|
lang(39707), # unknown
|
||||||
|
stream.attrib.get('codec'))
|
||||||
default = stream.attrib.get('default')
|
default = stream.attrib.get('default')
|
||||||
forced = stream.attrib.get('forced')
|
forced = stream.attrib.get('forced')
|
||||||
downloadable = stream.attrib.get('key')
|
downloadable = stream.attrib.get('key')
|
||||||
|
|
||||||
if default:
|
if default:
|
||||||
track = "%s - Default" % track
|
track = "%s - %s" % (track, lang(39708)) # Default
|
||||||
if forced:
|
if forced:
|
||||||
track = "%s - Forced" % track
|
track = "%s - %s" % (track, lang(39709)) # Forced
|
||||||
if downloadable:
|
if downloadable:
|
||||||
downloadableStreams.append(index)
|
# We do know the language - temporarily download
|
||||||
|
if 'language' in stream.attrib:
|
||||||
|
path = self.API.download_external_subtitles(
|
||||||
|
'{server}%s' % stream.attrib['key'],
|
||||||
|
"subtitle.%s.%s" % (stream.attrib['language'],
|
||||||
|
stream.attrib['codec']))
|
||||||
|
# We don't know the language - no need to download
|
||||||
|
else:
|
||||||
|
path = self.API.addPlexCredentialsToUrl(
|
||||||
|
"%s%s" % (self.server, stream.attrib['key']))
|
||||||
|
downloadable_streams.append(index)
|
||||||
|
download_subs.append(tryEncode(path))
|
||||||
else:
|
else:
|
||||||
track = "%s (burn-in)" % track
|
track = "%s (%s)" % (track, lang(39710)) # burn-in
|
||||||
if stream.attrib.get('selected') == '1' and downloadable:
|
if stream.attrib.get('selected') == '1' and downloadable:
|
||||||
# Only show subs without asking user if they can be
|
# Only show subs without asking user if they can be
|
||||||
# turned off
|
# turned off
|
||||||
defaultSub = index
|
default_sub = index
|
||||||
|
|
||||||
subtitleStreamsList.append(index)
|
subtitle_streams_list.append(index)
|
||||||
subtitleStreams.append(tryEncode(track))
|
subtitle_streams.append(tryEncode(track))
|
||||||
subNum += 1
|
sub_num += 1
|
||||||
|
|
||||||
if audioNum > 1:
|
if audio_numb > 1:
|
||||||
resp = dialog.select(lang(33013), audioStreams)
|
resp = dialog('select', lang(33013), audio_streams)
|
||||||
if resp > -1:
|
if resp > -1:
|
||||||
# User selected audio
|
# User selected some audio track
|
||||||
playurlprefs['audioStreamID'] = audioStreamsList[resp]
|
args = {
|
||||||
else: # User backed out of selection - let PMS decide
|
'audioStreamID': audio_streams_list[resp],
|
||||||
pass
|
'allParts': 1
|
||||||
else: # There's only one audiotrack.
|
}
|
||||||
playurlprefs['audioStreamID'] = audioStreamsList[0]
|
self.doUtils('{server}/library/parts/%s' % part_id,
|
||||||
|
action_type='PUT',
|
||||||
|
parameters=args)
|
||||||
|
|
||||||
# Add audio boost
|
if sub_num == 1:
|
||||||
playurlprefs['audioBoost'] = settings('audioBoost')
|
# No subtitles
|
||||||
|
return
|
||||||
|
|
||||||
selectSubsIndex = None
|
select_subs_index = None
|
||||||
if subNum > 1:
|
if (settings('pickPlexSubtitles') == 'true' and
|
||||||
if (settings('pickPlexSubtitles') == 'true' and
|
default_sub is not None):
|
||||||
defaultSub is not None):
|
log.info('Using default Plex subtitle: %s' % default_sub)
|
||||||
log.info('Using default Plex subtitle: %s' % defaultSub)
|
select_subs_index = default_sub
|
||||||
selectSubsIndex = defaultSub
|
else:
|
||||||
|
resp = dialog('select', lang(33014), subtitle_streams)
|
||||||
|
if resp > 0:
|
||||||
|
select_subs_index = subtitle_streams_list[resp-1]
|
||||||
else:
|
else:
|
||||||
resp = dialog.select(lang(33014), subtitleStreams)
|
# User selected no subtitles or backed out of dialog
|
||||||
if resp > 0:
|
select_subs_index = ''
|
||||||
selectSubsIndex = subtitleStreamsList[resp-1]
|
|
||||||
else:
|
|
||||||
# User selected no subtitles or backed out of dialog
|
|
||||||
playurlprefs["skipSubtitles"] = 1
|
|
||||||
if selectSubsIndex is not None:
|
|
||||||
# Load subtitles in the listitem if downloadable
|
|
||||||
if selectSubsIndex in downloadableStreams:
|
|
||||||
sub_url = self.API.addPlexHeadersToUrl(
|
|
||||||
"%s/library/streams/%s"
|
|
||||||
% (self.server, selectSubsIndex))
|
|
||||||
log.info("Downloadable sub: %s: %s"
|
|
||||||
% (selectSubsIndex, sub_url))
|
|
||||||
listitem.setSubtitles([tryEncode(sub_url)])
|
|
||||||
# Don't additionally burn in subtitles
|
|
||||||
playurlprefs["skipSubtitles"] = 1
|
|
||||||
else:
|
|
||||||
log.info('Need to burn in subtitle %s' % selectSubsIndex)
|
|
||||||
playurlprefs["subtitleStreamID"] = selectSubsIndex
|
|
||||||
playurlprefs["subtitleSize"] = settings('subtitleSize')
|
|
||||||
|
|
||||||
url += '&' + urlencode(playurlprefs)
|
log.debug('Adding external subtitles: %s' % download_subs)
|
||||||
|
# Enable Kodi to switch autonomously to downloadable subtitles
|
||||||
|
if download_subs:
|
||||||
|
listitem.setSubtitles(download_subs)
|
||||||
|
|
||||||
# Get number of channels for selected audio track
|
if select_subs_index in downloadable_streams:
|
||||||
# audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
|
for i, stream in enumerate(downloadable_streams):
|
||||||
# if audioChannels > 2:
|
if stream == select_subs_index:
|
||||||
# playurlprefs += "&AudioBitrate=384000"
|
# Set the correct subtitle
|
||||||
# else:
|
window('plex_%s.subtitle' % tryEncode(url), value=str(i))
|
||||||
# playurlprefs += "&AudioBitrate=192000"
|
break
|
||||||
|
# Don't additionally burn in subtitles
|
||||||
|
select_subs_index = ''
|
||||||
|
else:
|
||||||
|
window('plex_%s.subtitle' % tryEncode(url), value='None')
|
||||||
|
|
||||||
return url
|
args = {
|
||||||
|
'subtitleStreamID': select_subs_index,
|
||||||
|
'allParts': 1
|
||||||
|
}
|
||||||
|
self.doUtils('{server}/library/parts/%s' % part_id,
|
||||||
|
action_type='PUT',
|
||||||
|
parameters=args)
|
||||||
|
|
|
@ -548,6 +548,40 @@ def __setSubElement(element, subelement):
|
||||||
return answ
|
return answ
|
||||||
|
|
||||||
|
|
||||||
|
def get_advancessettings_xml_setting(node_list):
|
||||||
|
"""
|
||||||
|
Returns the etree element for nodelist (if it exists) and None if not set
|
||||||
|
|
||||||
|
node_list is a list of node names starting from the outside, ignoring the
|
||||||
|
outter advancedsettings. Example nodelist=['video', 'busydialogdelayms']
|
||||||
|
for the following xml would return the etree Element:
|
||||||
|
|
||||||
|
<busydialogdelayms>750</busydialogdelayms>
|
||||||
|
|
||||||
|
Example xml:
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<advancedsettings>
|
||||||
|
<video>
|
||||||
|
<busydialogdelayms>750</busydialogdelayms>
|
||||||
|
</video>
|
||||||
|
</advancedsettings>
|
||||||
|
"""
|
||||||
|
path = tryDecode(xbmc.translatePath("special://profile/"))
|
||||||
|
try:
|
||||||
|
xmlparse = etree.parse("%sadvancedsettings.xml" % path)
|
||||||
|
except:
|
||||||
|
log.debug('Could not parse advancedsettings.xml, returning None')
|
||||||
|
return
|
||||||
|
root = xmlparse.getroot()
|
||||||
|
|
||||||
|
for node in node_list:
|
||||||
|
root = root.find(node)
|
||||||
|
if root is None:
|
||||||
|
break
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
def advancedSettingsXML():
|
def advancedSettingsXML():
|
||||||
"""
|
"""
|
||||||
Kodi tweaks
|
Kodi tweaks
|
||||||
|
@ -595,12 +629,11 @@ def sourcesXML():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xmlparse = etree.parse(xmlpath)
|
xmlparse = etree.parse(xmlpath)
|
||||||
except: # Document is blank or missing
|
except: # Document is blank or missing
|
||||||
root = etree.Element('sources')
|
root = etree.Element('sources')
|
||||||
else:
|
else:
|
||||||
root = xmlparse.getroot()
|
root = xmlparse.getroot()
|
||||||
|
|
||||||
|
|
||||||
video = root.find('video')
|
video = root.find('video')
|
||||||
if video is None:
|
if video is None:
|
||||||
video = etree.SubElement(root, 'video')
|
video = etree.SubElement(root, 'video')
|
||||||
|
|
|
@ -27,7 +27,7 @@ ADDON_VERSION = _ADDON.getAddonInfo('version')
|
||||||
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
|
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
|
||||||
KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||||
KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion')
|
KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion')
|
||||||
KODI_PROFILE = xbmc.translatePath("special://profile")
|
KODI_PROFILE = tryDecode(xbmc.translatePath("special://profile"))
|
||||||
|
|
||||||
if xbmc.getCondVisibility('system.platform.osx'):
|
if xbmc.getCondVisibility('system.platform.osx'):
|
||||||
PLATFORM = "MacOSX"
|
PLATFORM = "MacOSX"
|
||||||
|
@ -95,6 +95,9 @@ DB_TEXTURE_PATH = tryDecode(xbmc.translatePath(
|
||||||
|
|
||||||
DB_PLEX_PATH = tryDecode(xbmc.translatePath("special://database/plex.db"))
|
DB_PLEX_PATH = tryDecode(xbmc.translatePath("special://database/plex.db"))
|
||||||
|
|
||||||
|
EXTERNAL_SUBTITLE_TEMP_PATH = tryDecode(xbmc.translatePath(
|
||||||
|
"special://profile/addon_data/%s/temp/" % ADDON_ID))
|
||||||
|
|
||||||
|
|
||||||
# Multiply Plex time by this factor to receive Kodi time
|
# Multiply Plex time by this factor to receive Kodi time
|
||||||
PLEX_TO_KODI_TIMEFACTOR = 1.0 / 1000.0
|
PLEX_TO_KODI_TIMEFACTOR = 1.0 / 1000.0
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
<setting id="bestQuality" type="bool" label="30541" default="false" />
|
<setting id="bestQuality" type="bool" label="30541" default="false" />
|
||||||
<setting id="bestTrailer" type="bool" label="30542" default="true" />
|
<setting id="bestTrailer" type="bool" label="30542" default="true" />
|
||||||
<setting id="force_transcode_pix" type="bool" label="30545" default="false" />
|
<setting id="force_transcode_pix" type="bool" label="30545" default="false" />
|
||||||
|
<setting id="kodi_video_cache" type="number" visible="false" default="20971520" />
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
<category label="30544"><!-- artwork -->
|
<category label="30544"><!-- artwork -->
|
||||||
|
|
|
@ -30,7 +30,7 @@ sys_path.append(_base_resource)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
from utils import settings, window, language as lang, dialog
|
from utils import settings, window, language as lang, dialog, tryDecode
|
||||||
from userclient import UserClient
|
from userclient import UserClient
|
||||||
import initialsetup
|
import initialsetup
|
||||||
from kodimonitor import KodiMonitor
|
from kodimonitor import KodiMonitor
|
||||||
|
@ -164,11 +164,12 @@ class Service():
|
||||||
counter = 0
|
counter = 0
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
|
|
||||||
if window('plex_kodiProfile') != kodiProfile:
|
if tryDecode(window('plex_kodiProfile')) != kodiProfile:
|
||||||
# Profile change happened, terminate this thread and others
|
# Profile change happened, terminate this thread and others
|
||||||
log.warn("Kodi profile was: %s and changed to: %s. "
|
log.warn("Kodi profile was: %s and changed to: %s. "
|
||||||
"Terminating old PlexKodiConnect thread."
|
"Terminating old PlexKodiConnect thread."
|
||||||
% (kodiProfile, window('plex_kodiProfile')))
|
% (kodiProfile,
|
||||||
|
tryDecode(window('plex_kodiProfile'))))
|
||||||
break
|
break
|
||||||
|
|
||||||
# Before proceeding, need to make sure:
|
# Before proceeding, need to make sure:
|
||||||
|
|
Loading…
Reference in a new issue