Files
JellyPy/plexpy/plextv.py
2019-11-23 19:21:10 -08:00

929 lines
42 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of Tautulli.
#
# Tautulli 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,
# 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/>.
from __future__ import absolute_import
from __future__ import unicode_literals
from builtins import next
from builtins import str
from builtins import object
import base64
import json
import plexpy
from plexpy import common
from plexpy import helpers
from plexpy import http_handler
from plexpy import logger
from plexpy import users
from plexpy import pmsconnect
from plexpy import session
def get_server_resources(return_presence=False, return_server=False, **kwargs):
if not return_presence:
logger.info("Tautulli PlexTV :: Requesting resources for server...")
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,
'pms_identifier': plexpy.CONFIG.PMS_IDENTIFIER
}
if kwargs:
server.update(kwargs)
for k in ['pms_ssl', 'pms_is_remote', 'pms_is_cloud', 'pms_url_manual']:
server[k] = int(server[k])
if server['pms_url_manual'] and server['pms_ssl'] or server['pms_is_cloud']:
scheme = 'https'
else:
scheme = 'http'
fallback_url = '{scheme}://{hostname}:{port}'.format(scheme=scheme,
hostname=server['pms_ip'],
port=server['pms_port'])
plex_tv = PlexTV()
result = plex_tv.get_server_connections(pms_identifier=server['pms_identifier'],
pms_ip=server['pms_ip'],
pms_port=server['pms_port'],
include_https=server['pms_ssl'])
if result:
connections = result.pop('connections', [])
server.update(result)
presence = server.pop('pms_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 server['pms_url_manual'] and server['pms_ssl']:
if connections:
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'] 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'] 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'] == server['pms_ip']
and c['port'] == str(server['pms_port'])), conns[0])
server['pms_url'] = conn['uri']
logger.info("Tautulli PlexTV :: Server URL retrieved.")
# get_server_urls() failed or PMS_URL not found, fallback url doesn't use SSL
if not server['pms_url']:
server['pms_url'] = fallback_url
logger.warn("Tautulli PlexTV :: Unable to retrieve server URLs. Using user-defined value without SSL.")
# Not using SSL, remote has no effect
else:
server['pms_url'] = fallback_url
logger.info("Tautulli PlexTV :: Using user-defined URL.")
if return_server:
return server
plexpy.CONFIG.process_kwargs(server)
plexpy.CONFIG.write()
class PlexTV(object):
"""
Plex.tv authentication
"""
def __init__(self, username=None, password=None, token=None, headers=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 self.username is None and self.password is None:
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()
user_tokens = user_data.get_tokens(user_id=session.get_session_user_id())
self.token = user_tokens['server_token']
else:
self.token = plexpy.CONFIG.PMS_TOKEN
if not self.token:
logger.error("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,
headers=headers)
def get_plex_auth(self, output_format='raw'):
uri = '/users/sign_in.xml'
base64string = base64.b64encode(('%s:%s' % (self.username, self.password)).encode('utf-8'))
headers = {'Content-Type': 'application/xml; charset=utf-8',
'Authorization': 'Basic %s' % base64string}
request = self.request_handler.make_request(uri=uri,
request_type='POST',
headers=headers,
output_format=output_format,
no_token=True)
return request
def get_token(self):
plextv_response = self.get_plex_auth(output_format='xml')
if plextv_response:
try:
xml_head = plextv_response.getElementsByTagName('user')
if xml_head:
user = {'auth_token': xml_head[0].getAttribute('authenticationToken'),
'user_id': xml_head[0].getAttribute('id')
}
else:
logger.warn("Tautulli PlexTV :: Could not get Plex authentication token.")
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_token: %s." % e)
return None
return user
else:
return None
def get_plexpy_pms_token(self, force=False):
if force:
logger.debug("Tautulli PlexTV :: Forcing refresh of Plex.tv token.")
devices_list = self.get_devices_list()
device_id = next((d for d in devices_list if d['device_identifier'] == plexpy.CONFIG.PMS_UUID), {}).get('device_id', None)
if device_id:
logger.debug("Tautulli PlexTV :: Removing Tautulli from Plex.tv devices.")
try:
self.delete_plextv_device(device_id=device_id)
except:
logger.error("Tautulli PlexTV :: Failed to remove Tautulli from Plex.tv devices.")
return None
else:
logger.warn("Tautulli PlexTV :: No existing Tautulli device found.")
logger.info("Tautulli PlexTV :: Fetching a new Plex.tv token for Tautulli.")
user = self.get_token()
if user:
token = user['auth_token']
plexpy.CONFIG.__setattr__('PMS_TOKEN', token)
plexpy.CONFIG.write()
logger.info("Tautulli PlexTV :: Updated Plex.tv token for Tautulli.")
return token
def get_server_token(self):
servers = self.get_plextv_resources(output_format='xml')
server_token = ''
try:
xml_head = servers.getElementsByTagName('Device')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_token: %s." % e)
return None
for a in xml_head:
if helpers.get_xml_attr(a, 'clientIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER \
and 'server' in helpers.get_xml_attr(a, 'provides'):
server_token = helpers.get_xml_attr(a, 'accessToken')
break
return server_token
def get_plextv_pin(self, pin='', output_format=''):
if pin:
uri = '/api/v2/pins/' + pin
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format,
no_token=True)
else:
uri = '/api/v2/pins?strong=true'
request = self.request_handler.make_request(uri=uri,
request_type='POST',
output_format=output_format,
no_token=True)
return request
def get_pin(self, pin=''):
plextv_response = self.get_plextv_pin(pin=pin,
output_format='xml')
if plextv_response:
try:
xml_head = plextv_response.getElementsByTagName('pin')
if xml_head:
pin = {'id': xml_head[0].getAttribute('id'),
'code': xml_head[0].getAttribute('code'),
'token': xml_head[0].getAttribute('authToken')
}
return pin
else:
logger.warn("Tautulli PlexTV :: Could not get Plex authentication pin.")
return None
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_pin: %s." % e)
return None
else:
return None
def get_plextv_user_data(self):
plextv_response = self.get_plex_auth(output_format='dict')
if plextv_response:
return plextv_response
else:
return []
def get_plextv_friends(self, output_format=''):
uri = '/api/users'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_plextv_user_details(self, output_format=''):
uri = '/users/account'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_plextv_devices_list(self, output_format=''):
uri = '/devices.xml'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_plextv_server_list(self, output_format=''):
uri = '/pms/servers.xml'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_plextv_shared_servers(self, machine_id='', output_format=''):
uri = '/api/servers/%s/shared_servers' % machine_id
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
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,
request_type='GET',
output_format=output_format)
return request
def get_plextv_resources(self, include_https=False, output_format=''):
if include_https:
uri = '/api/resources?includeHttps=1'
else:
uri = '/api/resources'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_plextv_downloads(self, plexpass=False, output_format=''):
if plexpass:
uri = '/api/downloads/5.json?channel=plexpass'
else:
uri = '/api/downloads/1.json'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def delete_plextv_device(self, device_id='', output_format=''):
uri = '/devices/%s.xml' % device_id
request = self.request_handler.make_request(uri=uri,
request_type='DELETE',
output_format=output_format)
return request
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,
request_type='GET',
output_format=output_format)
return request
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,
request_type='DELETE',
output_format=output_format)
return request
def cloud_server_status(self, output_format=''):
uri = '/api/v2/cloud_server'
request = self.request_handler.make_request(uri=uri,
request_type='GET',
output_format=output_format)
return request
def get_full_users_list(self):
own_account = self.get_plextv_user_details(output_format='xml')
friends_list = self.get_plextv_friends(output_format='xml')
shared_servers = self.get_plextv_shared_servers(machine_id=plexpy.CONFIG.PMS_IDENTIFIER,
output_format='xml')
users_list = []
try:
xml_head = own_account.getElementsByTagName('user')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse own account XML for get_full_users_list: %s." % e)
return []
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_admin": 1,
"is_home_user": helpers.get_xml_attr(a, 'home'),
"is_allow_sync": 1,
"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'),
"user_token": helpers.get_xml_attr(a, 'authToken'),
"server_token": helpers.get_xml_attr(a, 'authToken'),
"shared_libraries": None,
}
users_list.append(own_details)
try:
xml_head = friends_list.getElementsByTagName('User')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse friends list XML for get_full_users_list: %s." % e)
return []
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_admin": 0,
"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)
try:
xml_head = shared_servers.getElementsByTagName('SharedServer')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse shared server list XML for get_full_users_list: %s." % e)
return []
user_map = {}
for a in xml_head:
user_id = helpers.get_xml_attr(a, 'userID')
server_token = helpers.get_xml_attr(a, 'accessToken')
sections = a.getElementsByTagName('Section')
shared_libraries = [helpers.get_xml_attr(s, 'key')
for s in sections if helpers.get_xml_attr(s, 'shared') == '1']
user_map[user_id] = {'server_token': server_token,
'shared_libraries': shared_libraries}
for u in users_list:
d = user_map.get(u['user_id'], {})
u.update(d)
return users_list
def get_synced_items(self, machine_id=None, client_id_filter=None, user_id_filter=None,
rating_key_filter=None, sync_id_filter=None):
if not machine_id:
machine_id = plexpy.CONFIG.PMS_IDENTIFIER
if isinstance(rating_key_filter, list):
rating_key_filter = [str(k) for k in rating_key_filter]
elif rating_key_filter:
rating_key_filter = [str(rating_key_filter)]
if isinstance(user_id_filter, list):
user_id_filter = [str(k) for k in user_id_filter]
elif user_id_filter:
user_id_filter = [str(user_id_filter)]
sync_list = self.get_plextv_sync_lists(machine_id, output_format='xml')
user_data = users.Users()
synced_items = []
try:
xml_head = sync_list.getElementsByTagName('SyncList')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s." % e)
return {}
for a in xml_head:
client_id = helpers.get_xml_attr(a, 'clientIdentifier')
# Filter by client_id
if client_id_filter and str(client_id_filter) != client_id:
continue
sync_list_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')
# Filter by user_id
if user_id_filter and device_user_id not in user_id_filter:
continue
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')
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 not in rating_key_filter:
continue
sync_id = helpers.get_xml_attr(item, 'id')
# Filter by sync_id
if sync_id_filter and str(sync_id_filter) != sync_id:
continue
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 settings in item.getElementsByTagName('MediaSettings'):
settings_video_bitrate = helpers.get_xml_attr(settings, 'maxVideoBitrate')
settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')
settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
settings_audio_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')
sync_details = {"device_name": device_name,
"platform": device_platform,
"user_id": device_user_id,
"user": device_friendly_name,
"username": device_username,
"root_title": sync_root_title,
"sync_title": 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,
"video_bitrate": settings_video_bitrate,
"audio_bitrate": settings_audio_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')
def delete_sync(self, client_id, sync_id):
logger.info("Tautulli PlexTV :: Deleting sync item '%s'." % sync_id)
self.delete_plextv_sync(client_id=client_id, sync_id=sync_id)
def get_server_connections(self, pms_identifier='', pms_ip='', pms_port=32400, include_https=True):
if not pms_identifier:
logger.error("Tautulli PlexTV :: Unable to retrieve server connections: no pms_identifier provided.")
return {}
plextv_resources = self.get_plextv_resources(include_https=include_https,
output_format='xml')
try:
xml_head = plextv_resources.getElementsByTagName('Device')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_urls: %s." % e)
return {}
# Function to get all connections for a device
def get_connections(device):
conn = []
connections = device.getElementsByTagName('Connection')
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')
}
conn.append(server_details)
server['connections'] = conn
return server
server = {}
# Try to match the device
for a in xml_head:
if helpers.get_xml_attr(a, 'clientIdentifier') == pms_identifier:
server = get_connections(a)
break
# Else no device match found
if not server:
# Try to match the PMS_IP and PMS_PORT
for a in xml_head:
if helpers.get_xml_attr(a, 'provides') == 'server':
connections = a.getElementsByTagName('Connection')
for connection in connections:
if helpers.get_xml_attr(connection, 'address') == pms_ip and \
helpers.get_xml_attr(connection, 'port') == str(pms_port):
server = get_connections(a)
break
if server.get('connections'):
break
return server
def get_server_times(self):
servers = self.get_plextv_server_list(output_format='xml')
server_times = {}
try:
xml_head = servers.getElementsByTagName('Server')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_times: %s." % e)
return {}
for a in xml_head:
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
server_times = {"created_at": helpers.get_xml_attr(a, 'createdAt'),
"updated_at": helpers.get_xml_attr(a, 'updatedAt'),
"version": helpers.get_xml_attr(a, 'version')
}
break
return server_times
def discover(self, include_cloud=True, all_servers=False):
""" Query plex for all servers online. Returns the ones you own in a selectize format """
# Try to discover localhost server
local_machine_identifier = None
request_handler = http_handler.HTTPHandler(urls='http://127.0.0.1:32400', timeout=1,
ssl_verify=False, silent=True)
request = request_handler.make_request(uri='/identity', request_type='GET', output_format='xml')
if request:
xml_head = request.getElementsByTagName('MediaContainer')[0]
local_machine_identifier = xml_head.getAttribute('machineIdentifier')
local_server = {'httpsRequired': '0',
'clientIdentifier': local_machine_identifier,
'label': 'Local',
'ip': '127.0.0.1',
'port': '32400',
'uri': 'http://127.0.0.1:32400',
'local': '1',
'value': '127.0.0.1:32400',
'is_cloud': False
}
servers = self.get_plextv_resources(include_https=True, output_format='xml')
clean_servers = []
try:
xml_head = servers.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn("Tautulli PlexTV :: Failed to get servers from plex: %s." % e)
return []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
return []
if a.getElementsByTagName('Device'):
devices = a.getElementsByTagName('Device')
for d in devices:
if helpers.get_xml_attr(d, 'presence') == '1' and \
helpers.get_xml_attr(d, 'owned') == '1' and \
helpers.get_xml_attr(d, 'provides') == 'server':
is_cloud = (helpers.get_xml_attr(d, 'platform').lower() == 'cloud')
if not include_cloud and is_cloud:
continue
connections = d.getElementsByTagName('Connection')
for c in connections:
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 helpers.get_xml_attr(d, 'clientIdentifier') == local_machine_identifier:
local_server['httpsRequired'] = helpers.get_xml_attr(d, 'httpsRequired')
local_server['label'] = helpers.get_xml_attr(d, 'name')
clean_servers.append(local_server)
local_machine_identifier = None
server = {'httpsRequired': '1' if is_cloud else helpers.get_xml_attr(d, 'httpsRequired'),
'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'),
'label': helpers.get_xml_attr(d, 'name'),
'ip': 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'),
'value': helpers.get_xml_attr(c, 'address') + ':' + helpers.get_xml_attr(c, 'port'),
'is_cloud': is_cloud
}
clean_servers.append(server)
if local_machine_identifier:
clean_servers.append(local_server)
clean_servers.sort(key=lambda s: (s['label'], -int(s['local']), s['ip']))
return clean_servers
def get_plex_downloads(self):
logger.debug("Tautulli PlexTV :: Retrieving current server version.")
pms_connect = pmsconnect.PmsConnect()
pms_connect.set_server_version()
update_channel = pms_connect.get_server_update_channel()
logger.debug("Tautulli PlexTV :: Plex update channel is %s." % update_channel)
plex_downloads = self.get_plextv_downloads(plexpass=(update_channel == 'beta'))
try:
available_downloads = json.loads(plex_downloads)
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to load JSON for get_plex_updates.")
return {}
# Get the updates for the platform
pms_platform = common.PMS_PLATFORM_NAME_OVERRIDES.get(plexpy.CONFIG.PMS_PLATFORM, plexpy.CONFIG.PMS_PLATFORM)
platform_downloads = available_downloads.get('computer').get(pms_platform) or \
available_downloads.get('nas').get(pms_platform)
if not platform_downloads:
logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Could not match server platform: %s."
% pms_platform)
return {}
v_old = helpers.cast_to_int("".join(v.zfill(4) for v in plexpy.CONFIG.PMS_VERSION.split('-')[0].split('.')[:4]))
v_new = helpers.cast_to_int("".join(v.zfill(4) for v in platform_downloads.get('version', '').split('-')[0].split('.')[:4]))
if not v_old:
logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Invalid current server version: %s."
% plexpy.CONFIG.PMS_VERSION)
return {}
if not v_new:
logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Invalid new server version: %s."
% platform_downloads.get('version'))
return {}
# Get proper download
releases = platform_downloads.get('releases', [{}])
release = next((r for r in releases if r['distro'] == plexpy.CONFIG.PMS_UPDATE_DISTRO and
r['build'] == plexpy.CONFIG.PMS_UPDATE_DISTRO_BUILD), releases[0])
download_info = {'update_available': v_new > v_old,
'platform': platform_downloads.get('name'),
'release_date': platform_downloads.get('release_date'),
'version': platform_downloads.get('version'),
'requirements': platform_downloads.get('requirements'),
'extra_info': platform_downloads.get('extra_info'),
'changelog_added': platform_downloads.get('items_added'),
'changelog_fixed': platform_downloads.get('items_fixed'),
'label': release.get('label'),
'distro': release.get('distro'),
'distro_build': release.get('build'),
'download_url': release.get('url'),
}
return download_info
def get_plexpass_status(self):
account_data = self.get_plextv_user_details(output_format='xml')
try:
subscription = account_data.getElementsByTagName('subscription')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_plexpass_status: %s." % e)
return False
if subscription and helpers.get_xml_attr(subscription[0], 'active') == '1':
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', 1)
plexpy.CONFIG.write()
return True
else:
logger.debug("Tautulli PlexTV :: Plex Pass subscription not found.")
plexpy.CONFIG.__setattr__('PMS_PLEXPASS', 0)
plexpy.CONFIG.write()
return False
def get_devices_list(self):
devices = self.get_plextv_devices_list(output_format='xml')
try:
xml_head = devices.getElementsByTagName('Device')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_devices_list: %s." % e)
return []
devices_list = []
for a in xml_head:
device = {"device_name": helpers.get_xml_attr(a, 'name'),
"product": helpers.get_xml_attr(a, 'product'),
"product_version": helpers.get_xml_attr(a, 'productVersion'),
"platform": helpers.get_xml_attr(a, 'platform'),
"platform_version": helpers.get_xml_attr(a, 'platformVersion'),
"device": helpers.get_xml_attr(a, 'device'),
"model": helpers.get_xml_attr(a, 'model'),
"vendor": helpers.get_xml_attr(a, 'vendor'),
"provides": helpers.get_xml_attr(a, 'provides'),
"device_identifier": helpers.get_xml_attr(a, 'clientIdentifier'),
"device_id": helpers.get_xml_attr(a, 'id'),
"token": helpers.get_xml_attr(a, 'token')
}
devices_list.append(device)
return devices_list
def get_cloud_server_status(self):
cloud_status = self.cloud_server_status(output_format='xml')
try:
status_info = cloud_status.getElementsByTagName('info')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_cloud_server_status: %s." % e)
return False
for info in status_info:
servers = info.getElementsByTagName('server')
for s in servers:
if helpers.get_xml_attr(s, 'address') == plexpy.CONFIG.PMS_IP:
if helpers.get_xml_attr(info, 'running') == '1':
return True
else:
return False
def get_plex_account_details(self):
account_data = self.get_plextv_user_details(output_format='xml')
try:
xml_head = account_data.getElementsByTagName('user')
except Exception as e:
logger.warn("Tautulli PlexTV :: Unable to parse XML for get_plex_account_details: %s." % e)
return None
for a in xml_head:
account_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_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'),
"user_token": helpers.get_xml_attr(a, 'authToken')
}
return account_details