Update server connection code

This commit is contained in:
JonnyWong16 2017-12-24 14:01:16 -08:00
parent 29632b0805
commit 15faccfa2f
16 changed files with 530 additions and 535 deletions

View file

@ -653,7 +653,7 @@
</div>
<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="col-md-6">
<div class="input-group">

View file

@ -36,12 +36,14 @@ import activity_handler
import activity_pinger
import config
import database
import libraries
import logger
import mobile_app
import notification_handler
import notifiers
import plextv
import pmsconnect
import users
import versioncheck
import plexpy.config
@ -213,16 +215,15 @@ def initialize(config_file):
# Get the real PMS urls for SSL and remote access
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
plextv.get_real_pms_url()
pmsconnect.get_server_friendly_name()
plextv.get_server_resources()
# Refresh the users list on startup
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
plextv.refresh_users()
users.refresh_users()
# Refresh the libraries list 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
UMASK = os.umask(0)
@ -323,14 +324,8 @@ def initialize_scheduler():
hours=backup_hours, minutes=0, seconds=0, args=(True, True))
if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
#schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
# 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',
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
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',
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
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)
schedule_job(pmsconnect.refresh_libraries, 'Refresh libraries list',
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
hours=library_hours, minutes=0, seconds=0)
schedule_job(activity_pinger.check_server_response, 'Check server response',
@ -351,9 +346,7 @@ def initialize_scheduler():
else:
# Cancel all jobs
schedule_job(plextv.get_real_pms_url, 'Refresh Plex server URLs',
hours=0, minutes=0, seconds=0)
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex server name',
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
hours=0, minutes=0, seconds=0)
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',
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)
schedule_job(pmsconnect.refresh_libraries, 'Refresh libraries list',
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
hours=0, minutes=0, seconds=0)
# Schedule job to reconnect websocket

View file

@ -278,7 +278,6 @@ def check_server_response():
def check_server_access():
with monitor_lock:
pms_connect = pmsconnect.PmsConnect()
server_response = pms_connect.get_server_response()

View file

@ -32,10 +32,10 @@ import xmltodict
import plexpy
import config
import database
import libraries
import logger
import mobile_app
import plextv
import pmsconnect
import users
class API2:
@ -345,14 +345,14 @@ class API2:
def refresh_libraries_list(self, **kwargs):
""" Refresh the Tautulli libraries list."""
data = pmsconnect.refresh_libraries()
data = libraries.refresh_libraries()
self._api_result_type = 'success' if data else 'error'
return data
def refresh_users_list(self, **kwargs):
""" Refresh the Tautulli users list."""
data = plextv.refresh_users()
data = users.refresh_users()
self._api_result_type = 'success' if data else 'error'
return data

View file

@ -140,10 +140,10 @@ SCHEDULER_LIST = ['Check GitHub for updates',
'Check for recently added items',
'Check for Plex updates',
'Check for Plex remote access',
'Check server response',
'Refresh users list',
'Refresh libraries list',
'Refresh Plex server URLs',
'Refresh Plex server name',
'Backup Tautulli database',
'Backup Tautulli config'
]

View file

@ -45,6 +45,7 @@ _CONFIG_DEFINITIONS = {
'PLEXWATCH_DATABASE': (str, 'PlexWatch', ''),
'PMS_IDENTIFIER': (str, 'PMS', ''),
'PMS_IP': (str, 'PMS', '127.0.0.1'),
'PMS_IS_CLOUD': (int, 'PMS', 0),
'PMS_IS_REMOTE': (int, 'PMS', 0),
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000),

View file

@ -1,24 +1,28 @@
#!/usr/bin/env python
# -*- 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
# the Free Software Foundation, either version 3 of the License, or
# (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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# 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 httplib import HTTPConnection
import ssl
from functools import partial
from multiprocessing.dummy import Pool as ThreadPool
from urlparse import urljoin
import certifi
from requests.packages import urllib3
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import plexpy
import helpers
@ -30,94 +34,144 @@ class HTTPHandler(object):
Retrieve data from Plex Server
"""
def __init__(self, host, port, token, ssl_verify=True):
self.host = host
self.port = str(port)
def __init__(self, urls, token=None, timeout=10, ssl_verify=True):
if isinstance(urls, basestring):
self.urls = urls.split() or urls.split(',')
else:
self.urls = urls
self.token = token
if self.token:
self.headers = {'X-Plex-Token': self.token}
else:
self.headers = {}
self.timeout = timeout
self.ssl_verify = ssl_verify
"""
Handle the HTTP requests.
self.valid_request_types = ('GET', 'POST', 'PUT', 'DELETE')
Output: object
"""
def make_request(self,
uri=None, proto='HTTP',
request_type='GET',
uri=None,
headers=None,
request_type='GET',
output_format='raw',
return_type=False,
no_token=False,
timeout=None):
timeout=None,
callback=None):
"""
Handle the HTTP requests.
if timeout is None:
timeout = plexpy.CONFIG.PMS_TIMEOUT
Output: list
"""
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.")
return None
if uri:
if proto.upper() == 'HTTPS':
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)
request_urls = [urljoin(url, self.uri) for url in self.urls]
if not no_token:
if headers:
headers.update({'X-Plex-Token': self.token})
else:
headers = {'X-Plex-Token': self.token}
if no_token and headers:
self.headers = headers
elif headers:
self.headers.update(headers)
try:
if headers:
handler.request(request_type, uri, headers=headers)
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
responses = []
for r in self._http_requests_pool(request_urls):
responses.append(r)
if request_status in (200, 201):
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
return responses[0]
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:
logger.debug(u"HTTP request made but no enpoint given.")
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

View file

@ -27,6 +27,66 @@ import pmsconnect
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():
plexpy.CONFIG.UPDATE_SECTION_IDS = -1
@ -965,7 +1025,7 @@ class Libraries(object):
monitor_db = database.MonitorDatabase()
# 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

View file

@ -23,7 +23,6 @@ import activity_processor
import database
import helpers
import logger
import plextv
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
try:
plextv.refresh_users()
users.refresh_users()
except:
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
return None

View file

@ -18,11 +18,9 @@
import base64
import json
from xml.dom import minidom
import plexpy
import common
import database
import helpers
import http_handler
import logger
@ -31,129 +29,99 @@ import pmsconnect
import session
def refresh_users():
logger.info(u"Tautulli PlexTV :: Requesting users list refresh...")
result = PlexTV().get_full_users_list()
def get_server_resources(return_presence=False):
if not return_presence:
logger.info(u"Tautulli PlexTV :: Requesting resources for server...")
monitor_db = database.MonitorDatabase()
user_data = users.Users()
server = {'pms_name': plexpy.CONFIG.PMS_NAME,
'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:
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 PlexTV :: Users list refreshed.")
return True
if server['pms_url_manual'] and server['pms_ssl'] or server['pms_is_cloud']:
scheme = 'https'
else:
logger.warn(u"Tautulli PlexTV :: Unable to refresh users list.")
return False
scheme = 'http'
def get_real_pms_url():
logger.info(u"Tautulli PlexTV :: Requesting URLs for server...")
# 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)
fallback_url = '{scheme}://{hostname}:{port}'.format(scheme=scheme,
hostname=server['pms_ip'],
port=server['pms_port'])
plex_tv = PlexTV()
result = plex_tv.get_server_urls(include_https=plexpy.CONFIG.PMS_SSL)
plexpass = plex_tv.get_plexpass_status()
result = plex_tv.get_server_connections(pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER,
pms_ip=server['pms_ip'],
pms_port=server['pms_port'],
include_https=server['pms_ssl'])
connections = []
if result:
plexpy.CONFIG.__setattr__('PMS_VERSION', result['version'])
plexpy.CONFIG.__setattr__('PMS_PLATFORM', result['platform'])
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', plexpass)
connections = result['connections']
connections = result.pop('connections', [])
server.update(result)
presence = server.pop('server_presence', 0)
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
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 plexpy.CONFIG.PMS_IS_REMOTE:
if server['pms_is_remote']:
# 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:
# 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:
# Get connection with matching address, otherwise return first connection
conn = next((c for c in conns if c['address'] == plexpy.CONFIG.PMS_IP
and c['port'] == str(plexpy.CONFIG.PMS_PORT)), conns[0])
plexpy.CONFIG.__setattr__('PMS_URL', conn['uri'])
plexpy.CONFIG.write()
conn = next((c for c in conns if c['address'] == server['pms_ip']
and c['port'] == str(server['pms_port'])), conns[0])
server['pms_url'] = conn['uri']
logger.info(u"Tautulli PlexTV :: Server URL retrieved.")
# get_server_urls() failed or PMS_URL not found, fallback url doesn't use SSL
if not plexpy.CONFIG.PMS_URL:
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
if not server['pms_url']:
server['pms_url'] = fallback_url
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:
if plexpy.CONFIG.PMS_URL_MANUAL and plexpy.CONFIG.PMS_SSL:
fallback_url = fallback_url.replace('http://', 'https://')
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
server['pms_url'] = fallback_url
logger.info(u"Tautulli PlexTV :: Using user-defined URL.")
plexpy.CONFIG.process_kwargs(server)
plexpy.CONFIG.write()
class PlexTV(object):
"""
Plex.tv authentication
"""
def __init__(self, username='', password='', token=None):
self.protocol = 'HTTPS'
def __init__(self, username=None, password=None, token=None):
self.username = username
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
if not token:
if not self.token:
# Check if we should use the admin token, or the guest server token
if session.get_session_user_id():
user_data = users.Users()
@ -161,12 +129,14 @@ class PlexTV(object):
self.token = user_tokens['server_token']
else:
self.token = plexpy.CONFIG.PMS_TOKEN
else:
self.token = token
self.request_handler = http_handler.HTTPHandler(host='plex.tv',
port=443,
if not self.token:
logger.error(u"Tautulli PlexTV :: PlexTV called, but no token provided.")
return
self.request_handler = http_handler.HTTPHandler(urls=self.urls,
token=self.token,
timeout=self.timeout,
ssl_verify=self.ssl_verify)
def get_plex_auth(self, output_format='raw'):
@ -183,7 +153,6 @@ class PlexTV(object):
}
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='POST',
headers=headers,
output_format=output_format,
@ -265,7 +234,6 @@ class PlexTV(object):
def get_plextv_friends(self, output_format=''):
uri = '/api/users'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -274,7 +242,6 @@ class PlexTV(object):
def get_plextv_user_details(self, output_format=''):
uri = '/users/account'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -283,7 +250,6 @@ class PlexTV(object):
def get_plextv_devices_list(self, output_format=''):
uri = '/devices.xml'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -292,7 +258,6 @@ class PlexTV(object):
def get_plextv_server_list(self, output_format=''):
uri = '/pms/servers.xml'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -301,7 +266,6 @@ class PlexTV(object):
def get_plextv_sync_lists(self, machine_id='', output_format=''):
uri = '/servers/%s/sync_lists' % machine_id
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -313,7 +277,6 @@ class PlexTV(object):
else:
uri = '/api/resources'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -325,7 +288,6 @@ class PlexTV(object):
else:
uri = '/api/downloads/1.json'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -334,7 +296,6 @@ class PlexTV(object):
def delete_plextv_device(self, device_id='', output_format=''):
uri = '/devices/%s.xml' % device_id
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='DELETE',
output_format=output_format)
@ -343,7 +304,6 @@ class PlexTV(object):
def delete_plextv_device_sync_lists(self, client_id='', output_format=''):
uri = '/devices/%s/sync_items' % client_id
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -352,197 +312,174 @@ class PlexTV(object):
def delete_plextv_sync(self, client_id='', sync_id='', output_format=''):
uri = '/devices/%s/sync_items/%s' % (client_id, sync_id)
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='DELETE',
output_format=output_format)
return request
def get_full_users_list(self):
friends_list = self.get_plextv_friends()
own_account = self.get_plextv_user_details()
friends_list = self.get_plextv_friends(output_format='xml')
own_account = self.get_plextv_user_details(output_format='xml')
users_list = []
try:
xml_parse = minidom.parseString(own_account)
xml_head = own_account.getElementsByTagName('user')
except Exception as e:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list own account: %s" % e)
return []
except:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list own account.")
return []
logger.warn(u"Tautulli PlexTV :: Unable to parse own account XML for get_full_users_list: %s." % e)
return {}
xml_head = xml_parse.getElementsByTagName('user')
if not xml_head:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list.")
else:
for a in xml_head:
own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
"username": helpers.get_xml_attr(a, 'username'),
"thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'),
"is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": None,
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
"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')
}
for a in xml_head:
own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
"username": helpers.get_xml_attr(a, 'username'),
"thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'),
"is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": None,
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
"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:
xml_parse = minidom.parseString(friends_list)
xml_head = friends_list.getElementsByTagName('User')
except Exception as e:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list friends list: %s" % e)
return []
except:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list friends list.")
return []
logger.warn(u"Tautulli PlexTV :: Unable to parse friends list XML for get_full_users_list: %s." % e)
return {}
xml_head = xml_parse.getElementsByTagName('User')
if not xml_head:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_full_users_list.")
else:
for a in xml_head:
friend = {"user_id": helpers.get_xml_attr(a, 'id'),
"username": helpers.get_xml_attr(a, 'title'),
"thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'),
"is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
"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')
}
for a in xml_head:
friend = {"user_id": helpers.get_xml_attr(a, 'id'),
"username": helpers.get_xml_attr(a, 'title'),
"thumb": helpers.get_xml_attr(a, 'thumb'),
"email": helpers.get_xml_attr(a, 'email'),
"is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
"is_restricted": helpers.get_xml_attr(a, 'restricted'),
"filter_all": helpers.get_xml_attr(a, 'filterAll'),
"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
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()
synced_items = []
try:
xml_parse = minidom.parseString(sync_list)
xml_head = sync_list.getElementsByTagName('SyncList')
except Exception as e:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s" % e)
return []
except:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items.")
return []
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s." % e)
return {}
xml_head = xml_parse.getElementsByTagName('SyncList')
for a in xml_head:
client_id = helpers.get_xml_attr(a, 'clientIdentifier')
if not xml_head:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_synced_items.")
else:
for a in xml_head:
client_id = helpers.get_xml_attr(a, 'clientIdentifier')
# Filter by client_id
if client_id_filter and client_id_filter != client_id:
continue
# Filter by client_id
if client_id_filter and client_id_filter != client_id:
continue
sync_id = helpers.get_xml_attr(a, 'id')
sync_device = a.getElementsByTagName('Device')
sync_id = helpers.get_xml_attr(a, 'id')
sync_device = a.getElementsByTagName('Device')
for device in sync_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:
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')
# Filter by user_id
if user_id_filter and user_id_filter != device_user_id:
continue
# Filter by user_id
if user_id_filter and user_id_filter != device_user_id:
continue
for synced in a.getElementsByTagName('SyncItems'):
sync_item = synced.getElementsByTagName('SyncItem')
for item in sync_item:
for synced in a.getElementsByTagName('SyncItems'):
sync_item = synced.getElementsByTagName('SyncItem')
for item in sync_item:
for location in item.getElementsByTagName('Location'):
clean_uri = helpers.get_xml_attr(location, 'uri').split('%2F')
for location in item.getElementsByTagName('Location'):
clean_uri = helpers.get_xml_attr(location, 'uri').split('%2F')
rating_key = next((clean_uri[(idx + 1) % len(clean_uri)]
for idx, item in enumerate(clean_uri) if item == 'metadata'), None)
rating_key = next((clean_uri[(idx + 1) % len(clean_uri)]
for idx, item in enumerate(clean_uri) if item == 'metadata'), None)
# Filter by rating_key
if rating_key_filter and rating_key_filter != rating_key:
continue
# Filter by rating_key
if rating_key_filter and rating_key_filter != rating_key:
continue
sync_id = helpers.get_xml_attr(item, 'id')
sync_version = helpers.get_xml_attr(item, 'version')
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')
sync_version = helpers.get_xml_attr(item, 'version')
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')
for status in item.getElementsByTagName('Status'):
status_failure_code = helpers.get_xml_attr(status, 'failureCode')
status_failure = helpers.get_xml_attr(status, 'failure')
status_state = helpers.get_xml_attr(status, 'state')
status_item_count = helpers.get_xml_attr(status, 'itemsCount')
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'):
status_failure_code = helpers.get_xml_attr(status, 'failureCode')
status_failure = helpers.get_xml_attr(status, 'failure')
status_state = helpers.get_xml_attr(status, 'state')
status_item_count = helpers.get_xml_attr(status, 'itemsCount')
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 settings in item.getElementsByTagName('MediaSettings'):
settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
settings_music_bitrate = helpers.get_xml_attr(settings, 'musicBitrate')
settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality')
settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution')
settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')
for settings in item.getElementsByTagName('MediaSettings'):
settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
settings_music_bitrate = helpers.get_xml_attr(settings, 'musicBitrate')
settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality')
settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution')
settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')
sync_details = {"device_name": helpers.sanitize(device_name),
"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
}
sync_details = {"device_name": helpers.sanitize(device_name),
"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)
synced_items.append(sync_details)
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)
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:
server_id = plexpy.CONFIG.PMS_IDENTIFIER
else:
logger.error(u"Tautulli PlexTV :: Unable to retrieve server identity.")
if not pms_identifier:
logger.error(u"Tautulli PlexTV :: Unable to retrieve server connections: no pms_identifier provided.")
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:
xml_parse = minidom.parseString(plextv_resources)
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')
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 {}
@ -580,16 +506,20 @@ class PlexTV(object):
conn = []
connections = device.getElementsByTagName('Connection')
server = {"platform": helpers.get_xml_attr(device, 'platform'),
"version": helpers.get_xml_attr(device, 'productVersion')
server = {'pms_identifier': helpers.get_xml_attr(device, 'clientIdentifier'),
'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:
server_details = {"protocol": helpers.get_xml_attr(c, 'protocol'),
"address": helpers.get_xml_attr(c, 'address'),
"port": helpers.get_xml_attr(c, 'port'),
"uri": helpers.get_xml_attr(c, 'uri'),
"local": helpers.get_xml_attr(c, 'local')
server_details = {'protocol': helpers.get_xml_attr(c, 'protocol'),
'address': helpers.get_xml_attr(c, 'address'),
'port': helpers.get_xml_attr(c, 'port'),
'uri': helpers.get_xml_attr(c, 'uri'),
'local': helpers.get_xml_attr(c, 'local')
}
conn.append(server_details)
@ -600,7 +530,7 @@ class PlexTV(object):
# Try to match the device
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)
break
@ -612,15 +542,8 @@ class PlexTV(object):
connections = a.getElementsByTagName('Connection')
for connection in connections:
if helpers.get_xml_attr(connection, 'address') == plexpy.CONFIG.PMS_IP and \
int(helpers.get_xml_attr(connection, 'port')) == plexpy.CONFIG.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))
if helpers.get_xml_attr(connection, 'address') == pms_ip and \
helpers.get_xml_attr(connection, 'port') == str(pms_port):
server = get_connections(a)
break
@ -649,7 +572,7 @@ class PlexTV(object):
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 """
servers = self.get_plextv_resources(include_https=True, output_format='xml')
clean_servers = []
@ -679,15 +602,16 @@ class PlexTV(object):
connections = d.getElementsByTagName('Connection')
for c in connections:
# If this is a remote server don't show any local IPs.
if helpers.get_xml_attr(d, 'publicAddressMatches') == '0' and \
helpers.get_xml_attr(c, 'local') == '1':
continue
if not all_servers:
# If this is a remote server don't show any local IPs.
if helpers.get_xml_attr(d, 'publicAddressMatches') == '0' and \
helpers.get_xml_attr(c, 'local') == '1':
continue
# If this is a local server don't show any remote IPs.
if helpers.get_xml_attr(d, 'publicAddressMatches') == '1' and \
helpers.get_xml_attr(c, 'local') == '0':
continue
# If this is a local server don't show any remote IPs.
if helpers.get_xml_attr(d, 'publicAddressMatches') == '1' and \
helpers.get_xml_attr(c, 'local') == '0':
continue
server = {'httpsRequired': helpers.get_xml_attr(d, 'httpsRequired'),
'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'),
@ -770,8 +694,6 @@ class PlexTV(object):
return True
else:
logger.debug(u"Tautulli PlexTV :: Plex Pass subscription not found.")
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', 0)
plexpy.CONFIG.write()
return False
def get_devices_list(self):

View file

@ -22,7 +22,6 @@ import activity_processor
import database
import helpers
import logger
import plextv
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
try:
plextv.refresh_users()
users.refresh_users()
except:
logger.debug(u"Tautulli Importer :: Unable to refresh the users list. Aborting import.")
return None

View file

@ -13,16 +13,12 @@
# You should have received a copy of the GNU General Public License
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
import threading
import urllib
from urlparse import urlparse
import plexpy
import common
import database
import helpers
import http_handler
import libraries
import logger
import plextv
import session
@ -49,83 +45,23 @@ def get_server_friendly_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):
"""
Retrieve data from Plex Server
"""
def __init__(self, token=None):
if plexpy.CONFIG.PMS_URL:
url_parsed = urlparse(plexpy.CONFIG.PMS_URL)
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'
def __init__(self, url=None, token=None):
self.url = url
self.token = token
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
if session.get_session_user_id():
user_data = users.Users()
@ -133,12 +69,10 @@ class PmsConnect(object):
self.token = user_tokens['server_token']
else:
self.token = plexpy.CONFIG.PMS_TOKEN
else:
self.token = token
self.request_handler = http_handler.HTTPHandler(host=hostname,
port=port,
token=self.token)
self.request_handler = http_handler.HTTPHandler(urls=self.url,
token=self.token,
timeout=self.timeout)
def get_sessions(self, output_format=''):
"""
@ -150,7 +84,6 @@ class PmsConnect(object):
"""
uri = '/status/sessions'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -166,7 +99,6 @@ class PmsConnect(object):
"""
uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, reason)
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -183,7 +115,6 @@ class PmsConnect(object):
"""
uri = '/library/metadata/' + rating_key
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -200,7 +131,6 @@ class PmsConnect(object):
"""
uri = '/library/metadata/' + rating_key + '/children'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
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)
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
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)
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -251,7 +179,6 @@ class PmsConnect(object):
"""
uri = '/library/metadata/' + rating_key + '/children'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -285,7 +212,6 @@ class PmsConnect(object):
"""
uri = '/library/metadata/' + rating_key + '/allLeaves'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -301,7 +227,6 @@ class PmsConnect(object):
"""
uri = '/servers'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -317,7 +242,6 @@ class PmsConnect(object):
"""
uri = '/:/prefs'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -333,7 +257,6 @@ class PmsConnect(object):
"""
uri = '/identity'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -349,7 +272,6 @@ class PmsConnect(object):
"""
uri = '/library/sections'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
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
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -384,7 +305,6 @@ class PmsConnect(object):
"""
uri = '/library/sections/' + section_id + '/label'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -401,7 +321,6 @@ class PmsConnect(object):
"""
uri = '/sync/items/' + sync_id
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -417,7 +336,6 @@ class PmsConnect(object):
"""
uri = '/sync/transcodeQueue'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -433,7 +351,6 @@ class PmsConnect(object):
"""
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -449,7 +366,6 @@ class PmsConnect(object):
"""
uri = '/myplex/account'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -465,7 +381,6 @@ class PmsConnect(object):
"""
uri = '/myplex/refreshReachability'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='PUT')
return request
@ -480,7 +395,6 @@ class PmsConnect(object):
"""
uri = '/updater/check?download=0'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='PUT',
output_format=output_format)
@ -496,7 +410,6 @@ class PmsConnect(object):
"""
uri = '/updater/status'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
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)
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
@ -2334,7 +2246,6 @@ class PmsConnect(object):
uri = '/photo/:/transcode?%s' % urllib.urlencode(params)
result = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
return_type=True)

View file

@ -23,9 +23,67 @@ import datatables
import helpers
import logger
import plextv
import pmsconnect
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):
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."
% 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
plextv.refresh_users()
refresh_users()
user_details = get_user_details(user_id=user_id, user=user)

View file

@ -42,6 +42,7 @@ def start_thread():
def on_disconnect():
activity_processor.ActivityProcessor().set_temp_stopped()
plexpy.initialize_scheduler()
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.")
plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intdown'})
plexpy.PLEX_SERVER_UP = False
on_disconnect()
plexpy.initialize_scheduler()
on_disconnect()
logger.debug(u"Tautulli WebSocket :: Leaving thread.")

View file

@ -27,9 +27,8 @@ from hashing_passwords import check_hash
import plexpy
import logger
import plextv
from plexpy.database import MonitorDatabase
from plexpy.users import Users
from plexpy.users import Users, refresh_users
from plexpy.plextv import PlexTV
@ -72,7 +71,7 @@ def user_login(username=None, password=None):
if result:
# Refresh the users list to make sure we have all the correct permissions.
plextv.refresh_users()
users.refresh_users()
# Successful login
return True
else:

View file

@ -16,10 +16,8 @@
import hashlib
import json
import os
import random
import shutil
import threading
import uuid
import cherrypy
from cherrypy.lib.static import serve_file, serve_download
@ -119,7 +117,7 @@ class WebInterface(object):
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
@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.
```
@ -150,12 +148,14 @@ class WebInterface(object):
plexpy.CONFIG.write()
include_cloud = not (include_cloud == 'false')
all_servers = all_servers == 'true'
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:
return servers
if servers_list:
return servers_list
##### Home #####
@ -170,6 +170,7 @@ class WebInterface(object):
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
"home_stats_recently_added_count": plexpy.CONFIG.HOME_STATS_RECENTLY_ADDED_COUNT,
"pms_name": plexpy.CONFIG.PMS_NAME,
"pms_is_cloud": plexpy.CONFIG.PMS_IS_CLOUD,
"update_show_changelog": plexpy.CONFIG.UPDATE_SHOW_CHANGELOG
}
return serve_template(templatename="index.html", title="Home", config=config)
@ -452,7 +453,7 @@ class WebInterface(object):
@requireAuth(member_of("admin"))
def refresh_libraries_list(self, **kwargs):
""" 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.")
return True
@ -1074,7 +1075,7 @@ class WebInterface(object):
@requireAuth(member_of("admin"))
def refresh_users_list(self, **kwargs):
""" 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.")
return True
@ -2559,6 +2560,8 @@ class WebInterface(object):
"pms_port": plexpy.CONFIG.PMS_PORT,
"pms_token": plexpy.CONFIG.PMS_TOKEN,
"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_uuid": plexpy.CONFIG.PMS_UUID,
"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_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
"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_upload_posters": checked(plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS),
"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
if server_changed:
plextv.get_real_pms_url()
pmsconnect.get_server_friendly_name()
plextv.get_server_resources()
web_socket.reconnect()
# If first run, start websocket
@ -2751,11 +2752,11 @@ class WebInterface(object):
# Refresh users table if our server IP changes.
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.
if refresh_users:
threading.Thread(target=plextv.refresh_users).start()
threading.Thread(target=users.refresh_users).start()
return {'result': 'success', 'message': 'Settings saved.'}
@ -3425,16 +3426,15 @@ class WebInterface(object):
# Fallback to checking /identity endpoint is server is unpublished
# Cannot set SSL settings on the PMS if unpublished so 'http' is okay
if not identifier:
request_handler = http_handler.HTTPHandler(host=hostname,
port=port,
token=None)
scheme = 'https' if ssl else 'http'
url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, hostname=hostname, port=port)
uri = '/identity'
request_handler = http_handler.HTTPHandler(urls=url,
ssl_verify=False)
request = request_handler.make_request(uri=uri,
proto='http',
request_type='GET',
output_format='xml',
no_token=True,
timeout=10)
output_format='xml')
if request:
xml_head = request.getElementsByTagName('MediaContainer')[0]
identifier = xml_head.getAttribute('machineIdentifier')