Merge branch 'develop' into translations

This commit is contained in:
tomkat83 2017-05-01 09:05:07 +02:00
commit 1808d0b609
13 changed files with 65 additions and 345 deletions

View file

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.7" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="1.7.8" 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" />
@ -50,7 +50,14 @@
<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.7 <news>version 1.7.8 (beta only)
- 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
- Move translations to new strings.po system
- Fix some TypeErrors
- Some code refactoring
version 1.7.7
- Chinese Traditional, thanks @old2tan - Chinese Traditional, thanks @old2tan
- Chinese Simplified, thanks @everdream - Chinese Simplified, thanks @everdream
- Browse by folder: also sort by Date Added - Browse by folder: also sort by Date Added
@ -60,6 +67,37 @@ version 1.7.6
- Hotfix: Revert Cache missing artwork on PKC startup. This should help with slow PKC startup, videos not being started, lagging PKC, etc. - Hotfix: Revert Cache missing artwork on PKC startup. This should help with slow PKC startup, videos not being started, lagging PKC, etc.
version 1.7.5 version 1.7.5
- Dutch translation, thanks @mvanbaak</news> - Dutch translation, thanks @mvanbaak
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 -> ...
- Fix playback error popup when using Alexa
- New Italian translations, thanks @nikkux, @chicco83
- Update translations
- Rewire Kodi ListItem stuff
- Fix TypeError for setting ListItem streams
- Fix Kodi setContent for images
- Fix AttributeError due to missing Kodi sort methods
version 1.7.3 (beta only)
- Fix KeyError for channels if no media streams
- Move plex node navigation, playback to main thread
- Fix TypeError for malformed browsing xml
- Fix IndexError if we can't get a valid xml from PMS
- Pass 'None' instead of empty string in url args
version 1.7.2
- Fix for some channels not starting playback
version 1.7.1
- Fix Alexa not doing anything
version 1.7.0
- Amazon Alexa support! Be sure to check the Plex Alexa forum first if you encounter issues; there are still many bugs completely unrelated to PKC
- Plex Channels!
- Browse video nodes by folder/path
- Fix IndexError for playqueues
- Update translations
- Code optimization</news>
</extension> </extension>
</addon> </addon>

View file

@ -1,3 +1,10 @@
version 1.7.8 (beta only)
- 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
- Move translations to new strings.po system
- Fix some TypeErrors
- Some code refactoring
version 1.7.7 version 1.7.7
- Chinese Traditional, thanks @old2tan - Chinese Traditional, thanks @old2tan
- Chinese Simplified, thanks @everdream - Chinese Simplified, thanks @everdream

View file

@ -123,9 +123,7 @@ class Main():
elif mode in ('manualsync', 'repair'): elif mode in ('manualsync', 'repair'):
if window('plex_online') != 'true': if window('plex_online') != 'true':
# Server is not online, do not run the sync # Server is not online, do not run the sync
dialog('ok', dialog('ok', lang(29999), lang(39205))
heading=lang(29999),
message=lang(39205))
log.error('Not connected to a PMS.') log.error('Not connected to a PMS.')
else: else:
if mode == 'repair': if mode == 'repair':

View file

@ -1408,7 +1408,7 @@ msgid "Playback Mode"
msgstr "" msgstr ""
msgctxt "#39028" msgctxt "#39028"
msgid "CAUTION! If you choose \"Native\" mode , you might loose access to certain Plex features such as: Plex trailers and transcoding options. ALL Plex shares need to use direct paths (e.g. smb://myNAS/mymovie.mkv or \\myNAS/mymovie.mkv)!" msgid "CAUTION! If you choose \"Native\" mode , you might loose access to certain Plex features such as: Plex trailers and transcoding options. ALL Plex shares need to use direct paths (e.g. smb://myNAS/mymovie.mkv or \\\\myNAS/mymovie.mkv)!"
msgstr "" msgstr ""
msgctxt "#39029" msgctxt "#39029"
@ -1428,15 +1428,15 @@ msgid "Please verify the path. You may need to verify your network credentials i
msgstr "" msgstr ""
msgctxt "#39033" msgctxt "#39033"
msgid "Transform Plex UNC library paths \\myNas\mymovie.mkv automatically to smb paths, smb://myNas/mymovie.mkv? (recommended)" msgid "Transform Plex UNC library paths \\\\myNas\\mymovie.mkv automatically to smb paths, smb://myNas/mymovie.mkv? (recommended)"
msgstr "" msgstr ""
msgctxt "#39034" msgctxt "#39034"
msgid "Replace Plex UNC paths \\myNas with smb://myNas" msgid "Replace Plex UNC paths \\\\myNas with smb://myNas"
msgstr "" msgstr ""
msgctxt "#39035" msgctxt "#39035"
msgid "Replace Plex paths /volume1/media or \\myserver\media with custom SMB paths smb://NAS/mystuff" msgid "Replace Plex paths /volume1/media or \\\\myserver\\media with custom SMB paths smb://NAS/mystuff"
msgstr "" msgstr ""
msgctxt "#39037" msgctxt "#39037"

View file

@ -63,10 +63,6 @@ REGEX_TVDB = re_compile(r'''thetvdb:\/\/(.+?)\?''')
class PlexAPI(): class PlexAPI():
# CONSTANTS
# Timeout for POST/GET commands, I guess in seconds
timeout = 10
def __init__(self): def __init__(self):
self.g_PMS = {} self.g_PMS = {}
self.doUtils = DownloadUtils().downloadUrl self.doUtils = DownloadUtils().downloadUrl
@ -259,7 +255,6 @@ class PlexAPI():
""" """
Checks connection to a Plex server, available at url. Can also be used Checks connection to a Plex server, available at url. Can also be used
to check for connection with plex.tv. to check for connection with plex.tv.
Will check up to 3x until reply with False
Override SSL to skip the check by setting verifySSL=False Override SSL to skip the check by setting verifySSL=False
if 'None', SSL will be checked (standard requests setting) if 'None', SSL will be checked (standard requests setting)
@ -288,14 +283,13 @@ class PlexAPI():
url = url + '/library/onDeck' url = url + '/library/onDeck'
log.debug("Checking connection to server %s with verifySSL=%s" log.debug("Checking connection to server %s with verifySSL=%s"
% (url, verifySSL)) % (url, verifySSL))
# Check up to 3 times before giving up
count = 0 count = 0
while count < 1: while count < 1:
answer = self.doUtils(url, answer = self.doUtils(url,
authenticate=False, authenticate=False,
headerOptions=headerOptions, headerOptions=headerOptions,
verifySSL=verifySSL, verifySSL=verifySSL,
timeout=4) timeout=10)
if answer is None: if answer is None:
log.debug("Could not connect to %s" % url) log.debug("Could not connect to %s" % url)
count += 1 count += 1

View file

@ -377,7 +377,7 @@ def GetMachineIdentifier(url):
xml = downloadutils.DownloadUtils().downloadUrl('%s/identity' % url, xml = downloadutils.DownloadUtils().downloadUrl('%s/identity' % url,
authenticate=False, authenticate=False,
verifySSL=False, verifySSL=False,
timeout=4) timeout=10)
try: try:
machineIdentifier = xml.attrib['machineIdentifier'] machineIdentifier = xml.attrib['machineIdentifier']
except (AttributeError, KeyError): except (AttributeError, KeyError):

View file

@ -1,257 +0,0 @@
# -*- coding: utf-8 -*-
##################################################################################################
import json
import requests
import logging
import clientinfo
from utils import window
##################################################################################################
# Disable requests logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class ConnectUtils():
# Borg - multiple instances, shared state
_shared_state = {}
clientInfo = clientinfo.ClientInfo()
# Requests session
c = None
timeout = 30
def __init__(self):
self.__dict__ = self._shared_state
def setUserId(self, userId):
# Reserved for userclient only
self.userId = userId
log.debug("Set connect userId: %s" % userId)
def setServer(self, server):
# Reserved for userclient only
self.server = server
log.debug("Set connect server: %s" % server)
def setToken(self, token):
# Reserved for userclient only
self.token = token
log.debug("Set connect token: %s" % token)
def startSession(self):
self.deviceId = self.clientInfo.getDeviceId()
# User is identified from this point
# Attach authenticated header to the session
verify = False
header = self.getHeader()
# If user enabled host certificate verification
try:
verify = self.sslverify
if self.sslclient is not None:
verify = self.sslclient
except:
log.info("Could not load SSL settings.")
# Start session
self.c = requests.Session()
self.c.headers = header
self.c.verify = verify
# Retry connections to the server
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
log.info("Requests session started on: %s" % self.server)
def stopSession(self):
try:
self.c.close()
except Exception:
log.warn("Requests session could not be terminated")
def getHeader(self, authenticate=True):
version = self.clientInfo.getVersion()
if not authenticate:
# If user is not authenticated
header = {
'X-Application': "Kodi/%s" % version,
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': "application/json"
}
log.info("Header: %s" % header)
else:
token = self.token
# Attached to the requests session
header = {
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': "application/json",
'X-Application': "Kodi/%s" % version,
'X-Connect-UserToken': token
}
log.info("Header: %s" % header)
return header
def doUrl(self, url, data=None, postBody=None, rtype="GET",
parameters=None, authenticate=True, timeout=None):
log.debug("=== ENTER connectUrl ===")
default_link = ""
if timeout is None:
timeout = self.timeout
# Get requests session
try:
# If connect user is authenticated
if authenticate:
try:
c = self.c
# Replace for the real values
url = url.replace("{server}", self.server)
url = url.replace("{UserId}", self.userId)
# Prepare request
if rtype == "GET":
r = c.get(url, json=postBody, params=parameters, timeout=timeout)
elif rtype == "POST":
r = c.post(url, data=data, timeout=timeout)
elif rtype == "DELETE":
r = c.delete(url, json=postBody, timeout=timeout)
except AttributeError:
# request session does not exists
self.server = "https://connect.emby.media/service"
self.userId = window('embyco_currUser')
self.token = window('embyco_accessToken%s' % self.userId)
header = self.getHeader()
verifyssl = False
# If user enables ssl verification
try:
verifyssl = self.sslverify
if self.sslclient is not None:
verifyssl = self.sslclient
except AttributeError:
pass
# Prepare request
if rtype == "GET":
r = requests.get(url,
json=postBody,
params=parameters,
headers=header,
timeout=timeout,
verify=verifyssl)
elif rtype == "POST":
r = requests.post(url,
data=data,
headers=header,
timeout=timeout,
verify=verifyssl)
# If user is not authenticated
else:
header = self.getHeader(authenticate=False)
verifyssl = False
# If user enables ssl verification
try:
verifyssl = self.sslverify
if self.sslclient is not None:
verifyssl = self.sslclient
except AttributeError:
pass
# Prepare request
if rtype == "GET":
r = requests.get(url,
json=postBody,
params=parameters,
headers=header,
timeout=timeout,
verify=verifyssl)
elif rtype == "POST":
r = requests.post(url,
data=data,
headers=header,
timeout=timeout,
verify=verifyssl)
##### THE RESPONSE #####
log.info(r.url)
log.info(r)
if r.status_code == 204:
# No body in the response
log.info("====== 204 Success ======")
elif r.status_code == requests.codes.ok:
try:
# UNICODE - JSON object
r = r.json()
log.info("====== 200 Success ======")
log.info("Response: %s" % r)
return r
except:
if r.headers.get('content-type') != "text/html":
log.info("Unable to convert the response for: %s" % url)
else:
r.raise_for_status()
##### EXCEPTIONS #####
except requests.exceptions.ConnectionError as e:
# Make the addon aware of status
pass
except requests.exceptions.ConnectTimeout as e:
log.warn("Server timeout at: %s" % url)
except requests.exceptions.HTTPError as e:
if r.status_code == 401:
# Unauthorized
pass
elif r.status_code in (301, 302):
# Redirects
pass
elif r.status_code == 400:
# Bad requests
pass
except requests.exceptions.SSLError as e:
log.warn("Invalid SSL certificate for: %s" % url)
except requests.exceptions.RequestException as e:
log.warn("Unknown error connecting to: %s" % url)
return default_link

View file

@ -34,11 +34,11 @@ class DownloadUtils():
connectionAttempts = 2 connectionAttempts = 2
# How many 401 returns before declaring unauthorized? # How many 401 returns before declaring unauthorized?
unauthorizedAttempts = 2 unauthorizedAttempts = 2
# How long should we wait for an answer from the
timeout = 30.0
def __init__(self): def __init__(self):
self.__dict__ = self._shared_state self.__dict__ = self._shared_state
# Requests session
self.timeout = 30.0
def setUsername(self, username): def setUsername(self, username):
""" """

View file

@ -768,7 +768,7 @@ def channels():
xml = downloadutils.DownloadUtils().downloadUrl('{server}/channels/all') xml = downloadutils.DownloadUtils().downloadUrl('{server}/channels/all')
try: try:
xml[0].attrib xml[0].attrib
except (ValueError, AttributeError, IndexError): except (ValueError, AttributeError, IndexError, TypeError):
log.error('Could not download Plex Channels') log.error('Could not download Plex Channels')
return xbmcplugin.endOfDirectory(HANDLE, False) return xbmcplugin.endOfDirectory(HANDLE, False)

View file

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
#################################################################################################
import logging
import threading
import requests
# Disable annoying requests warnings
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
#################################################################################################
log = logging.getLogger("PLEX."+__name__)
#################################################################################################
class ImageCacheThread(threading.Thread):
url_to_process = None
is_finished = False
xbmc_host = ""
xbmc_port = ""
xbmc_username = ""
xbmc_password = ""
def __init__(self):
threading.Thread.__init__(self)
def set_url(self, url):
self.url_to_process = url
def set_host(self, host, port):
self.xbmc_host = host
self.xbmc_port = port
def set_auth(self, username, password):
self.xbmc_username = username
self.xbmc_password = password
def run(self):
try:
response = requests.head(
url=("http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, self.url_to_process)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(5, 5))
# We don't need the result
except Exception:
pass
self.is_finished = True

View file

@ -407,11 +407,9 @@ class InitialSetup():
# If a Plex server IP has already been set # If a Plex server IP has already been set
# return only if the right machine identifier is found # return only if the right machine identifier is found
getNewIP = False
if self.server: if self.server:
log.info("PMS is already set: %s. Checking now..." % self.server) log.info("PMS is already set: %s. Checking now..." % self.server)
getNewIP = not self.CheckPMS() if self.CheckPMS():
if getNewIP is False:
log.info("Using PMS %s with machineIdentifier %s" log.info("Using PMS %s with machineIdentifier %s"
% (self.server, self.serverid)) % (self.server, self.serverid))
self._write_PMS_settings(self.server, self.pms_token) self._write_PMS_settings(self.server, self.pms_token)

View file

@ -341,7 +341,7 @@ class Movies(Items):
WHERE idMovie = ? WHERE idMovie = ?
''' '''
kodicursor.execute(query, (title, plot, shortplot, tagline, kodicursor.execute(query, (title, plot, shortplot, tagline,
votecount, rating_id, writer, year, imdb, sorttitle, votecount, rating_id, writer, year, uniqueid, sorttitle,
runtime, mpaa, genre, director, title, studio, trailer, runtime, mpaa, genre, director, title, studio, trailer,
country, playurl, pathid, fileid, year, country, playurl, pathid, fileid, year,
userdata['UserRating'], movieid)) userdata['UserRating'], movieid))
@ -372,7 +372,8 @@ class Movies(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(), uniqueid = self.kodi_db.create_entry_uniqueid()
self.kodi_db.add_uniqueid(uniqueid,
movieid, movieid,
v.KODI_TYPE_MOVIE, v.KODI_TYPE_MOVIE,
imdb, imdb,
@ -386,8 +387,8 @@ class Movies(Items):
''' '''
kodicursor.execute(query, (movieid, fileid, title, plot, kodicursor.execute(query, (movieid, fileid, title, plot,
shortplot, tagline, votecount, rating_id, writer, year, shortplot, tagline, votecount, rating_id, writer, year,
imdb, sorttitle, runtime, mpaa, genre, director, title, uniqueid, sorttitle, runtime, mpaa, genre, director,
studio, trailer, country, playurl, pathid, year, title, studio, trailer, country, playurl, pathid, year,
userdata['UserRating'])) userdata['UserRating']))
else: else:
query = ''' query = '''