plexpy/plexpy/plextv.py

404 lines
No EOL
18 KiB
Python

# This file is part of PlexPy.
#
# 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.
#
# 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 PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, helpers, users, http_handler, database
from xml.dom import minidom
import base64
import plexpy
def refresh_users():
logger.info("Requesting users list refresh...")
result = PlexTV().get_full_users_list()
monitor_db = database.MonitorDatabase()
if len(result) > 0:
for item in result:
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']
}
# 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("Users list refreshed.")
else:
logger.warn("Unable to refresh users list.")
def get_real_pms_url():
logger.info("Requesting URLs for server...")
# Reset any current PMS_URL value
plexpy.CONFIG.__setattr__('PMS_URL', '')
plexpy.CONFIG.write()
fallback_url = 'http://' + plexpy.CONFIG.PMS_IP + ':' + str(plexpy.CONFIG.PMS_PORT)
if plexpy.CONFIG.PMS_SSL:
result = PlexTV().get_server_urls(include_https=True)
process_urls = True
elif plexpy.CONFIG.PMS_IS_REMOTE:
result = PlexTV().get_server_urls(include_https=False)
process_urls = True
else:
process_urls = False
if process_urls:
if len(result) > 0:
for item in result:
if plexpy.CONFIG.PMS_IS_REMOTE and item['local'] == '0':
plexpy.CONFIG.__setattr__('PMS_URL', item['uri'])
plexpy.CONFIG.write()
logger.info("Server URL retrieved.")
if not plexpy.CONFIG.PMS_IS_REMOTE and item['local'] == '1':
plexpy.CONFIG.__setattr__('PMS_URL', item['uri'])
plexpy.CONFIG.write()
logger.info("Server URL retrieved.")
else:
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
logger.warn("Unable to retrieve server URLs. Using user-defined value.")
else:
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
class PlexTV(object):
"""
Plex.tv authentication
"""
def __init__(self, username=None, password=None):
self.protocol = 'HTTPS'
self.username = username
self.password = password
self.ssl_verify = plexpy.CONFIG.VERIFY_SSL_CERT
self.request_handler = http_handler.HTTPHandler(host='plex.tv',
port=443,
token=plexpy.CONFIG.PMS_TOKEN,
ssl_verify=self.ssl_verify)
def get_plex_auth(self, output_format='raw'):
uri = '/users/sign_in.xml'
base64string = base64.encodestring('%s:%s' % (self.username, self.password)).replace('\n', '')
headers = {'Content-Type': 'application/xml; charset=utf-8',
'Content-Length': '0',
'X-Plex-Device-Name': 'PlexPy',
'X-Plex-Product': 'PlexPy',
'X-Plex-Version': 'v0.1 dev',
'X-Plex-Client-Identifier': plexpy.CONFIG.PMS_UUID,
'Authorization': 'Basic %s' % base64string + ":"
}
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='POST',
headers=headers,
output_format=output_format)
return request
def get_token(self):
plextv_response = self.get_plex_auth(output_format='xml')
if plextv_response:
xml_head = plextv_response.getElementsByTagName('user')
if not xml_head:
logger.warn("Error parsing XML for Plex.tv token: %s" % e)
return []
auth_token = xml_head[0].getAttribute('authenticationToken')
return auth_token
else:
return []
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,
proto=self.protocol,
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,
proto=self.protocol,
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,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
def get_plextv_sync_lists(self, machine_id='', output_format=''):
uri = '/servers/' + machine_id + '/sync_lists'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
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,
proto=self.protocol,
request_type='GET',
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()
users_list = []
try:
xml_parse = minidom.parseString(own_account)
except Exception, e:
logger.warn("Error parsing XML for Plex account details: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex account details.")
return []
xml_head = xml_parse.getElementsByTagName('user')
if not xml_head:
logger.warn("Error parsing XML for Plex account details.")
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')
}
users_list.append(own_details)
try:
xml_parse = minidom.parseString(friends_list)
except Exception, e:
logger.warn("Error parsing XML for Plex friends list: %s" % e)
except:
logger.warn("Error parsing XML for Plex friends list.")
xml_head = xml_parse.getElementsByTagName('User')
if not xml_head:
logger.warn("Error parsing XML for Plex friends 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')
}
users_list.append(friend)
return users_list
def get_synced_items(self, machine_id=None, user_id=None):
sync_list = self.get_plextv_sync_lists(machine_id)
user_data = users.Users()
synced_items = []
try:
xml_parse = minidom.parseString(sync_list)
except Exception, e:
logger.warn("Error parsing XML for Plex sync lists: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex sync lists.")
return []
xml_head = xml_parse.getElementsByTagName('SyncList')
if not xml_head:
logger.warn("Error parsing XML for Plex sync lists.")
else:
for a in xml_head:
client_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_user_details(user_id=device_user_id)['username']
device_friendly_name = user_data.get_user_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 and user_id != device_user_id:
continue
for synced in a.getElementsByTagName('SyncItems'):
sync_item = synced.getElementsByTagName('SyncItem')
for item in sync_item:
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 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')
if helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri').endswith('%2Fchildren'):
clean_uri = helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri')[:-11]
else:
clean_uri = helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri')
rating_key = clean_uri.rpartition('%2F')[-1]
sync_details = {"device_name": device_name,
"platform": device_platform,
"username": device_username,
"friendly_name": device_friendly_name,
"user_id": device_user_id,
"root_title": sync_root_title,
"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,
"music_bitrate": settings_music_bitrate,
"photo_quality": settings_photo_quality,
"video_quality": settings_video_quality,
"total_size": status_total_size,
"failure": status_failure,
"sync_id": sync_id
}
synced_items.append(sync_details)
return synced_items
def get_server_urls(self, include_https=True):
if plexpy.CONFIG.PMS_IDENTIFIER:
server_id = plexpy.CONFIG.PMS_IDENTIFIER
else:
logger.error('PlexPy PlexTV connector :: Unable to retrieve server identity.')
return []
plextv_resources = self.get_plextv_resources(include_https=include_https)
server_urls = []
try:
xml_parse = minidom.parseString(plextv_resources)
except Exception, e:
logger.warn("Error parsing XML for Plex resources: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex resources.")
return []
try:
xml_head = xml_parse.getElementsByTagName('Device')
except:
logger.warn("Error parsing XML for Plex resources.")
return []
for a in xml_head:
if helpers.get_xml_attr(a, 'clientIdentifier') == server_id:
connections = a.getElementsByTagName('Connection')
for connection in connections:
server_details = {"protocol": helpers.get_xml_attr(connection, 'protocol'),
"address": helpers.get_xml_attr(connection, 'address'),
"port": helpers.get_xml_attr(connection, 'port'),
"uri": helpers.get_xml_attr(connection, 'uri'),
"local": helpers.get_xml_attr(connection, 'local')
}
server_urls.append(server_details)
return server_urls