mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-10 23:42:37 -07:00
Update server connection code
This commit is contained in:
parent
29632b0805
commit
15faccfa2f
16 changed files with 530 additions and 535 deletions
|
@ -653,7 +653,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_token">PMS Token</label>
|
<label for="pms_token">Plex.tv Account Token</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
|
@ -36,12 +36,14 @@ import activity_handler
|
||||||
import activity_pinger
|
import activity_pinger
|
||||||
import config
|
import config
|
||||||
import database
|
import database
|
||||||
|
import libraries
|
||||||
import logger
|
import logger
|
||||||
import mobile_app
|
import mobile_app
|
||||||
import notification_handler
|
import notification_handler
|
||||||
import notifiers
|
import notifiers
|
||||||
import plextv
|
import plextv
|
||||||
import pmsconnect
|
import pmsconnect
|
||||||
|
import users
|
||||||
import versioncheck
|
import versioncheck
|
||||||
import plexpy.config
|
import plexpy.config
|
||||||
|
|
||||||
|
@ -213,16 +215,15 @@ def initialize(config_file):
|
||||||
|
|
||||||
# Get the real PMS urls for SSL and remote access
|
# Get the real PMS urls for SSL and remote access
|
||||||
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
||||||
plextv.get_real_pms_url()
|
plextv.get_server_resources()
|
||||||
pmsconnect.get_server_friendly_name()
|
|
||||||
|
|
||||||
# Refresh the users list on startup
|
# Refresh the users list on startup
|
||||||
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
||||||
plextv.refresh_users()
|
users.refresh_users()
|
||||||
|
|
||||||
# Refresh the libraries list on startup
|
# Refresh the libraries list on startup
|
||||||
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN and CONFIG.REFRESH_LIBRARIES_ON_STARTUP:
|
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN and CONFIG.REFRESH_LIBRARIES_ON_STARTUP:
|
||||||
pmsconnect.refresh_libraries()
|
libraries.refresh_libraries()
|
||||||
|
|
||||||
# Store the original umask
|
# Store the original umask
|
||||||
UMASK = os.umask(0)
|
UMASK = os.umask(0)
|
||||||
|
@ -323,14 +324,8 @@ def initialize_scheduler():
|
||||||
hours=backup_hours, minutes=0, seconds=0, args=(True, True))
|
hours=backup_hours, minutes=0, seconds=0, args=(True, True))
|
||||||
|
|
||||||
if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
||||||
#schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
|
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
|
||||||
# hours=0, minutes=0, seconds=1)
|
|
||||||
#schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
|
||||||
# hours=0, minutes=0, seconds=monitor_seconds * bool(CONFIG.NOTIFY_RECENTLY_ADDED))
|
|
||||||
schedule_job(plextv.get_real_pms_url, 'Refresh Plex server URLs',
|
|
||||||
hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0)
|
hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0)
|
||||||
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex server name',
|
|
||||||
hours=12, minutes=0, seconds=0)
|
|
||||||
|
|
||||||
schedule_job(activity_pinger.check_server_access, 'Check for Plex remote access',
|
schedule_job(activity_pinger.check_server_access, 'Check for Plex remote access',
|
||||||
hours=0, minutes=0, seconds=60 * bool(CONFIG.MONITOR_REMOTE_ACCESS))
|
hours=0, minutes=0, seconds=60 * bool(CONFIG.MONITOR_REMOTE_ACCESS))
|
||||||
|
@ -341,9 +336,9 @@ def initialize_scheduler():
|
||||||
user_hours = CONFIG.REFRESH_USERS_INTERVAL if 1 <= CONFIG.REFRESH_USERS_INTERVAL <= 24 else 12
|
user_hours = CONFIG.REFRESH_USERS_INTERVAL if 1 <= CONFIG.REFRESH_USERS_INTERVAL <= 24 else 12
|
||||||
library_hours = CONFIG.REFRESH_LIBRARIES_INTERVAL if 1 <= CONFIG.REFRESH_LIBRARIES_INTERVAL <= 24 else 12
|
library_hours = CONFIG.REFRESH_LIBRARIES_INTERVAL if 1 <= CONFIG.REFRESH_LIBRARIES_INTERVAL <= 24 else 12
|
||||||
|
|
||||||
schedule_job(plextv.refresh_users, 'Refresh users list',
|
schedule_job(users.refresh_users, 'Refresh users list',
|
||||||
hours=user_hours, minutes=0, seconds=0)
|
hours=user_hours, minutes=0, seconds=0)
|
||||||
schedule_job(pmsconnect.refresh_libraries, 'Refresh libraries list',
|
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
|
||||||
hours=library_hours, minutes=0, seconds=0)
|
hours=library_hours, minutes=0, seconds=0)
|
||||||
|
|
||||||
schedule_job(activity_pinger.check_server_response, 'Check server response',
|
schedule_job(activity_pinger.check_server_response, 'Check server response',
|
||||||
|
@ -351,9 +346,7 @@ def initialize_scheduler():
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Cancel all jobs
|
# Cancel all jobs
|
||||||
schedule_job(plextv.get_real_pms_url, 'Refresh Plex server URLs',
|
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
|
||||||
hours=0, minutes=0, seconds=0)
|
|
||||||
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex server name',
|
|
||||||
hours=0, minutes=0, seconds=0)
|
hours=0, minutes=0, seconds=0)
|
||||||
|
|
||||||
schedule_job(activity_pinger.check_server_access, 'Check for Plex remote access',
|
schedule_job(activity_pinger.check_server_access, 'Check for Plex remote access',
|
||||||
|
@ -361,9 +354,9 @@ def initialize_scheduler():
|
||||||
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
||||||
hours=0, minutes=0, seconds=0)
|
hours=0, minutes=0, seconds=0)
|
||||||
|
|
||||||
schedule_job(plextv.refresh_users, 'Refresh users list',
|
schedule_job(users.refresh_users, 'Refresh users list',
|
||||||
hours=0, minutes=0, seconds=0)
|
hours=0, minutes=0, seconds=0)
|
||||||
schedule_job(pmsconnect.refresh_libraries, 'Refresh libraries list',
|
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
|
||||||
hours=0, minutes=0, seconds=0)
|
hours=0, minutes=0, seconds=0)
|
||||||
|
|
||||||
# Schedule job to reconnect websocket
|
# Schedule job to reconnect websocket
|
||||||
|
|
|
@ -278,7 +278,6 @@ def check_server_response():
|
||||||
|
|
||||||
|
|
||||||
def check_server_access():
|
def check_server_access():
|
||||||
|
|
||||||
with monitor_lock:
|
with monitor_lock:
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
server_response = pms_connect.get_server_response()
|
server_response = pms_connect.get_server_response()
|
||||||
|
@ -287,7 +286,7 @@ def check_server_access():
|
||||||
|
|
||||||
# Check for remote access
|
# Check for remote access
|
||||||
if server_response:
|
if server_response:
|
||||||
|
|
||||||
mapping_state = server_response['mapping_state']
|
mapping_state = server_response['mapping_state']
|
||||||
mapping_error = server_response['mapping_error']
|
mapping_error = server_response['mapping_error']
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,10 @@ import xmltodict
|
||||||
import plexpy
|
import plexpy
|
||||||
import config
|
import config
|
||||||
import database
|
import database
|
||||||
|
import libraries
|
||||||
import logger
|
import logger
|
||||||
import mobile_app
|
import mobile_app
|
||||||
import plextv
|
import users
|
||||||
import pmsconnect
|
|
||||||
|
|
||||||
|
|
||||||
class API2:
|
class API2:
|
||||||
|
@ -345,14 +345,14 @@ class API2:
|
||||||
|
|
||||||
def refresh_libraries_list(self, **kwargs):
|
def refresh_libraries_list(self, **kwargs):
|
||||||
""" Refresh the Tautulli libraries list."""
|
""" Refresh the Tautulli libraries list."""
|
||||||
data = pmsconnect.refresh_libraries()
|
data = libraries.refresh_libraries()
|
||||||
self._api_result_type = 'success' if data else 'error'
|
self._api_result_type = 'success' if data else 'error'
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def refresh_users_list(self, **kwargs):
|
def refresh_users_list(self, **kwargs):
|
||||||
""" Refresh the Tautulli users list."""
|
""" Refresh the Tautulli users list."""
|
||||||
data = plextv.refresh_users()
|
data = users.refresh_users()
|
||||||
self._api_result_type = 'success' if data else 'error'
|
self._api_result_type = 'success' if data else 'error'
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -140,10 +140,10 @@ SCHEDULER_LIST = ['Check GitHub for updates',
|
||||||
'Check for recently added items',
|
'Check for recently added items',
|
||||||
'Check for Plex updates',
|
'Check for Plex updates',
|
||||||
'Check for Plex remote access',
|
'Check for Plex remote access',
|
||||||
|
'Check server response',
|
||||||
'Refresh users list',
|
'Refresh users list',
|
||||||
'Refresh libraries list',
|
'Refresh libraries list',
|
||||||
'Refresh Plex server URLs',
|
'Refresh Plex server URLs',
|
||||||
'Refresh Plex server name',
|
|
||||||
'Backup Tautulli database',
|
'Backup Tautulli database',
|
||||||
'Backup Tautulli config'
|
'Backup Tautulli config'
|
||||||
]
|
]
|
||||||
|
|
|
@ -45,6 +45,7 @@ _CONFIG_DEFINITIONS = {
|
||||||
'PLEXWATCH_DATABASE': (str, 'PlexWatch', ''),
|
'PLEXWATCH_DATABASE': (str, 'PlexWatch', ''),
|
||||||
'PMS_IDENTIFIER': (str, 'PMS', ''),
|
'PMS_IDENTIFIER': (str, 'PMS', ''),
|
||||||
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
||||||
|
'PMS_IS_CLOUD': (int, 'PMS', 0),
|
||||||
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
||||||
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
||||||
'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000),
|
'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000),
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# This file is part of Tautulli.
|
# This file is part of PlexPy.
|
||||||
#
|
#
|
||||||
# Tautulli is free software: you can redistribute it and/or modify
|
# PlexPy is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# Tautulli is distributed in the hope that it will be useful,
|
# PlexPy is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from httplib import HTTPSConnection
|
from functools import partial
|
||||||
from httplib import HTTPConnection
|
from multiprocessing.dummy import Pool as ThreadPool
|
||||||
import ssl
|
from urlparse import urljoin
|
||||||
|
|
||||||
|
import certifi
|
||||||
|
from requests.packages import urllib3
|
||||||
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import helpers
|
import helpers
|
||||||
|
@ -30,94 +34,144 @@ class HTTPHandler(object):
|
||||||
Retrieve data from Plex Server
|
Retrieve data from Plex Server
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host, port, token, ssl_verify=True):
|
def __init__(self, urls, token=None, timeout=10, ssl_verify=True):
|
||||||
self.host = host
|
if isinstance(urls, basestring):
|
||||||
self.port = str(port)
|
self.urls = urls.split() or urls.split(',')
|
||||||
|
else:
|
||||||
|
self.urls = urls
|
||||||
|
|
||||||
self.token = token
|
self.token = token
|
||||||
|
if self.token:
|
||||||
|
self.headers = {'X-Plex-Token': self.token}
|
||||||
|
else:
|
||||||
|
self.headers = {}
|
||||||
|
|
||||||
|
self.timeout = timeout
|
||||||
self.ssl_verify = ssl_verify
|
self.ssl_verify = ssl_verify
|
||||||
|
|
||||||
"""
|
self.valid_request_types = ('GET', 'POST', 'PUT', 'DELETE')
|
||||||
Handle the HTTP requests.
|
|
||||||
|
|
||||||
Output: object
|
|
||||||
"""
|
|
||||||
def make_request(self,
|
def make_request(self,
|
||||||
uri=None, proto='HTTP',
|
uri=None,
|
||||||
request_type='GET',
|
|
||||||
headers=None,
|
headers=None,
|
||||||
|
request_type='GET',
|
||||||
output_format='raw',
|
output_format='raw',
|
||||||
return_type=False,
|
return_type=False,
|
||||||
no_token=False,
|
no_token=False,
|
||||||
timeout=None):
|
timeout=None,
|
||||||
|
callback=None):
|
||||||
|
"""
|
||||||
|
Handle the HTTP requests.
|
||||||
|
|
||||||
if timeout is None:
|
Output: list
|
||||||
timeout = plexpy.CONFIG.PMS_TIMEOUT
|
"""
|
||||||
|
|
||||||
valid_request_types = ['GET', 'POST', 'PUT', 'DELETE']
|
self.uri = uri
|
||||||
|
self.request_type = request_type.upper()
|
||||||
|
self.output_format = output_format.lower()
|
||||||
|
self.return_type = return_type
|
||||||
|
self.callback = callback
|
||||||
|
self.timeout = timeout or self.timeout
|
||||||
|
|
||||||
if request_type.upper() not in valid_request_types:
|
if self.request_type not in self.valid_request_types:
|
||||||
logger.debug(u"HTTP request made but unsupported request type given.")
|
logger.debug(u"HTTP request made but unsupported request type given.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if uri:
|
if uri:
|
||||||
if proto.upper() == 'HTTPS':
|
request_urls = [urljoin(url, self.uri) for url in self.urls]
|
||||||
if not self.ssl_verify and hasattr(ssl, '_create_unverified_context'):
|
|
||||||
context = ssl._create_unverified_context()
|
|
||||||
handler = HTTPSConnection(host=self.host, port=self.port, timeout=timeout, context=context)
|
|
||||||
logger.warn(u"Tautulli HTTP Handler :: Unverified HTTPS request made. This connection is not secure.")
|
|
||||||
else:
|
|
||||||
handler = HTTPSConnection(host=self.host, port=self.port, timeout=timeout)
|
|
||||||
else:
|
|
||||||
handler = HTTPConnection(host=self.host, port=self.port, timeout=timeout)
|
|
||||||
|
|
||||||
if not no_token:
|
if no_token and headers:
|
||||||
if headers:
|
self.headers = headers
|
||||||
headers.update({'X-Plex-Token': self.token})
|
elif headers:
|
||||||
else:
|
self.headers.update(headers)
|
||||||
headers = {'X-Plex-Token': self.token}
|
|
||||||
|
|
||||||
try:
|
responses = []
|
||||||
if headers:
|
for r in self._http_requests_pool(request_urls):
|
||||||
handler.request(request_type, uri, headers=headers)
|
responses.append(r)
|
||||||
else:
|
|
||||||
handler.request(request_type, uri)
|
|
||||||
response = handler.getresponse()
|
|
||||||
request_status = response.status
|
|
||||||
request_content = response.read()
|
|
||||||
content_type = response.getheader('content-type')
|
|
||||||
except IOError as e:
|
|
||||||
logger.warn(u"Failed to access uri endpoint %s with error %s" % (uri, e))
|
|
||||||
return None
|
|
||||||
except Exception as e:
|
|
||||||
logger.warn(u"Failed to access uri endpoint %s. Is your server maybe accepting SSL connections only? %s" % (uri, e))
|
|
||||||
return None
|
|
||||||
except:
|
|
||||||
logger.warn(u"Failed to access uri endpoint %s with Uncaught exception." % uri)
|
|
||||||
return None
|
|
||||||
|
|
||||||
if request_status in (200, 201):
|
return responses[0]
|
||||||
try:
|
|
||||||
if output_format == 'dict':
|
|
||||||
output = helpers.convert_xml_to_dict(request_content)
|
|
||||||
elif output_format == 'json':
|
|
||||||
output = helpers.convert_xml_to_json(request_content)
|
|
||||||
elif output_format == 'xml':
|
|
||||||
output = helpers.parse_xml(request_content)
|
|
||||||
else:
|
|
||||||
output = request_content
|
|
||||||
|
|
||||||
if return_type:
|
|
||||||
return output, content_type
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.warn(u"Failed format response from uri %s to %s error %s" % (uri, output_format, e))
|
|
||||||
return None
|
|
||||||
|
|
||||||
else:
|
|
||||||
logger.warn(u"Failed to access uri endpoint %s. Status code %r" % (uri, request_status))
|
|
||||||
return None
|
|
||||||
else:
|
else:
|
||||||
logger.debug(u"HTTP request made but no enpoint given.")
|
logger.debug(u"HTTP request made but no enpoint given.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _http_requests_pool(self, urls, workers=10, chunk=None):
|
||||||
|
"""Generator function to request urls in chunks"""
|
||||||
|
# From cpython
|
||||||
|
if chunk is None:
|
||||||
|
chunk, extra = divmod(len(urls), workers * 4)
|
||||||
|
if extra:
|
||||||
|
chunk += 1
|
||||||
|
if len(urls) == 0:
|
||||||
|
chunk = 0
|
||||||
|
|
||||||
|
if self.ssl_verify:
|
||||||
|
session = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where())
|
||||||
|
else:
|
||||||
|
urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
|
session = urllib3.PoolManager()
|
||||||
|
part = partial(self._http_requests_urllib3, session=session)
|
||||||
|
|
||||||
|
if len(urls) == 1:
|
||||||
|
yield part(urls[0])
|
||||||
|
else:
|
||||||
|
pool = ThreadPool(workers)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for work in pool.imap_unordered(part, urls, chunk):
|
||||||
|
yield work
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(u"Failed to yield request: %s" % e)
|
||||||
|
finally:
|
||||||
|
pool.close()
|
||||||
|
pool.join()
|
||||||
|
|
||||||
|
def _http_requests_urllib3(self, url, session):
|
||||||
|
"""Request the data from the url"""
|
||||||
|
try:
|
||||||
|
r = session.request(self.request_type, url, headers=self.headers, timeout=self.timeout)
|
||||||
|
except IOError as e:
|
||||||
|
logger.warn(u"Failed to access uri endpoint %s with error %s" % (self.uri, e))
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warn(u"Failed to access uri endpoint %s. Is your server maybe accepting SSL connections only? %s" % (self.uri, e))
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
logger.warn(u"Failed to access uri endpoint %s with Uncaught exception." % self.uri)
|
||||||
|
return None
|
||||||
|
|
||||||
|
response_status = r.status
|
||||||
|
response_content = r.data
|
||||||
|
response_headers = r.headers
|
||||||
|
|
||||||
|
if response_status in (200, 201):
|
||||||
|
return self._http_format_output(response_content, response_headers)
|
||||||
|
else:
|
||||||
|
logger.warn(u"Failed to access uri endpoint %s. Status code %r" % (self.uri, response_status))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _http_format_output(self, response_content, response_headers):
|
||||||
|
"""Formats the request response to the desired type"""
|
||||||
|
try:
|
||||||
|
if self.output_format == 'text':
|
||||||
|
output = response_content.decode('utf-8', 'ignore')
|
||||||
|
if self.output_format == 'dict':
|
||||||
|
output = helpers.convert_xml_to_dict(response_content.decode('utf-8', 'ignore'))
|
||||||
|
elif self.output_format == 'json':
|
||||||
|
output = helpers.convert_xml_to_json(response_content.decode('utf-8', 'ignore'))
|
||||||
|
elif self.output_format == 'xml':
|
||||||
|
output = helpers.parse_xml(response_content.decode('utf-8', 'ignore'))
|
||||||
|
else:
|
||||||
|
output = response_content
|
||||||
|
|
||||||
|
if self.callback:
|
||||||
|
return self.callback(output)
|
||||||
|
|
||||||
|
if self.return_type:
|
||||||
|
return output, response_headers['Content-Type']
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warn(u"Failed format response from uri %s to %s error %s" % (self.uri, self.response_type, e))
|
||||||
|
return None
|
||||||
|
|
|
@ -27,6 +27,66 @@ import pmsconnect
|
||||||
import session
|
import session
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_libraries():
|
||||||
|
logger.info(u"Tautulli Libraries :: Requesting libraries list refresh...")
|
||||||
|
|
||||||
|
server_id = plexpy.CONFIG.PMS_IDENTIFIER
|
||||||
|
if not server_id:
|
||||||
|
logger.error(u"Tautulli Libraries :: No PMS identifier, cannot refresh libraries. Verify server in settings.")
|
||||||
|
return
|
||||||
|
|
||||||
|
library_sections = pmsconnect.PmsConnect().get_library_details()
|
||||||
|
|
||||||
|
if library_sections:
|
||||||
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
library_keys = []
|
||||||
|
new_keys = []
|
||||||
|
|
||||||
|
for section in library_sections:
|
||||||
|
section_keys = {'server_id': server_id,
|
||||||
|
'section_id': section['section_id']}
|
||||||
|
section_values = {'server_id': server_id,
|
||||||
|
'section_id': section['section_id'],
|
||||||
|
'section_name': section['section_name'],
|
||||||
|
'section_type': section['section_type'],
|
||||||
|
'thumb': section['thumb'],
|
||||||
|
'art': section['art'],
|
||||||
|
'count': section['count'],
|
||||||
|
'parent_count': section.get('parent_count', None),
|
||||||
|
'child_count': section.get('child_count', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
result = monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
|
||||||
|
|
||||||
|
library_keys.append(section['section_id'])
|
||||||
|
|
||||||
|
if result == 'insert':
|
||||||
|
new_keys.append(section['section_id'])
|
||||||
|
|
||||||
|
if plexpy.CONFIG.HOME_LIBRARY_CARDS == ['first_run_wizard']:
|
||||||
|
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', library_keys)
|
||||||
|
plexpy.CONFIG.write()
|
||||||
|
else:
|
||||||
|
new_keys = plexpy.CONFIG.HOME_LIBRARY_CARDS + new_keys
|
||||||
|
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', new_keys)
|
||||||
|
plexpy.CONFIG.write()
|
||||||
|
|
||||||
|
#if plexpy.CONFIG.UPDATE_SECTION_IDS == 1 or plexpy.CONFIG.UPDATE_SECTION_IDS == -1:
|
||||||
|
# # Start library section_id update on it's own thread
|
||||||
|
# threading.Thread(target=libraries.update_section_ids).start()
|
||||||
|
|
||||||
|
#if plexpy.CONFIG.UPDATE_LABELS == 1 or plexpy.CONFIG.UPDATE_LABELS == -1:
|
||||||
|
# # Start library labels update on it's own thread
|
||||||
|
# threading.Thread(target=libraries.update_labels).start()
|
||||||
|
|
||||||
|
logger.info(u"Tautulli Libraries :: Libraries list refreshed.")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warn(u"Tautulli Libraries :: Unable to refresh libraries list.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def update_section_ids():
|
def update_section_ids():
|
||||||
plexpy.CONFIG.UPDATE_SECTION_IDS = -1
|
plexpy.CONFIG.UPDATE_SECTION_IDS = -1
|
||||||
|
|
||||||
|
@ -965,7 +1025,7 @@ class Libraries(object):
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
# Refresh the PMS_URL to make sure the server_id is updated
|
# Refresh the PMS_URL to make sure the server_id is updated
|
||||||
plextv.get_real_pms_url()
|
plextv.get_server_resources()
|
||||||
|
|
||||||
server_id = plexpy.CONFIG.PMS_IDENTIFIER
|
server_id = plexpy.CONFIG.PMS_IDENTIFIER
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ import activity_processor
|
||||||
import database
|
import database
|
||||||
import helpers
|
import helpers
|
||||||
import logger
|
import logger
|
||||||
import plextv
|
|
||||||
import users
|
import users
|
||||||
|
|
||||||
|
|
||||||
|
@ -284,7 +283,7 @@ def import_from_plexivity(database=None, table_name=None, import_ignore_interval
|
||||||
|
|
||||||
# Get the latest friends list so we can pull user id's
|
# Get the latest friends list so we can pull user id's
|
||||||
try:
|
try:
|
||||||
plextv.refresh_users()
|
users.refresh_users()
|
||||||
except:
|
except:
|
||||||
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
|
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
|
||||||
return None
|
return None
|
||||||
|
|
522
plexpy/plextv.py
522
plexpy/plextv.py
|
@ -18,11 +18,9 @@
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import common
|
import common
|
||||||
import database
|
|
||||||
import helpers
|
import helpers
|
||||||
import http_handler
|
import http_handler
|
||||||
import logger
|
import logger
|
||||||
|
@ -31,129 +29,99 @@ import pmsconnect
|
||||||
import session
|
import session
|
||||||
|
|
||||||
|
|
||||||
def refresh_users():
|
def get_server_resources(return_presence=False):
|
||||||
logger.info(u"Tautulli PlexTV :: Requesting users list refresh...")
|
if not return_presence:
|
||||||
result = PlexTV().get_full_users_list()
|
logger.info(u"Tautulli PlexTV :: Requesting resources for server...")
|
||||||
|
|
||||||
monitor_db = database.MonitorDatabase()
|
server = {'pms_name': plexpy.CONFIG.PMS_NAME,
|
||||||
user_data = users.Users()
|
'pms_version': plexpy.CONFIG.PMS_VERSION,
|
||||||
|
'pms_platform': plexpy.CONFIG.PMS_PLATFORM,
|
||||||
|
'pms_ip': plexpy.CONFIG.PMS_IP,
|
||||||
|
'pms_port': plexpy.CONFIG.PMS_PORT,
|
||||||
|
'pms_ssl': plexpy.CONFIG.PMS_SSL,
|
||||||
|
'pms_is_remote': plexpy.CONFIG.PMS_IS_REMOTE,
|
||||||
|
'pms_is_cloud': plexpy.CONFIG.PMS_IS_CLOUD,
|
||||||
|
'pms_url': plexpy.CONFIG.PMS_URL,
|
||||||
|
'pms_url_manual': plexpy.CONFIG.PMS_URL_MANUAL
|
||||||
|
}
|
||||||
|
|
||||||
if result:
|
if server['pms_url_manual'] and server['pms_ssl'] or server['pms_is_cloud']:
|
||||||
for item in result:
|
scheme = 'https'
|
||||||
|
|
||||||
shared_libraries = ''
|
|
||||||
user_tokens = user_data.get_tokens(user_id=item['user_id'])
|
|
||||||
if user_tokens and user_tokens['server_token']:
|
|
||||||
pms_connect = pmsconnect.PmsConnect(token=user_tokens['server_token'])
|
|
||||||
library_details = pms_connect.get_server_children()
|
|
||||||
|
|
||||||
if library_details:
|
|
||||||
shared_libraries = ';'.join(d['section_id'] for d in library_details['libraries_list'])
|
|
||||||
else:
|
|
||||||
shared_libraries = ''
|
|
||||||
|
|
||||||
control_value_dict = {"user_id": item['user_id']}
|
|
||||||
new_value_dict = {"username": item['username'],
|
|
||||||
"thumb": item['thumb'],
|
|
||||||
"email": item['email'],
|
|
||||||
"is_home_user": item['is_home_user'],
|
|
||||||
"is_allow_sync": item['is_allow_sync'],
|
|
||||||
"is_restricted": item['is_restricted'],
|
|
||||||
"shared_libraries": shared_libraries,
|
|
||||||
"filter_all": item['filter_all'],
|
|
||||||
"filter_movies": item['filter_movies'],
|
|
||||||
"filter_tv": item['filter_tv'],
|
|
||||||
"filter_music": item['filter_music'],
|
|
||||||
"filter_photos": item['filter_photos']
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if we've set a custom avatar if so don't overwrite it.
|
|
||||||
if item['user_id']:
|
|
||||||
avatar_urls = monitor_db.select('SELECT thumb, custom_avatar_url '
|
|
||||||
'FROM users WHERE user_id = ?',
|
|
||||||
[item['user_id']])
|
|
||||||
if avatar_urls:
|
|
||||||
if not avatar_urls[0]['custom_avatar_url'] or \
|
|
||||||
avatar_urls[0]['custom_avatar_url'] == avatar_urls[0]['thumb']:
|
|
||||||
new_value_dict['custom_avatar_url'] = item['thumb']
|
|
||||||
else:
|
|
||||||
new_value_dict['custom_avatar_url'] = item['thumb']
|
|
||||||
|
|
||||||
monitor_db.upsert('users', new_value_dict, control_value_dict)
|
|
||||||
|
|
||||||
logger.info(u"Tautulli PlexTV :: Users list refreshed.")
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to refresh users list.")
|
scheme = 'http'
|
||||||
return False
|
|
||||||
|
|
||||||
|
fallback_url = '{scheme}://{hostname}:{port}'.format(scheme=scheme,
|
||||||
def get_real_pms_url():
|
hostname=server['pms_ip'],
|
||||||
logger.info(u"Tautulli PlexTV :: Requesting URLs for server...")
|
port=server['pms_port'])
|
||||||
|
|
||||||
# Reset any current PMS_URL value
|
|
||||||
plexpy.CONFIG.__setattr__('PMS_URL', '')
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
|
|
||||||
fallback_url = 'http://{}:{}'.format(plexpy.CONFIG.PMS_IP, plexpy.CONFIG.PMS_PORT)
|
|
||||||
|
|
||||||
plex_tv = PlexTV()
|
plex_tv = PlexTV()
|
||||||
result = plex_tv.get_server_urls(include_https=plexpy.CONFIG.PMS_SSL)
|
result = plex_tv.get_server_connections(pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER,
|
||||||
plexpass = plex_tv.get_plexpass_status()
|
pms_ip=server['pms_ip'],
|
||||||
|
pms_port=server['pms_port'],
|
||||||
|
include_https=server['pms_ssl'])
|
||||||
|
|
||||||
connections = []
|
|
||||||
if result:
|
if result:
|
||||||
plexpy.CONFIG.__setattr__('PMS_VERSION', result['version'])
|
connections = result.pop('connections', [])
|
||||||
plexpy.CONFIG.__setattr__('PMS_PLATFORM', result['platform'])
|
server.update(result)
|
||||||
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', plexpass)
|
presence = server.pop('server_presence', 0)
|
||||||
connections = result['connections']
|
else:
|
||||||
|
connections = []
|
||||||
|
presence = 0
|
||||||
|
|
||||||
|
if return_presence:
|
||||||
|
return presence
|
||||||
|
|
||||||
|
plexpass = plex_tv.get_plexpass_status()
|
||||||
|
server['pms_plexpass'] = int(plexpass)
|
||||||
|
|
||||||
# Only need to retrieve PMS_URL if using SSL
|
# Only need to retrieve PMS_URL if using SSL
|
||||||
if not plexpy.CONFIG.PMS_URL_MANUAL and plexpy.CONFIG.PMS_SSL:
|
if not server['pms_url_manual'] and server['pms_ssl']:
|
||||||
if connections:
|
if connections:
|
||||||
if plexpy.CONFIG.PMS_IS_REMOTE:
|
if server['pms_is_remote']:
|
||||||
# Get all remote connections
|
# Get all remote connections
|
||||||
conns = [c for c in connections if c['local'] == '0' and 'plex.direct' in c['uri']]
|
conns = [c for c in connections if
|
||||||
|
c['local'] == '0' and ('plex.direct' in c['uri'] or 'plex.service' in c['uri'])]
|
||||||
else:
|
else:
|
||||||
# Get all local connections
|
# Get all local connections
|
||||||
conns = [c for c in connections if c['local'] == '1' and 'plex.direct' in c['uri']]
|
conns = [c for c in connections if
|
||||||
|
c['local'] == '1' and ('plex.direct' in c['uri'] or 'plex.service' in c['uri'])]
|
||||||
|
|
||||||
if conns:
|
if conns:
|
||||||
# Get connection with matching address, otherwise return first connection
|
# Get connection with matching address, otherwise return first connection
|
||||||
conn = next((c for c in conns if c['address'] == plexpy.CONFIG.PMS_IP
|
conn = next((c for c in conns if c['address'] == server['pms_ip']
|
||||||
and c['port'] == str(plexpy.CONFIG.PMS_PORT)), conns[0])
|
and c['port'] == str(server['pms_port'])), conns[0])
|
||||||
plexpy.CONFIG.__setattr__('PMS_URL', conn['uri'])
|
server['pms_url'] = conn['uri']
|
||||||
plexpy.CONFIG.write()
|
|
||||||
logger.info(u"Tautulli PlexTV :: Server URL retrieved.")
|
logger.info(u"Tautulli PlexTV :: Server URL retrieved.")
|
||||||
|
|
||||||
# get_server_urls() failed or PMS_URL not found, fallback url doesn't use SSL
|
# get_server_urls() failed or PMS_URL not found, fallback url doesn't use SSL
|
||||||
if not plexpy.CONFIG.PMS_URL:
|
if not server['pms_url']:
|
||||||
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
|
server['pms_url'] = fallback_url
|
||||||
plexpy.CONFIG.write()
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to retrieve server URLs. Using user-defined value without SSL.")
|
logger.warn(u"Tautulli PlexTV :: Unable to retrieve server URLs. Using user-defined value without SSL.")
|
||||||
|
|
||||||
# Not using SSL, remote has no effect
|
# Not using SSL, remote has no effect
|
||||||
else:
|
else:
|
||||||
if plexpy.CONFIG.PMS_URL_MANUAL and plexpy.CONFIG.PMS_SSL:
|
server['pms_url'] = fallback_url
|
||||||
fallback_url = fallback_url.replace('http://', 'https://')
|
|
||||||
|
|
||||||
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
logger.info(u"Tautulli PlexTV :: Using user-defined URL.")
|
logger.info(u"Tautulli PlexTV :: Using user-defined URL.")
|
||||||
|
|
||||||
|
plexpy.CONFIG.process_kwargs(server)
|
||||||
|
plexpy.CONFIG.write()
|
||||||
|
|
||||||
|
|
||||||
class PlexTV(object):
|
class PlexTV(object):
|
||||||
"""
|
"""
|
||||||
Plex.tv authentication
|
Plex.tv authentication
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, username='', password='', token=None):
|
def __init__(self, username=None, password=None, token=None):
|
||||||
self.protocol = 'HTTPS'
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self.token = token
|
||||||
|
|
||||||
|
self.urls = 'https://plex.tv'
|
||||||
|
self.timeout = plexpy.CONFIG.PMS_TIMEOUT
|
||||||
self.ssl_verify = plexpy.CONFIG.VERIFY_SSL_CERT
|
self.ssl_verify = plexpy.CONFIG.VERIFY_SSL_CERT
|
||||||
|
|
||||||
if not token:
|
if not self.token:
|
||||||
# Check if we should use the admin token, or the guest server token
|
# Check if we should use the admin token, or the guest server token
|
||||||
if session.get_session_user_id():
|
if session.get_session_user_id():
|
||||||
user_data = users.Users()
|
user_data = users.Users()
|
||||||
|
@ -161,12 +129,14 @@ class PlexTV(object):
|
||||||
self.token = user_tokens['server_token']
|
self.token = user_tokens['server_token']
|
||||||
else:
|
else:
|
||||||
self.token = plexpy.CONFIG.PMS_TOKEN
|
self.token = plexpy.CONFIG.PMS_TOKEN
|
||||||
else:
|
|
||||||
self.token = token
|
|
||||||
|
|
||||||
self.request_handler = http_handler.HTTPHandler(host='plex.tv',
|
if not self.token:
|
||||||
port=443,
|
logger.error(u"Tautulli PlexTV :: PlexTV called, but no token provided.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.request_handler = http_handler.HTTPHandler(urls=self.urls,
|
||||||
token=self.token,
|
token=self.token,
|
||||||
|
timeout=self.timeout,
|
||||||
ssl_verify=self.ssl_verify)
|
ssl_verify=self.ssl_verify)
|
||||||
|
|
||||||
def get_plex_auth(self, output_format='raw'):
|
def get_plex_auth(self, output_format='raw'):
|
||||||
|
@ -183,7 +153,6 @@ class PlexTV(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='POST',
|
request_type='POST',
|
||||||
headers=headers,
|
headers=headers,
|
||||||
output_format=output_format,
|
output_format=output_format,
|
||||||
|
@ -265,7 +234,6 @@ class PlexTV(object):
|
||||||
def get_plextv_friends(self, output_format=''):
|
def get_plextv_friends(self, output_format=''):
|
||||||
uri = '/api/users'
|
uri = '/api/users'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -274,7 +242,6 @@ class PlexTV(object):
|
||||||
def get_plextv_user_details(self, output_format=''):
|
def get_plextv_user_details(self, output_format=''):
|
||||||
uri = '/users/account'
|
uri = '/users/account'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -283,7 +250,6 @@ class PlexTV(object):
|
||||||
def get_plextv_devices_list(self, output_format=''):
|
def get_plextv_devices_list(self, output_format=''):
|
||||||
uri = '/devices.xml'
|
uri = '/devices.xml'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -292,7 +258,6 @@ class PlexTV(object):
|
||||||
def get_plextv_server_list(self, output_format=''):
|
def get_plextv_server_list(self, output_format=''):
|
||||||
uri = '/pms/servers.xml'
|
uri = '/pms/servers.xml'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -301,7 +266,6 @@ class PlexTV(object):
|
||||||
def get_plextv_sync_lists(self, machine_id='', output_format=''):
|
def get_plextv_sync_lists(self, machine_id='', output_format=''):
|
||||||
uri = '/servers/%s/sync_lists' % machine_id
|
uri = '/servers/%s/sync_lists' % machine_id
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -313,7 +277,6 @@ class PlexTV(object):
|
||||||
else:
|
else:
|
||||||
uri = '/api/resources'
|
uri = '/api/resources'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -325,7 +288,6 @@ class PlexTV(object):
|
||||||
else:
|
else:
|
||||||
uri = '/api/downloads/1.json'
|
uri = '/api/downloads/1.json'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -334,7 +296,6 @@ class PlexTV(object):
|
||||||
def delete_plextv_device(self, device_id='', output_format=''):
|
def delete_plextv_device(self, device_id='', output_format=''):
|
||||||
uri = '/devices/%s.xml' % device_id
|
uri = '/devices/%s.xml' % device_id
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='DELETE',
|
request_type='DELETE',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -343,7 +304,6 @@ class PlexTV(object):
|
||||||
def delete_plextv_device_sync_lists(self, client_id='', output_format=''):
|
def delete_plextv_device_sync_lists(self, client_id='', output_format=''):
|
||||||
uri = '/devices/%s/sync_items' % client_id
|
uri = '/devices/%s/sync_items' % client_id
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -352,197 +312,174 @@ class PlexTV(object):
|
||||||
def delete_plextv_sync(self, client_id='', sync_id='', output_format=''):
|
def delete_plextv_sync(self, client_id='', sync_id='', output_format=''):
|
||||||
uri = '/devices/%s/sync_items/%s' % (client_id, sync_id)
|
uri = '/devices/%s/sync_items/%s' % (client_id, sync_id)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='DELETE',
|
request_type='DELETE',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def get_full_users_list(self):
|
def get_full_users_list(self):
|
||||||
friends_list = self.get_plextv_friends()
|
friends_list = self.get_plextv_friends(output_format='xml')
|
||||||
own_account = self.get_plextv_user_details()
|
own_account = self.get_plextv_user_details(output_format='xml')
|
||||||
users_list = []
|
users_list = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml_parse = minidom.parseString(own_account)
|
xml_head = own_account.getElementsByTagName('user')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list own account: %s" % e)
|
logger.warn(u"Tautulli PlexTV :: Unable to parse own account XML for get_full_users_list: %s." % e)
|
||||||
return []
|
return {}
|
||||||
except:
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list own account.")
|
|
||||||
return []
|
|
||||||
|
|
||||||
xml_head = xml_parse.getElementsByTagName('user')
|
for a in xml_head:
|
||||||
if not xml_head:
|
own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list.")
|
"username": helpers.get_xml_attr(a, 'username'),
|
||||||
else:
|
"thumb": helpers.get_xml_attr(a, 'thumb'),
|
||||||
for a in xml_head:
|
"email": helpers.get_xml_attr(a, 'email'),
|
||||||
own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
|
"is_home_user": helpers.get_xml_attr(a, 'home'),
|
||||||
"username": helpers.get_xml_attr(a, 'username'),
|
"is_allow_sync": None,
|
||||||
"thumb": helpers.get_xml_attr(a, 'thumb'),
|
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
|
||||||
"email": helpers.get_xml_attr(a, 'email'),
|
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
|
||||||
"is_home_user": helpers.get_xml_attr(a, 'home'),
|
"filter_movies": helpers.get_xml_attr(a, 'filterMovies'),
|
||||||
"is_allow_sync": None,
|
"filter_tv": helpers.get_xml_attr(a, 'filterTelevision'),
|
||||||
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
|
"filter_music": helpers.get_xml_attr(a, 'filterMusic'),
|
||||||
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
|
"filter_photos": helpers.get_xml_attr(a, 'filterPhotos')
|
||||||
"filter_movies": helpers.get_xml_attr(a, 'filterMovies'),
|
}
|
||||||
"filter_tv": helpers.get_xml_attr(a, 'filterTelevision'),
|
|
||||||
"filter_music": helpers.get_xml_attr(a, 'filterMusic'),
|
|
||||||
"filter_photos": helpers.get_xml_attr(a, 'filterPhotos')
|
|
||||||
}
|
|
||||||
|
|
||||||
users_list.append(own_details)
|
users_list.append(own_details)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml_parse = minidom.parseString(friends_list)
|
xml_head = friends_list.getElementsByTagName('User')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list friends list: %s" % e)
|
logger.warn(u"Tautulli PlexTV :: Unable to parse friends list XML for get_full_users_list: %s." % e)
|
||||||
return []
|
return {}
|
||||||
except:
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list friends list.")
|
|
||||||
return []
|
|
||||||
|
|
||||||
xml_head = xml_parse.getElementsByTagName('User')
|
for a in xml_head:
|
||||||
if not xml_head:
|
friend = {"user_id": helpers.get_xml_attr(a, 'id'),
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list.")
|
"username": helpers.get_xml_attr(a, 'title'),
|
||||||
else:
|
"thumb": helpers.get_xml_attr(a, 'thumb'),
|
||||||
for a in xml_head:
|
"email": helpers.get_xml_attr(a, 'email'),
|
||||||
friend = {"user_id": helpers.get_xml_attr(a, 'id'),
|
"is_home_user": helpers.get_xml_attr(a, 'home'),
|
||||||
"username": helpers.get_xml_attr(a, 'title'),
|
"is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
|
||||||
"thumb": helpers.get_xml_attr(a, 'thumb'),
|
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
|
||||||
"email": helpers.get_xml_attr(a, 'email'),
|
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
|
||||||
"is_home_user": helpers.get_xml_attr(a, 'home'),
|
"filter_movies": helpers.get_xml_attr(a, 'filterMovies'),
|
||||||
"is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
|
"filter_tv": helpers.get_xml_attr(a, 'filterTelevision'),
|
||||||
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
|
"filter_music": helpers.get_xml_attr(a, 'filterMusic'),
|
||||||
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
|
"filter_photos": helpers.get_xml_attr(a, 'filterPhotos')
|
||||||
"filter_movies": helpers.get_xml_attr(a, 'filterMovies'),
|
}
|
||||||
"filter_tv": helpers.get_xml_attr(a, 'filterTelevision'),
|
|
||||||
"filter_music": helpers.get_xml_attr(a, 'filterMusic'),
|
|
||||||
"filter_photos": helpers.get_xml_attr(a, 'filterPhotos')
|
|
||||||
}
|
|
||||||
|
|
||||||
users_list.append(friend)
|
users_list.append(friend)
|
||||||
|
|
||||||
return users_list
|
return users_list
|
||||||
|
|
||||||
def get_synced_items(self, machine_id=None, client_id_filter=None, user_id_filter=None, rating_key_filter=None):
|
def get_synced_items(self, machine_id=None, client_id_filter=None, user_id_filter=None, rating_key_filter=None):
|
||||||
sync_list = self.get_plextv_sync_lists(machine_id)
|
sync_list = self.get_plextv_sync_lists(machine_id, output_format='xml')
|
||||||
user_data = users.Users()
|
user_data = users.Users()
|
||||||
|
|
||||||
synced_items = []
|
synced_items = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml_parse = minidom.parseString(sync_list)
|
xml_head = sync_list.getElementsByTagName('SyncList')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s" % e)
|
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s." % e)
|
||||||
return []
|
return {}
|
||||||
except:
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items.")
|
|
||||||
return []
|
|
||||||
|
|
||||||
xml_head = xml_parse.getElementsByTagName('SyncList')
|
for a in xml_head:
|
||||||
|
client_id = helpers.get_xml_attr(a, 'clientIdentifier')
|
||||||
|
|
||||||
if not xml_head:
|
# Filter by client_id
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items.")
|
if client_id_filter and client_id_filter != client_id:
|
||||||
else:
|
continue
|
||||||
for a in xml_head:
|
|
||||||
client_id = helpers.get_xml_attr(a, 'clientIdentifier')
|
|
||||||
|
|
||||||
# Filter by client_id
|
sync_id = helpers.get_xml_attr(a, 'id')
|
||||||
if client_id_filter and client_id_filter != client_id:
|
sync_device = a.getElementsByTagName('Device')
|
||||||
continue
|
|
||||||
|
|
||||||
sync_id = helpers.get_xml_attr(a, 'id')
|
for device in sync_device:
|
||||||
sync_device = a.getElementsByTagName('Device')
|
device_user_id = helpers.get_xml_attr(device, 'userID')
|
||||||
|
try:
|
||||||
|
device_username = user_data.get_details(user_id=device_user_id)['username']
|
||||||
|
device_friendly_name = user_data.get_details(user_id=device_user_id)['friendly_name']
|
||||||
|
except:
|
||||||
|
device_username = ''
|
||||||
|
device_friendly_name = ''
|
||||||
|
device_name = helpers.get_xml_attr(device, 'name')
|
||||||
|
device_product = helpers.get_xml_attr(device, 'product')
|
||||||
|
device_product_version = helpers.get_xml_attr(device, 'productVersion')
|
||||||
|
device_platform = helpers.get_xml_attr(device, 'platform')
|
||||||
|
device_platform_version = helpers.get_xml_attr(device, 'platformVersion')
|
||||||
|
device_type = helpers.get_xml_attr(device, 'device')
|
||||||
|
device_model = helpers.get_xml_attr(device, 'model')
|
||||||
|
device_last_seen = helpers.get_xml_attr(device, 'lastSeenAt')
|
||||||
|
|
||||||
for device in sync_device:
|
# Filter by user_id
|
||||||
device_user_id = helpers.get_xml_attr(device, 'userID')
|
if user_id_filter and user_id_filter != device_user_id:
|
||||||
try:
|
continue
|
||||||
device_username = user_data.get_details(user_id=device_user_id)['username']
|
|
||||||
device_friendly_name = user_data.get_details(user_id=device_user_id)['friendly_name']
|
|
||||||
except:
|
|
||||||
device_username = ''
|
|
||||||
device_friendly_name = ''
|
|
||||||
device_name = helpers.get_xml_attr(device, 'name')
|
|
||||||
device_product = helpers.get_xml_attr(device, 'product')
|
|
||||||
device_product_version = helpers.get_xml_attr(device, 'productVersion')
|
|
||||||
device_platform = helpers.get_xml_attr(device, 'platform')
|
|
||||||
device_platform_version = helpers.get_xml_attr(device, 'platformVersion')
|
|
||||||
device_type = helpers.get_xml_attr(device, 'device')
|
|
||||||
device_model = helpers.get_xml_attr(device, 'model')
|
|
||||||
device_last_seen = helpers.get_xml_attr(device, 'lastSeenAt')
|
|
||||||
|
|
||||||
# Filter by user_id
|
for synced in a.getElementsByTagName('SyncItems'):
|
||||||
if user_id_filter and user_id_filter != device_user_id:
|
sync_item = synced.getElementsByTagName('SyncItem')
|
||||||
continue
|
for item in sync_item:
|
||||||
|
|
||||||
for synced in a.getElementsByTagName('SyncItems'):
|
for location in item.getElementsByTagName('Location'):
|
||||||
sync_item = synced.getElementsByTagName('SyncItem')
|
clean_uri = helpers.get_xml_attr(location, 'uri').split('%2F')
|
||||||
for item in sync_item:
|
|
||||||
|
|
||||||
for location in item.getElementsByTagName('Location'):
|
rating_key = next((clean_uri[(idx + 1) % len(clean_uri)]
|
||||||
clean_uri = helpers.get_xml_attr(location, 'uri').split('%2F')
|
for idx, item in enumerate(clean_uri) if item == 'metadata'), None)
|
||||||
|
|
||||||
rating_key = next((clean_uri[(idx + 1) % len(clean_uri)]
|
# Filter by rating_key
|
||||||
for idx, item in enumerate(clean_uri) if item == 'metadata'), None)
|
if rating_key_filter and rating_key_filter != rating_key:
|
||||||
|
continue
|
||||||
|
|
||||||
# Filter by rating_key
|
sync_id = helpers.get_xml_attr(item, 'id')
|
||||||
if rating_key_filter and rating_key_filter != rating_key:
|
sync_version = helpers.get_xml_attr(item, 'version')
|
||||||
continue
|
sync_root_title = helpers.get_xml_attr(item, 'rootTitle')
|
||||||
|
sync_title = helpers.get_xml_attr(item, 'title')
|
||||||
|
sync_metadata_type = helpers.get_xml_attr(item, 'metadataType')
|
||||||
|
sync_content_type = helpers.get_xml_attr(item, 'contentType')
|
||||||
|
|
||||||
sync_id = helpers.get_xml_attr(item, 'id')
|
for status in item.getElementsByTagName('Status'):
|
||||||
sync_version = helpers.get_xml_attr(item, 'version')
|
status_failure_code = helpers.get_xml_attr(status, 'failureCode')
|
||||||
sync_root_title = helpers.get_xml_attr(item, 'rootTitle')
|
status_failure = helpers.get_xml_attr(status, 'failure')
|
||||||
sync_title = helpers.get_xml_attr(item, 'title')
|
status_state = helpers.get_xml_attr(status, 'state')
|
||||||
sync_metadata_type = helpers.get_xml_attr(item, 'metadataType')
|
status_item_count = helpers.get_xml_attr(status, 'itemsCount')
|
||||||
sync_content_type = helpers.get_xml_attr(item, 'contentType')
|
status_item_complete_count = helpers.get_xml_attr(status, 'itemsCompleteCount')
|
||||||
|
status_item_downloaded_count = helpers.get_xml_attr(status, 'itemsDownloadedCount')
|
||||||
|
status_item_ready_count = helpers.get_xml_attr(status, 'itemsReadyCount')
|
||||||
|
status_item_successful_count = helpers.get_xml_attr(status, 'itemsSuccessfulCount')
|
||||||
|
status_total_size = helpers.get_xml_attr(status, 'totalSize')
|
||||||
|
status_item_download_percent_complete = helpers.get_percent(
|
||||||
|
status_item_downloaded_count, status_item_count)
|
||||||
|
|
||||||
for status in item.getElementsByTagName('Status'):
|
for settings in item.getElementsByTagName('MediaSettings'):
|
||||||
status_failure_code = helpers.get_xml_attr(status, 'failureCode')
|
settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
|
||||||
status_failure = helpers.get_xml_attr(status, 'failure')
|
settings_music_bitrate = helpers.get_xml_attr(settings, 'musicBitrate')
|
||||||
status_state = helpers.get_xml_attr(status, 'state')
|
settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality')
|
||||||
status_item_count = helpers.get_xml_attr(status, 'itemsCount')
|
settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution')
|
||||||
status_item_complete_count = helpers.get_xml_attr(status, 'itemsCompleteCount')
|
settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
|
||||||
status_item_downloaded_count = helpers.get_xml_attr(status, 'itemsDownloadedCount')
|
settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')
|
||||||
status_item_ready_count = helpers.get_xml_attr(status, 'itemsReadyCount')
|
|
||||||
status_item_successful_count = helpers.get_xml_attr(status, 'itemsSuccessfulCount')
|
|
||||||
status_total_size = helpers.get_xml_attr(status, 'totalSize')
|
|
||||||
status_item_download_percent_complete = helpers.get_percent(
|
|
||||||
status_item_downloaded_count, status_item_count)
|
|
||||||
|
|
||||||
for settings in item.getElementsByTagName('MediaSettings'):
|
sync_details = {"device_name": helpers.sanitize(device_name),
|
||||||
settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
|
"platform": helpers.sanitize(device_platform),
|
||||||
settings_music_bitrate = helpers.get_xml_attr(settings, 'musicBitrate')
|
"username": helpers.sanitize(device_username),
|
||||||
settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality')
|
"friendly_name": helpers.sanitize(device_friendly_name),
|
||||||
settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution')
|
"user_id": device_user_id,
|
||||||
settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
|
"root_title": helpers.sanitize(sync_root_title),
|
||||||
settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')
|
"title": helpers.sanitize(sync_title),
|
||||||
|
"metadata_type": sync_metadata_type,
|
||||||
|
"content_type": sync_content_type,
|
||||||
|
"rating_key": rating_key,
|
||||||
|
"state": status_state,
|
||||||
|
"item_count": status_item_count,
|
||||||
|
"item_complete_count": status_item_complete_count,
|
||||||
|
"item_downloaded_count": status_item_downloaded_count,
|
||||||
|
"item_downloaded_percent_complete": status_item_download_percent_complete,
|
||||||
|
"music_bitrate": settings_music_bitrate,
|
||||||
|
"photo_quality": settings_photo_quality,
|
||||||
|
"video_quality": settings_video_quality,
|
||||||
|
"total_size": status_total_size,
|
||||||
|
"failure": status_failure,
|
||||||
|
"client_id": client_id,
|
||||||
|
"sync_id": sync_id
|
||||||
|
}
|
||||||
|
|
||||||
sync_details = {"device_name": helpers.sanitize(device_name),
|
synced_items.append(sync_details)
|
||||||
"platform": helpers.sanitize(device_platform),
|
|
||||||
"username": helpers.sanitize(device_username),
|
|
||||||
"friendly_name": helpers.sanitize(device_friendly_name),
|
|
||||||
"user_id": device_user_id,
|
|
||||||
"root_title": helpers.sanitize(sync_root_title),
|
|
||||||
"title": helpers.sanitize(sync_title),
|
|
||||||
"metadata_type": sync_metadata_type,
|
|
||||||
"content_type": sync_content_type,
|
|
||||||
"rating_key": rating_key,
|
|
||||||
"state": status_state,
|
|
||||||
"item_count": status_item_count,
|
|
||||||
"item_complete_count": status_item_complete_count,
|
|
||||||
"item_downloaded_count": status_item_downloaded_count,
|
|
||||||
"item_downloaded_percent_complete": status_item_download_percent_complete,
|
|
||||||
"music_bitrate": settings_music_bitrate,
|
|
||||||
"photo_quality": settings_photo_quality,
|
|
||||||
"video_quality": settings_video_quality,
|
|
||||||
"total_size": status_total_size,
|
|
||||||
"failure": status_failure,
|
|
||||||
"client_id": client_id,
|
|
||||||
"sync_id": sync_id
|
|
||||||
}
|
|
||||||
|
|
||||||
synced_items.append(sync_details)
|
|
||||||
|
|
||||||
return session.filter_session_info(synced_items, filter_key='user_id')
|
return session.filter_session_info(synced_items, filter_key='user_id')
|
||||||
|
|
||||||
|
@ -550,27 +487,16 @@ class PlexTV(object):
|
||||||
logger.info(u"Tautulli PlexTV :: Deleting sync item '%s'." % sync_id)
|
logger.info(u"Tautulli PlexTV :: Deleting sync item '%s'." % sync_id)
|
||||||
self.delete_plextv_sync(client_id=client_id, sync_id=sync_id)
|
self.delete_plextv_sync(client_id=client_id, sync_id=sync_id)
|
||||||
|
|
||||||
def get_server_urls(self, include_https=True):
|
def get_server_connections(self, pms_identifier='', pms_ip='', pms_port=32400, include_https=True):
|
||||||
|
|
||||||
if plexpy.CONFIG.PMS_IDENTIFIER:
|
if not pms_identifier:
|
||||||
server_id = plexpy.CONFIG.PMS_IDENTIFIER
|
logger.error(u"Tautulli PlexTV :: Unable to retrieve server connections: no pms_identifier provided.")
|
||||||
else:
|
|
||||||
logger.error(u"Tautulli PlexTV :: Unable to retrieve server identity.")
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
plextv_resources = self.get_plextv_resources(include_https=include_https)
|
plextv_resources = self.get_plextv_resources(include_https=include_https,
|
||||||
|
output_format='xml')
|
||||||
try:
|
try:
|
||||||
xml_parse = minidom.parseString(plextv_resources)
|
xml_head = plextv_resources.getElementsByTagName('Device')
|
||||||
except Exception as e:
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_server_urls: %s" % e)
|
|
||||||
return {}
|
|
||||||
except:
|
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_server_urls.")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
try:
|
|
||||||
xml_head = xml_parse.getElementsByTagName('Device')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_server_urls: %s." % e)
|
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_server_urls: %s." % e)
|
||||||
return {}
|
return {}
|
||||||
|
@ -580,16 +506,20 @@ class PlexTV(object):
|
||||||
conn = []
|
conn = []
|
||||||
connections = device.getElementsByTagName('Connection')
|
connections = device.getElementsByTagName('Connection')
|
||||||
|
|
||||||
server = {"platform": helpers.get_xml_attr(device, 'platform'),
|
server = {'pms_identifier': helpers.get_xml_attr(device, 'clientIdentifier'),
|
||||||
"version": helpers.get_xml_attr(device, 'productVersion')
|
'pms_name': helpers.get_xml_attr(device, 'name'),
|
||||||
|
'pms_version': helpers.get_xml_attr(device, 'productVersion'),
|
||||||
|
'pms_platform': helpers.get_xml_attr(device, 'platform'),
|
||||||
|
'pms_presence': helpers.get_xml_attr(device, 'presence'),
|
||||||
|
'pms_is_cloud': 1 if helpers.get_xml_attr(device, 'platform') == 'Cloud' else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for c in connections:
|
for c in connections:
|
||||||
server_details = {"protocol": helpers.get_xml_attr(c, 'protocol'),
|
server_details = {'protocol': helpers.get_xml_attr(c, 'protocol'),
|
||||||
"address": helpers.get_xml_attr(c, 'address'),
|
'address': helpers.get_xml_attr(c, 'address'),
|
||||||
"port": helpers.get_xml_attr(c, 'port'),
|
'port': helpers.get_xml_attr(c, 'port'),
|
||||||
"uri": helpers.get_xml_attr(c, 'uri'),
|
'uri': helpers.get_xml_attr(c, 'uri'),
|
||||||
"local": helpers.get_xml_attr(c, 'local')
|
'local': helpers.get_xml_attr(c, 'local')
|
||||||
}
|
}
|
||||||
conn.append(server_details)
|
conn.append(server_details)
|
||||||
|
|
||||||
|
@ -600,10 +530,10 @@ class PlexTV(object):
|
||||||
|
|
||||||
# Try to match the device
|
# Try to match the device
|
||||||
for a in xml_head:
|
for a in xml_head:
|
||||||
if helpers.get_xml_attr(a, 'clientIdentifier') == server_id:
|
if helpers.get_xml_attr(a, 'clientIdentifier') == pms_identifier:
|
||||||
server = get_connections(a)
|
server = get_connections(a)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Else no device match found
|
# Else no device match found
|
||||||
if not server:
|
if not server:
|
||||||
# Try to match the PMS_IP and PMS_PORT
|
# Try to match the PMS_IP and PMS_PORT
|
||||||
|
@ -612,15 +542,8 @@ class PlexTV(object):
|
||||||
connections = a.getElementsByTagName('Connection')
|
connections = a.getElementsByTagName('Connection')
|
||||||
|
|
||||||
for connection in connections:
|
for connection in connections:
|
||||||
if helpers.get_xml_attr(connection, 'address') == plexpy.CONFIG.PMS_IP and \
|
if helpers.get_xml_attr(connection, 'address') == pms_ip and \
|
||||||
int(helpers.get_xml_attr(connection, 'port')) == plexpy.CONFIG.PMS_PORT:
|
helpers.get_xml_attr(connection, 'port') == str(pms_port):
|
||||||
|
|
||||||
plexpy.CONFIG.PMS_IDENTIFIER = helpers.get_xml_attr(a, 'clientIdentifier')
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
|
|
||||||
logger.info(u"Tautulli PlexTV :: PMS identifier changed from %s to %s."
|
|
||||||
% (server_id, plexpy.CONFIG.PMS_IDENTIFIER))
|
|
||||||
|
|
||||||
server = get_connections(a)
|
server = get_connections(a)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -649,7 +572,7 @@ class PlexTV(object):
|
||||||
|
|
||||||
return server_times
|
return server_times
|
||||||
|
|
||||||
def discover(self, include_cloud=True):
|
def discover(self, include_cloud=True, all_servers=False):
|
||||||
""" Query plex for all servers online. Returns the ones you own in a selectize format """
|
""" Query plex for all servers online. Returns the ones you own in a selectize format """
|
||||||
servers = self.get_plextv_resources(include_https=True, output_format='xml')
|
servers = self.get_plextv_resources(include_https=True, output_format='xml')
|
||||||
clean_servers = []
|
clean_servers = []
|
||||||
|
@ -679,15 +602,16 @@ class PlexTV(object):
|
||||||
connections = d.getElementsByTagName('Connection')
|
connections = d.getElementsByTagName('Connection')
|
||||||
|
|
||||||
for c in connections:
|
for c in connections:
|
||||||
# If this is a remote server don't show any local IPs.
|
if not all_servers:
|
||||||
if helpers.get_xml_attr(d, 'publicAddressMatches') == '0' and \
|
# If this is a remote server don't show any local IPs.
|
||||||
helpers.get_xml_attr(c, 'local') == '1':
|
if helpers.get_xml_attr(d, 'publicAddressMatches') == '0' and \
|
||||||
continue
|
helpers.get_xml_attr(c, 'local') == '1':
|
||||||
|
continue
|
||||||
|
|
||||||
# If this is a local server don't show any remote IPs.
|
# If this is a local server don't show any remote IPs.
|
||||||
if helpers.get_xml_attr(d, 'publicAddressMatches') == '1' and \
|
if helpers.get_xml_attr(d, 'publicAddressMatches') == '1' and \
|
||||||
helpers.get_xml_attr(c, 'local') == '0':
|
helpers.get_xml_attr(c, 'local') == '0':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
server = {'httpsRequired': helpers.get_xml_attr(d, 'httpsRequired'),
|
server = {'httpsRequired': helpers.get_xml_attr(d, 'httpsRequired'),
|
||||||
'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'),
|
'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'),
|
||||||
|
@ -770,8 +694,6 @@ class PlexTV(object):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.debug(u"Tautulli PlexTV :: Plex Pass subscription not found.")
|
logger.debug(u"Tautulli PlexTV :: Plex Pass subscription not found.")
|
||||||
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', 0)
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_devices_list(self):
|
def get_devices_list(self):
|
||||||
|
|
|
@ -22,7 +22,6 @@ import activity_processor
|
||||||
import database
|
import database
|
||||||
import helpers
|
import helpers
|
||||||
import logger
|
import logger
|
||||||
import plextv
|
|
||||||
import users
|
import users
|
||||||
|
|
||||||
|
|
||||||
|
@ -275,7 +274,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
|
||||||
|
|
||||||
# Get the latest friends list so we can pull user id's
|
# Get the latest friends list so we can pull user id's
|
||||||
try:
|
try:
|
||||||
plextv.refresh_users()
|
users.refresh_users()
|
||||||
except:
|
except:
|
||||||
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
|
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -13,16 +13,12 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import threading
|
|
||||||
import urllib
|
import urllib
|
||||||
from urlparse import urlparse
|
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import common
|
import common
|
||||||
import database
|
|
||||||
import helpers
|
import helpers
|
||||||
import http_handler
|
import http_handler
|
||||||
import libraries
|
|
||||||
import logger
|
import logger
|
||||||
import plextv
|
import plextv
|
||||||
import session
|
import session
|
||||||
|
@ -49,83 +45,23 @@ def get_server_friendly_name():
|
||||||
return server_name
|
return server_name
|
||||||
|
|
||||||
|
|
||||||
def refresh_libraries():
|
|
||||||
logger.info(u"Tautulli Pmsconnect :: Requesting libraries list refresh...")
|
|
||||||
|
|
||||||
server_id = plexpy.CONFIG.PMS_IDENTIFIER
|
|
||||||
if not server_id:
|
|
||||||
logger.error(u"Tautulli Pmsconnect :: No PMS identifier, cannot refresh libraries. Verify server in settings.")
|
|
||||||
return
|
|
||||||
|
|
||||||
library_sections = PmsConnect().get_library_details()
|
|
||||||
|
|
||||||
if library_sections:
|
|
||||||
monitor_db = database.MonitorDatabase()
|
|
||||||
|
|
||||||
library_keys = []
|
|
||||||
new_keys = []
|
|
||||||
|
|
||||||
for section in library_sections:
|
|
||||||
section_keys = {'server_id': server_id,
|
|
||||||
'section_id': section['section_id']}
|
|
||||||
section_values = {'server_id': server_id,
|
|
||||||
'section_id': section['section_id'],
|
|
||||||
'section_name': section['section_name'],
|
|
||||||
'section_type': section['section_type'],
|
|
||||||
'thumb': section['thumb'],
|
|
||||||
'art': section['art'],
|
|
||||||
'count': section['count'],
|
|
||||||
'parent_count': section.get('parent_count', None),
|
|
||||||
'child_count': section.get('child_count', None),
|
|
||||||
}
|
|
||||||
|
|
||||||
result = monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
|
|
||||||
|
|
||||||
library_keys.append(section['section_id'])
|
|
||||||
|
|
||||||
if result == 'insert':
|
|
||||||
new_keys.append(section['section_id'])
|
|
||||||
|
|
||||||
if plexpy.CONFIG.HOME_LIBRARY_CARDS == ['first_run_wizard']:
|
|
||||||
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', library_keys)
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
else:
|
|
||||||
new_keys = plexpy.CONFIG.HOME_LIBRARY_CARDS + new_keys
|
|
||||||
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', new_keys)
|
|
||||||
plexpy.CONFIG.write()
|
|
||||||
|
|
||||||
#if plexpy.CONFIG.UPDATE_SECTION_IDS == 1 or plexpy.CONFIG.UPDATE_SECTION_IDS == -1:
|
|
||||||
# # Start library section_id update on it's own thread
|
|
||||||
# threading.Thread(target=libraries.update_section_ids).start()
|
|
||||||
|
|
||||||
#if plexpy.CONFIG.UPDATE_LABELS == 1 or plexpy.CONFIG.UPDATE_LABELS == -1:
|
|
||||||
# # Start library labels update on it's own thread
|
|
||||||
# threading.Thread(target=libraries.update_labels).start()
|
|
||||||
|
|
||||||
logger.info(u"Tautulli Pmsconnect :: Libraries list refreshed.")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.warn(u"Tautulli Pmsconnect :: Unable to refresh libraries list.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class PmsConnect(object):
|
class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
Retrieve data from Plex Server
|
Retrieve data from Plex Server
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, token=None):
|
def __init__(self, url=None, token=None):
|
||||||
if plexpy.CONFIG.PMS_URL:
|
self.url = url
|
||||||
url_parsed = urlparse(plexpy.CONFIG.PMS_URL)
|
self.token = token
|
||||||
hostname = url_parsed.hostname
|
|
||||||
port = url_parsed.port
|
|
||||||
self.protocol = url_parsed.scheme
|
|
||||||
else:
|
|
||||||
hostname = plexpy.CONFIG.PMS_IP
|
|
||||||
port = plexpy.CONFIG.PMS_PORT
|
|
||||||
self.protocol = 'http'
|
|
||||||
|
|
||||||
if not token:
|
if not self.url and plexpy.CONFIG.PMS_URL:
|
||||||
|
self.url = plexpy.CONFIG.PMS_URL
|
||||||
|
elif not self.url:
|
||||||
|
self.url = 'http://{hostname}:{port}'.format(hostname=plexpy.CONFIG.PMS_IP,
|
||||||
|
port=plexpy.CONFIG.PMS_PORT)
|
||||||
|
self.timeout = plexpy.CONFIG.PMS_TIMEOUT
|
||||||
|
|
||||||
|
if not self.token:
|
||||||
# Check if we should use the admin token, or the guest server token
|
# Check if we should use the admin token, or the guest server token
|
||||||
if session.get_session_user_id():
|
if session.get_session_user_id():
|
||||||
user_data = users.Users()
|
user_data = users.Users()
|
||||||
|
@ -133,12 +69,10 @@ class PmsConnect(object):
|
||||||
self.token = user_tokens['server_token']
|
self.token = user_tokens['server_token']
|
||||||
else:
|
else:
|
||||||
self.token = plexpy.CONFIG.PMS_TOKEN
|
self.token = plexpy.CONFIG.PMS_TOKEN
|
||||||
else:
|
|
||||||
self.token = token
|
|
||||||
|
|
||||||
self.request_handler = http_handler.HTTPHandler(host=hostname,
|
self.request_handler = http_handler.HTTPHandler(urls=self.url,
|
||||||
port=port,
|
token=self.token,
|
||||||
token=self.token)
|
timeout=self.timeout)
|
||||||
|
|
||||||
def get_sessions(self, output_format=''):
|
def get_sessions(self, output_format=''):
|
||||||
"""
|
"""
|
||||||
|
@ -150,7 +84,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/status/sessions'
|
uri = '/status/sessions'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -166,7 +99,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, reason)
|
uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, reason)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -183,7 +115,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/metadata/' + rating_key
|
uri = '/library/metadata/' + rating_key
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -200,7 +131,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/metadata/' + rating_key + '/children'
|
uri = '/library/metadata/' + rating_key + '/children'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -217,7 +147,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (start, count)
|
uri = '/library/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (start, count)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -234,7 +163,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/sections/%s/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (section_id, start, count)
|
uri = '/library/sections/%s/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (section_id, start, count)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -251,7 +179,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/metadata/' + rating_key + '/children'
|
uri = '/library/metadata/' + rating_key + '/children'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -285,7 +212,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/metadata/' + rating_key + '/allLeaves'
|
uri = '/library/metadata/' + rating_key + '/allLeaves'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -301,7 +227,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/servers'
|
uri = '/servers'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -317,7 +242,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/:/prefs'
|
uri = '/:/prefs'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -333,7 +257,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/identity'
|
uri = '/identity'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -349,7 +272,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/sections'
|
uri = '/library/sections'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -368,7 +290,6 @@ class PmsConnect(object):
|
||||||
|
|
||||||
uri = '/library/sections/' + section_id + '/' + list_type + '?X-Plex-Container-Start=0' + count + sort_type + label_key
|
uri = '/library/sections/' + section_id + '/' + list_type + '?X-Plex-Container-Start=0' + count + sort_type + label_key
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -384,7 +305,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/library/sections/' + section_id + '/label'
|
uri = '/library/sections/' + section_id + '/label'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -401,7 +321,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/sync/items/' + sync_id
|
uri = '/sync/items/' + sync_id
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -417,7 +336,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/sync/transcodeQueue'
|
uri = '/sync/transcodeQueue'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -433,7 +351,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
|
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -449,7 +366,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/myplex/account'
|
uri = '/myplex/account'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -465,7 +381,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/myplex/refreshReachability'
|
uri = '/myplex/refreshReachability'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='PUT')
|
request_type='PUT')
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
@ -480,7 +395,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/updater/check?download=0'
|
uri = '/updater/check?download=0'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='PUT',
|
request_type='PUT',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -496,7 +410,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/updater/status'
|
uri = '/updater/status'
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -515,7 +428,6 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
uri = '/hubs/home/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s&type=%s' % (start, count, type)
|
uri = '/hubs/home/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s&type=%s' % (start, count, type)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format=output_format)
|
output_format=output_format)
|
||||||
|
|
||||||
|
@ -2334,7 +2246,6 @@ class PmsConnect(object):
|
||||||
|
|
||||||
uri = '/photo/:/transcode?%s' % urllib.urlencode(params)
|
uri = '/photo/:/transcode?%s' % urllib.urlencode(params)
|
||||||
result = self.request_handler.make_request(uri=uri,
|
result = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
return_type=True)
|
return_type=True)
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,67 @@ import datatables
|
||||||
import helpers
|
import helpers
|
||||||
import logger
|
import logger
|
||||||
import plextv
|
import plextv
|
||||||
|
import pmsconnect
|
||||||
import session
|
import session
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_users():
|
||||||
|
logger.info(u"Tautulli Users :: Requesting users list refresh...")
|
||||||
|
result = plextv.PlexTV().get_full_users_list()
|
||||||
|
|
||||||
|
monitor_db = database.MonitorDatabase()
|
||||||
|
user_data = Users()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
for item in result:
|
||||||
|
|
||||||
|
shared_libraries = ''
|
||||||
|
user_tokens = user_data.get_tokens(user_id=item['user_id'])
|
||||||
|
if user_tokens and user_tokens['server_token']:
|
||||||
|
pms_connect = pmsconnect.PmsConnect(token=user_tokens['server_token'])
|
||||||
|
library_details = pms_connect.get_server_children()
|
||||||
|
|
||||||
|
if library_details:
|
||||||
|
shared_libraries = ';'.join(d['section_id'] for d in library_details['libraries_list'])
|
||||||
|
else:
|
||||||
|
shared_libraries = ''
|
||||||
|
|
||||||
|
control_value_dict = {"user_id": item['user_id']}
|
||||||
|
new_value_dict = {"username": item['username'],
|
||||||
|
"thumb": item['thumb'],
|
||||||
|
"email": item['email'],
|
||||||
|
"is_home_user": item['is_home_user'],
|
||||||
|
"is_allow_sync": item['is_allow_sync'],
|
||||||
|
"is_restricted": item['is_restricted'],
|
||||||
|
"shared_libraries": shared_libraries,
|
||||||
|
"filter_all": item['filter_all'],
|
||||||
|
"filter_movies": item['filter_movies'],
|
||||||
|
"filter_tv": item['filter_tv'],
|
||||||
|
"filter_music": item['filter_music'],
|
||||||
|
"filter_photos": item['filter_photos']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we've set a custom avatar if so don't overwrite it.
|
||||||
|
if item['user_id']:
|
||||||
|
avatar_urls = monitor_db.select('SELECT thumb, custom_avatar_url '
|
||||||
|
'FROM users WHERE user_id = ?',
|
||||||
|
[item['user_id']])
|
||||||
|
if avatar_urls:
|
||||||
|
if not avatar_urls[0]['custom_avatar_url'] or \
|
||||||
|
avatar_urls[0]['custom_avatar_url'] == avatar_urls[0]['thumb']:
|
||||||
|
new_value_dict['custom_avatar_url'] = item['thumb']
|
||||||
|
else:
|
||||||
|
new_value_dict['custom_avatar_url'] = item['thumb']
|
||||||
|
|
||||||
|
monitor_db.upsert('users', new_value_dict, control_value_dict)
|
||||||
|
|
||||||
|
logger.info(u"Tautulli Users :: Users list refreshed.")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warn(u"Tautulli Users :: Unable to refresh users list.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Users(object):
|
class Users(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -360,7 +418,7 @@ class Users(object):
|
||||||
logger.warn(u"Tautulli Users :: Unable to retrieve user %s from database. Requesting user list refresh."
|
logger.warn(u"Tautulli Users :: Unable to retrieve user %s from database. Requesting user list refresh."
|
||||||
% user_id if user_id else user)
|
% user_id if user_id else user)
|
||||||
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
|
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
|
||||||
plextv.refresh_users()
|
refresh_users()
|
||||||
|
|
||||||
user_details = get_user_details(user_id=user_id, user=user)
|
user_details = get_user_details(user_id=user_id, user=user)
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ def start_thread():
|
||||||
|
|
||||||
def on_disconnect():
|
def on_disconnect():
|
||||||
activity_processor.ActivityProcessor().set_temp_stopped()
|
activity_processor.ActivityProcessor().set_temp_stopped()
|
||||||
|
plexpy.initialize_scheduler()
|
||||||
|
|
||||||
|
|
||||||
def reconnect():
|
def reconnect():
|
||||||
|
@ -148,9 +149,8 @@ def run():
|
||||||
logger.info(u"Tautulli WebSocket :: Unable to get an internal response from the server, Plex server is down.")
|
logger.info(u"Tautulli WebSocket :: Unable to get an internal response from the server, Plex server is down.")
|
||||||
plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intdown'})
|
plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intdown'})
|
||||||
plexpy.PLEX_SERVER_UP = False
|
plexpy.PLEX_SERVER_UP = False
|
||||||
on_disconnect()
|
|
||||||
|
|
||||||
plexpy.initialize_scheduler()
|
on_disconnect()
|
||||||
|
|
||||||
logger.debug(u"Tautulli WebSocket :: Leaving thread.")
|
logger.debug(u"Tautulli WebSocket :: Leaving thread.")
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,8 @@ from hashing_passwords import check_hash
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import logger
|
import logger
|
||||||
import plextv
|
|
||||||
from plexpy.database import MonitorDatabase
|
from plexpy.database import MonitorDatabase
|
||||||
from plexpy.users import Users
|
from plexpy.users import Users, refresh_users
|
||||||
from plexpy.plextv import PlexTV
|
from plexpy.plextv import PlexTV
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +71,7 @@ def user_login(username=None, password=None):
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
# Refresh the users list to make sure we have all the correct permissions.
|
# Refresh the users list to make sure we have all the correct permissions.
|
||||||
plextv.refresh_users()
|
users.refresh_users()
|
||||||
# Successful login
|
# Successful login
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
import shutil
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
from cherrypy.lib.static import serve_file, serve_download
|
from cherrypy.lib.static import serve_file, serve_download
|
||||||
|
@ -119,7 +117,7 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi("get_server_list")
|
@addtoapi("get_server_list")
|
||||||
def discover(self, token=None, include_cloud=True, **kwargs):
|
def discover(self, token=None, include_cloud=True, all_servers=False, **kwargs):
|
||||||
""" Get all your servers that are published to Plex.tv.
|
""" Get all your servers that are published to Plex.tv.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -150,12 +148,14 @@ class WebInterface(object):
|
||||||
plexpy.CONFIG.write()
|
plexpy.CONFIG.write()
|
||||||
|
|
||||||
include_cloud = not (include_cloud == 'false')
|
include_cloud = not (include_cloud == 'false')
|
||||||
|
all_servers = all_servers == 'true'
|
||||||
|
|
||||||
plex_tv = plextv.PlexTV()
|
plex_tv = plextv.PlexTV()
|
||||||
servers = plex_tv.discover(include_cloud=include_cloud)
|
servers_list = plex_tv.discover(include_cloud=include_cloud,
|
||||||
|
all_servers=all_servers)
|
||||||
|
|
||||||
if servers:
|
if servers_list:
|
||||||
return servers
|
return servers_list
|
||||||
|
|
||||||
|
|
||||||
##### Home #####
|
##### Home #####
|
||||||
|
@ -170,6 +170,7 @@ class WebInterface(object):
|
||||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||||
"home_stats_recently_added_count": plexpy.CONFIG.HOME_STATS_RECENTLY_ADDED_COUNT,
|
"home_stats_recently_added_count": plexpy.CONFIG.HOME_STATS_RECENTLY_ADDED_COUNT,
|
||||||
"pms_name": plexpy.CONFIG.PMS_NAME,
|
"pms_name": plexpy.CONFIG.PMS_NAME,
|
||||||
|
"pms_is_cloud": plexpy.CONFIG.PMS_IS_CLOUD,
|
||||||
"update_show_changelog": plexpy.CONFIG.UPDATE_SHOW_CHANGELOG
|
"update_show_changelog": plexpy.CONFIG.UPDATE_SHOW_CHANGELOG
|
||||||
}
|
}
|
||||||
return serve_template(templatename="index.html", title="Home", config=config)
|
return serve_template(templatename="index.html", title="Home", config=config)
|
||||||
|
@ -452,7 +453,7 @@ class WebInterface(object):
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def refresh_libraries_list(self, **kwargs):
|
def refresh_libraries_list(self, **kwargs):
|
||||||
""" Refresh the libraries list on it's own thread. """
|
""" Refresh the libraries list on it's own thread. """
|
||||||
threading.Thread(target=pmsconnect.refresh_libraries).start()
|
threading.Thread(target=libraries.refresh_libraries).start()
|
||||||
logger.info(u"Manual libraries list refresh requested.")
|
logger.info(u"Manual libraries list refresh requested.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1074,7 +1075,7 @@ class WebInterface(object):
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def refresh_users_list(self, **kwargs):
|
def refresh_users_list(self, **kwargs):
|
||||||
""" Refresh the users list on it's own thread. """
|
""" Refresh the users list on it's own thread. """
|
||||||
threading.Thread(target=plextv.refresh_users).start()
|
threading.Thread(target=users.refresh_users).start()
|
||||||
logger.info(u"Manual users list refresh requested.")
|
logger.info(u"Manual users list refresh requested.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -2559,6 +2560,8 @@ class WebInterface(object):
|
||||||
"pms_port": plexpy.CONFIG.PMS_PORT,
|
"pms_port": plexpy.CONFIG.PMS_PORT,
|
||||||
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
||||||
"pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
|
"pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
|
||||||
|
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
|
||||||
|
"pms_is_cloud": plexpy.CONFIG.PMS_IS_CLOUD,
|
||||||
"pms_url_manual": checked(plexpy.CONFIG.PMS_URL_MANUAL),
|
"pms_url_manual": checked(plexpy.CONFIG.PMS_URL_MANUAL),
|
||||||
"pms_uuid": plexpy.CONFIG.PMS_UUID,
|
"pms_uuid": plexpy.CONFIG.PMS_UUID,
|
||||||
"pms_web_url": plexpy.CONFIG.PMS_WEB_URL,
|
"pms_web_url": plexpy.CONFIG.PMS_WEB_URL,
|
||||||
|
@ -2576,7 +2579,6 @@ class WebInterface(object):
|
||||||
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
||||||
"refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
"refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
||||||
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
||||||
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
|
|
||||||
"notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
|
"notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
|
||||||
"notify_upload_posters": checked(plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS),
|
"notify_upload_posters": checked(plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS),
|
||||||
"notify_recently_added_upgrade": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_UPGRADE),
|
"notify_recently_added_upgrade": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_UPGRADE),
|
||||||
|
@ -2733,8 +2735,7 @@ class WebInterface(object):
|
||||||
|
|
||||||
# Get new server URLs for SSL communications and get new server friendly name
|
# Get new server URLs for SSL communications and get new server friendly name
|
||||||
if server_changed:
|
if server_changed:
|
||||||
plextv.get_real_pms_url()
|
plextv.get_server_resources()
|
||||||
pmsconnect.get_server_friendly_name()
|
|
||||||
web_socket.reconnect()
|
web_socket.reconnect()
|
||||||
|
|
||||||
# If first run, start websocket
|
# If first run, start websocket
|
||||||
|
@ -2751,11 +2752,11 @@ class WebInterface(object):
|
||||||
|
|
||||||
# Refresh users table if our server IP changes.
|
# Refresh users table if our server IP changes.
|
||||||
if refresh_libraries:
|
if refresh_libraries:
|
||||||
threading.Thread(target=pmsconnect.refresh_libraries).start()
|
threading.Thread(target=libraries.refresh_libraries).start()
|
||||||
|
|
||||||
# Refresh users table if our server IP changes.
|
# Refresh users table if our server IP changes.
|
||||||
if refresh_users:
|
if refresh_users:
|
||||||
threading.Thread(target=plextv.refresh_users).start()
|
threading.Thread(target=users.refresh_users).start()
|
||||||
|
|
||||||
return {'result': 'success', 'message': 'Settings saved.'}
|
return {'result': 'success', 'message': 'Settings saved.'}
|
||||||
|
|
||||||
|
@ -3425,16 +3426,15 @@ class WebInterface(object):
|
||||||
# Fallback to checking /identity endpoint is server is unpublished
|
# Fallback to checking /identity endpoint is server is unpublished
|
||||||
# Cannot set SSL settings on the PMS if unpublished so 'http' is okay
|
# Cannot set SSL settings on the PMS if unpublished so 'http' is okay
|
||||||
if not identifier:
|
if not identifier:
|
||||||
request_handler = http_handler.HTTPHandler(host=hostname,
|
scheme = 'https' if ssl else 'http'
|
||||||
port=port,
|
url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, hostname=hostname, port=port)
|
||||||
token=None)
|
|
||||||
uri = '/identity'
|
uri = '/identity'
|
||||||
|
|
||||||
|
request_handler = http_handler.HTTPHandler(urls=url,
|
||||||
|
ssl_verify=False)
|
||||||
request = request_handler.make_request(uri=uri,
|
request = request_handler.make_request(uri=uri,
|
||||||
proto='http',
|
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
output_format='xml',
|
output_format='xml')
|
||||||
no_token=True,
|
|
||||||
timeout=10)
|
|
||||||
if request:
|
if request:
|
||||||
xml_head = request.getElementsByTagName('MediaContainer')[0]
|
xml_head = request.getElementsByTagName('MediaContainer')[0]
|
||||||
identifier = xml_head.getAttribute('machineIdentifier')
|
identifier = xml_head.getAttribute('machineIdentifier')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue