removed plextv / pms, added TODO's for reimplementing Jellyfin

This commit is contained in:
2021-02-05 21:01:43 +01:00
parent af3c826f7d
commit 38f0a44fa0
16 changed files with 733 additions and 4835 deletions

View File

@@ -50,10 +50,7 @@
<h3 style="line-height: 50px;">Welcome!</h3>
<div class="wizard-input-section">
<p class="welcome-message">
Thanks for taking the time to try out Tautulli. Hope you find it useful.
</p>
<p class="welcome-message">
Tautulli requires a permanent internet connection to ensure a reliable experience.
Thanks for taking the time to try out JellyPy. Hope you find it useful.
</p>
<p class="welcome-message">
This wizard will help you get set up, to continue press Next.
@@ -65,11 +62,11 @@
<h3>Authentication</h3>
<div class="wizard-input-section">
<p class="help-block">
Please setup an admin username and password for Tautulli.
Please setup an admin username and password for JellyPy.
</p>
</div>
<div class="wizard-input-section">
<label for="http_username">HTTP Username</label>
<label for="http_username">Username</label>
<div class="row">
<div class="col-xs-8">
<input type="text" class="form-control auth-settings" id="http_username" name="http_username" value="" size="30">
@@ -77,7 +74,7 @@
</div>
</div>
<div class="wizard-input-section">
<label for="http_password">HTTP Password</label>
<label for="http_password">Password</label>
<div class="row">
<div class="col-xs-8">
<input type="password" class="form-control auth-settings" id="http_password" name="http_password" value="" size="30" autocomplete="new-password">
@@ -91,26 +88,14 @@
</div>
<div class="wizard-card" data-cardname="card3">
<h3>Plex Account</h3>
<h3>Jellyfin</h3>
<div class="wizard-input-section">
<p class="help-block">
Tautulli requires a Plex.tv account. Click the button below to sign in on Plex.tv. You may need to allow popups in your browser.
</p>
</div>
<input type="hidden" class="form-control" name="pms_token" id="pms_token" value="" data-validate="validatePMStoken">
<a class="btn btn-dark" id="sign-in-plex" href="#" role="button">Sign In with Plex</a>
<span style="margin-left: 10px; display: none;" id="pms-token-status"></span>
</div>
<div class="wizard-card" data-cardname="card4">
<h3>Plex Media Server</h3>
<div class="wizard-input-section">
<p class="help-block">
Select your Plex Media Server from the dropdown menu or enter an IP address or hostname.
Select your Jellyfin Server from the dropdown menu or enter an IP address or hostname.
</p>
</div>
<div class="wizard-input-section">
<label for="pms_ip_selectize">Plex IP Address or Hostname</label>
<label for="pms_ip_selectize">Jellyfin IP Address or Hostname</label>
<div class="row">
<div class="col-xs-12">
<select class="form-control pms-settings selectize-pms-ip" id="pms_ip_selectize">
@@ -130,7 +115,7 @@
</div>
</div>
<div class="wizard-input-section">
<label for="pms_port">Plex Port</label>
<label for="pms_port">Jellyfin Port</label>
<div class="row">
<div class="col-xs-3">
<input type="text" class="form-control pms-settings" name="pms_port" id="pms_port" placeholder="32400" value="${config['pms_port']}" required>
@@ -161,7 +146,7 @@
<span style="margin-left: 10px; display: none;" id="pms-verify-status"></span>
</div>
<div class="wizard-card" data-cardname="card5">
<div class="wizard-card" data-cardname="card4">
<h3>Activity Logging</h3>
<div class="wizard-input-section">
<p class="help-block">
@@ -186,7 +171,7 @@
</div>
</div>
<div class="wizard-card" data-cardname="card6">
<div class="wizard-card" data-cardname="card5">
<h3>Notifications</h3>
<div class="wizard-input-section">
<p class="help-block">
@@ -199,7 +184,7 @@
</div>
</div>
<div class="wizard-card" data-cardname="card7">
<div class="wizard-card" data-cardname="card6">
<h3>Database Import</h3>
<div class="wizard-input-section">
<p class="help-block">

View File

@@ -47,7 +47,6 @@ from jellypy import newsletters
from jellypy import newsletter_handler
from jellypy import notification_handler
from jellypy import notifiers
from jellypy import plextv
from jellypy import users
from jellypy import versioncheck
from jellypy import web_socket
@@ -426,8 +425,9 @@ 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(plextv.get_server_resources, 'Refresh Plex server URLs',
hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0)
# TODO: Jellyfin
# schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
# hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0)
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
hours=pms_update_check_hours * bool(CONFIG.MONITOR_PMS_UPDATES), minutes=0, seconds=0)
@@ -448,8 +448,9 @@ def initialize_scheduler():
else:
# Cancel all jobs
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
hours=0, minutes=0, seconds=0)
# TODO: Jellyfin
# schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
# hours=0, minutes=0, seconds=0)
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
hours=0, minutes=0, seconds=0)
@@ -532,7 +533,9 @@ def start():
def startup_refresh():
# Get the real PMS urls for SSL and remote access
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
plextv.get_server_resources()
pass
# TODO: Jellyfin
# plextv.get_server_resources()
# Connect server after server resource is refreshed
if CONFIG.FIRST_RUN_COMPLETE:

View File

@@ -27,7 +27,6 @@ from jellypy import datafactory
from jellypy import helpers
from jellypy import logger
from jellypy import notification_handler
from jellypy import pmsconnect
ACTIVITY_SCHED = None
@@ -60,27 +59,29 @@ class ActivityHandler(object):
def get_metadata(self, skip_cache=False):
cache_key = None if skip_cache else self.get_session_key()
pms_connect = pmsconnect.PmsConnect()
metadata = pms_connect.get_metadata_details(rating_key=self.get_rating_key(), cache_key=cache_key)
if metadata:
return metadata
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# metadata = pms_connect.get_metadata_details(rating_key=self.get_rating_key(), cache_key=cache_key)
#
# if metadata:
# return metadata
return None
def get_live_session(self, skip_cache=False):
pms_connect = pmsconnect.PmsConnect()
session_list = pms_connect.get_current_activity(skip_cache=skip_cache)
if session_list:
for session in session_list['sessions']:
if int(session['session_key']) == self.get_session_key():
# Live sessions don't have rating keys in sessions
# Get it from the websocket data
if not session['rating_key']:
session['rating_key'] = self.get_rating_key()
session['rating_key_websocket'] = self.get_rating_key()
return session
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# session_list = pms_connect.get_current_activity(skip_cache=skip_cache)
#
# if session_list:
# for session in session_list['sessions']:
# if int(session['session_key']) == self.get_session_key():
# # Live sessions don't have rating keys in sessions
# # Get it from the websocket data
# if not session['rating_key']:
# session['rating_key'] = self.get_rating_key()
# session['rating_key_websocket'] = self.get_rating_key()
# return session
return None
@@ -390,11 +391,12 @@ class TimelineHandler(object):
return None
def get_metadata(self):
pms_connect = pmsconnect.PmsConnect()
metadata = pms_connect.get_metadata_details(self.get_rating_key())
if metadata:
return metadata
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# metadata = pms_connect.get_metadata_details(self.get_rating_key())
#
# if metadata:
# return metadata
return None
@@ -522,9 +524,11 @@ class ReachabilityHandler(object):
return False
def remote_access_enabled(self):
pms_connect = pmsconnect.PmsConnect()
pref = pms_connect.get_server_pref(pref='PublishServerOnPlexOnlineKey')
return helpers.bool_true(pref)
return False
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# pref = pms_connect.get_server_pref(pref='PublishServerOnPlexOnlineKey')
# return helpers.bool_true(pref)
def on_down(self, server_response):
jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_extdown', 'remote_access_info': server_response})
@@ -541,39 +545,40 @@ class ReachabilityHandler(object):
if self.is_reachable() and jellypy.PLEX_REMOTE_ACCESS_UP:
return
pms_connect = pmsconnect.PmsConnect()
server_response = pms_connect.get_server_response()
if server_response:
# Waiting for port mapping
if server_response['mapping_state'] == 'waiting':
logger.warn("Tautulli ReachabilityHandler :: Remote access waiting for port mapping.")
elif jellypy.PLEX_REMOTE_ACCESS_UP is not False and server_response['reason']:
logger.warn("Tautulli ReachabilityHandler :: Remote access failed: %s" % server_response['reason'])
logger.info("Tautulli ReachabilityHandler :: Plex remote access is down.")
jellypy.PLEX_REMOTE_ACCESS_UP = False
if not ACTIVITY_SCHED.get_job('on_extdown'):
logger.debug("Tautulli ReachabilityHandler :: Schedule remote access down callback in %d seconds.",
jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD)
schedule_callback('on_extdown', func=self.on_down, args=[server_response],
seconds=jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD)
elif jellypy.PLEX_REMOTE_ACCESS_UP is False and not server_response['reason']:
logger.info("Tautulli ReachabilityHandler :: Plex remote access is back up.")
jellypy.PLEX_REMOTE_ACCESS_UP = True
if ACTIVITY_SCHED.get_job('on_extdown'):
logger.debug("Tautulli ReachabilityHandler :: Cancelling scheduled remote access down callback.")
schedule_callback('on_extdown', remove_job=True)
else:
self.on_up(server_response)
elif jellypy.PLEX_REMOTE_ACCESS_UP is None:
jellypy.PLEX_REMOTE_ACCESS_UP = self.is_reachable()
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# server_response = pms_connect.get_server_response()
#
# if server_response:
# # Waiting for port mapping
# if server_response['mapping_state'] == 'waiting':
# logger.warn("Tautulli ReachabilityHandler :: Remote access waiting for port mapping.")
#
# elif jellypy.PLEX_REMOTE_ACCESS_UP is not False and server_response['reason']:
# logger.warn("Tautulli ReachabilityHandler :: Remote access failed: %s" % server_response['reason'])
# logger.info("Tautulli ReachabilityHandler :: Plex remote access is down.")
#
# jellypy.PLEX_REMOTE_ACCESS_UP = False
#
# if not ACTIVITY_SCHED.get_job('on_extdown'):
# logger.debug("Tautulli ReachabilityHandler :: Schedule remote access down callback in %d seconds.",
# jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD)
# schedule_callback('on_extdown', func=self.on_down, args=[server_response],
# seconds=jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD)
#
# elif jellypy.PLEX_REMOTE_ACCESS_UP is False and not server_response['reason']:
# logger.info("Tautulli ReachabilityHandler :: Plex remote access is back up.")
#
# jellypy.PLEX_REMOTE_ACCESS_UP = True
#
# if ACTIVITY_SCHED.get_job('on_extdown'):
# logger.debug("Tautulli ReachabilityHandler :: Cancelling scheduled remote access down callback.")
# schedule_callback('on_extdown', remove_job=True)
# else:
# self.on_up(server_response)
#
# elif jellypy.PLEX_REMOTE_ACCESS_UP is None:
# jellypy.PLEX_REMOTE_ACCESS_UP = self.is_reachable()
def del_keys(key):
@@ -670,41 +675,42 @@ def clear_recently_added_queue(rating_key, title):
def on_created(rating_key, **kwargs):
logger.debug("Tautulli TimelineHandler :: Library item %s added to Plex." % str(rating_key))
pms_connect = pmsconnect.PmsConnect()
metadata = pms_connect.get_metadata_details(rating_key)
if metadata:
notify = True
# now = helpers.timestamp()
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# metadata = pms_connect.get_metadata_details(rating_key)
#
# if helpers.cast_to_int(metadata['added_at']) < now - 86400: # Updated more than 24 hours ago
# logger.debug("Tautulli TimelineHandler :: Library item %s added more than 24 hours ago. Not notifying."
# if metadata:
# notify = True
# # now = helpers.timestamp()
# #
# # if helpers.cast_to_int(metadata['added_at']) < now - 86400: # Updated more than 24 hours ago
# # logger.debug("Tautulli TimelineHandler :: Library item %s added more than 24 hours ago. Not notifying."
# # % str(rating_key))
# # notify = False
#
# data_factory = datafactory.DataFactory()
# if 'child_keys' not in kwargs:
# if data_factory.get_recently_added_item(rating_key):
# logger.debug("Tautulli TimelineHandler :: Library item %s added already. Not notifying again."
# % str(rating_key))
# notify = False
data_factory = datafactory.DataFactory()
if 'child_keys' not in kwargs:
if data_factory.get_recently_added_item(rating_key):
logger.debug("Tautulli TimelineHandler :: Library item %s added already. Not notifying again."
% str(rating_key))
notify = False
if notify:
data = {'timeline_data': metadata, 'notify_action': 'on_created'}
data.update(kwargs)
jellypy.NOTIFY_QUEUE.put(data)
all_keys = [rating_key]
if 'child_keys' in kwargs:
all_keys.extend(kwargs['child_keys'])
for key in all_keys:
data_factory.set_recently_added_item(key)
logger.debug("Added %s items to the recently_added database table." % str(len(all_keys)))
else:
logger.error("Tautulli TimelineHandler :: Unable to retrieve metadata for rating_key %s" % str(rating_key))
#
# if notify:
# data = {'timeline_data': metadata, 'notify_action': 'on_created'}
# data.update(kwargs)
# jellypy.NOTIFY_QUEUE.put(data)
#
# all_keys = [rating_key]
# if 'child_keys' in kwargs:
# all_keys.extend(kwargs['child_keys'])
#
# for key in all_keys:
# data_factory.set_recently_added_item(key)
#
# logger.debug("Added %s items to the recently_added database table." % str(len(all_keys)))
#
# else:
# logger.error("Tautulli TimelineHandler :: Unable to retrieve metadata for rating_key %s" % str(rating_key))
def delete_metadata_cache(session_key):

View File

@@ -23,8 +23,6 @@ from jellypy import database
from jellypy import helpers
from jellypy import logger
from jellypy import notification_handler
from jellypy import plextv
from jellypy import pmsconnect
from jellypy import web_socket
monitor_lock = threading.Lock()
@@ -43,8 +41,10 @@ def check_active_sessions(ws_request=False):
for stream in db_streams:
activity_handler.delete_metadata_cache(stream['session_key'])
pms_connect = pmsconnect.PmsConnect()
session_list = pms_connect.get_current_activity()
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# session_list = pms_connect.get_current_activity()
session_list = None
logger.debug("Tautulli Monitor :: Checking for active streams.")
@@ -229,8 +229,10 @@ def connect_server(log=True, startup=False):
if log:
logger.info("Tautulli Monitor :: Checking for Plex Cloud server status...")
plex_tv = plextv.PlexTV()
status = plex_tv.get_cloud_server_status()
# TODO: Jellyfin
# plex_tv = plextv.PlexTV()
# status = plex_tv.get_cloud_server_status()
status = None
if status is True:
logger.info("Tautulli Monitor :: Plex Cloud server is active.")
@@ -261,16 +263,17 @@ def check_server_updates():
with monitor_lock:
logger.info("Tautulli Monitor :: Checking for PMS updates...")
plex_tv = plextv.PlexTV()
download_info = plex_tv.get_plex_downloads()
if download_info:
logger.info("Tautulli Monitor :: Current PMS version: %s", jellypy.CONFIG.PMS_VERSION)
if download_info['update_available']:
logger.info("Tautulli Monitor :: PMS update available version: %s", download_info['version'])
jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_pmsupdate', 'pms_download_info': download_info})
else:
logger.info("Tautulli Monitor :: No PMS update available.")
# TODO: Jellyfin
# plex_tv = plextv.PlexTV()
# download_info = plex_tv.get_plex_downloads()
#
# if download_info:
# logger.info("Tautulli Monitor :: Current PMS version: %s", jellypy.CONFIG.PMS_VERSION)
#
# if download_info['update_available']:
# logger.info("Tautulli Monitor :: PMS update available version: %s", download_info['version'])
#
# jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_pmsupdate', 'pms_download_info': download_info})
#
# else:
# logger.info("Tautulli Monitor :: No PMS update available.")

View File

@@ -22,7 +22,6 @@ from jellypy import database
from jellypy import helpers
from jellypy import libraries
from jellypy import logger
from jellypy import pmsconnect
from jellypy import users
@@ -260,19 +259,20 @@ class ActivityProcessor(object):
if not is_import:
logger.debug(
"Tautulli ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
pms_connect = pmsconnect.PmsConnect()
if session['live']:
metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']),
cache_key=session['session_key'],
return_cache=True)
else:
metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
if not metadata:
return False
else:
media_info = {}
if 'media_info' in metadata and len(metadata['media_info']) > 0:
media_info = metadata['media_info'][0]
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# if session['live']:
# metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']),
# cache_key=session['session_key'],
# return_cache=True)
# else:
# metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
# if not metadata:
# return False
# else:
# media_info = {}
# if 'media_info' in metadata and len(metadata['media_info']) > 0:
# media_info = metadata['media_info'][0]
else:
metadata = import_metadata
## TODO: Fix media info from imports. Temporary media info from import session.

View File

@@ -40,7 +40,6 @@ from jellypy import notification_handler
from jellypy import notifiers
from jellypy import newsletter_handler
from jellypy import newsletters
from jellypy import plextv
from jellypy import users
from jellypy.password import check_hash
@@ -451,11 +450,13 @@ class API2(object):
mobile_app.set_temp_device_token(True)
plex_server = plextv.get_server_resources(return_info=True)
# TODO: Jellyfin
# plex_server = plextv.get_server_resources(return_info=True)
tautulli = jellypy.get_tautulli_info()
data = {"server_id": jellypy.CONFIG.PMS_UUID}
data.update(plex_server)
# TODO: Jellyfin
# data.update(plex_server)
data.update(tautulli)
return data

View File

@@ -25,7 +25,6 @@ from jellypy import database
from jellypy import datatables
from jellypy import helpers
from jellypy import logger
from jellypy import pmsconnect
from jellypy import session
@@ -1589,7 +1588,7 @@ class DataFactory(object):
return key_list
def update_metadata(self, old_key_list='', new_key_list='', media_type=''):
pms_connect = pmsconnect.PmsConnect()
# TODO: pms_connect = pmsconnect.PmsConnect()
monitor_db = database.MonitorDatabase()
# function to map rating keys pairs
@@ -1608,37 +1607,38 @@ class DataFactory(object):
if old_key_list and new_key_list:
mapping = get_pairs(old_key_list, new_key_list)
if mapping:
logger.info("Tautulli DataFactory :: Updating metadata in the database.")
for old_key, new_key in mapping.items():
metadata = pms_connect.get_metadata_details(new_key)
if metadata:
if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
# check grandparent_rating_key (2 tables)
monitor_db.action(
'UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
monitor_db.action(
'UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album':
# check parent_rating_key (2 tables)
monitor_db.action(
'UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
monitor_db.action(
'UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
else:
# check rating_key (2 tables)
monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
# update session_history_metadata table
self.update_metadata_details(old_key, new_key, metadata)
# TODO: Jellyfin
# if mapping:
# logger.info("Tautulli DataFactory :: Updating metadata in the database.")
# for old_key, new_key in mapping.items():
# metadata = pms_connect.get_metadata_details(new_key)
#
# if metadata:
# if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
# # check grandparent_rating_key (2 tables)
# monitor_db.action(
# 'UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
# [new_key, old_key])
# monitor_db.action(
# 'UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
# [new_key, old_key])
# elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album':
# # check parent_rating_key (2 tables)
# monitor_db.action(
# 'UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?',
# [new_key, old_key])
# monitor_db.action(
# 'UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?',
# [new_key, old_key])
# else:
# # check rating_key (2 tables)
# monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?',
# [new_key, old_key])
# monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?',
# [new_key, old_key])
#
# # update session_history_metadata table
# self.update_metadata_details(old_key, new_key, metadata)
return 'Updated metadata in database.'
else:
@@ -1886,23 +1886,24 @@ class DataFactory(object):
def set_recently_added_item(self, rating_key=''):
monitor_db = database.MonitorDatabase()
pms_connect = pmsconnect.PmsConnect()
metadata = pms_connect.get_metadata_details(rating_key)
keys = {'rating_key': metadata['rating_key']}
values = {'added_at': metadata['added_at'],
'section_id': metadata['section_id'],
'parent_rating_key': metadata['parent_rating_key'],
'grandparent_rating_key': metadata['grandparent_rating_key'],
'media_type': metadata['media_type'],
'media_info': json.dumps(metadata['media_info'])
}
try:
monitor_db.upsert(table_name='recently_added', key_dict=keys, value_dict=values)
except Exception as e:
logger.warn("Tautulli DataFactory :: Unable to execute database query for set_recently_added_item: %s." % e)
return False
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# metadata = pms_connect.get_metadata_details(rating_key)
#
# keys = {'rating_key': metadata['rating_key']}
#
# values = {'added_at': metadata['added_at'],
# 'section_id': metadata['section_id'],
# 'parent_rating_key': metadata['parent_rating_key'],
# 'grandparent_rating_key': metadata['grandparent_rating_key'],
# 'media_type': metadata['media_type'],
# 'media_info': json.dumps(metadata['media_info'])
# }
#
# try:
# monitor_db.upsert(table_name='recently_added', key_dict=keys, value_dict=values)
# except Exception as e:
# logger.warn("Tautulli DataFactory :: Unable to execute database query for set_recently_added_item: %s." % e)
# return False
return True

View File

@@ -25,8 +25,6 @@ from jellypy import database
from jellypy import datatables
from jellypy import helpers
from jellypy import logger
from jellypy import plextv
from jellypy import pmsconnect
from jellypy import session
from jellypy import users
from jellypy.jellyfin import Jellyfin
@@ -1159,7 +1157,7 @@ class Libraries(object):
monitor_db = database.MonitorDatabase()
# Refresh the PMS_URL to make sure the server_id is updated
plextv.get_server_resources()
# TODO: plextv.get_server_resources()
server_id = jellypy.CONFIG.PMS_IDENTIFIER

View File

@@ -32,7 +32,6 @@ from jellypy import helpers
from jellypy import libraries
from jellypy import logger
from jellypy import newsletter_handler
from jellypy import pmsconnect
from jellypy.notifiers import send_notification, EMAIL
AGENT_IDS = {
@@ -693,7 +692,8 @@ class RecentlyAdded(Newsletter):
def _get_recently_added(self, media_type=None):
from jellypy.notification_handler import format_group_index
pms_connect = pmsconnect.PmsConnect()
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
recently_added = []
done = False

View File

@@ -39,7 +39,6 @@ from jellypy import datafactory
from jellypy import logger
from jellypy import helpers
from jellypy import notifiers
from jellypy import pmsconnect
from jellypy import request
from jellypy.newsletter_handler import notify as notify_newsletter
@@ -164,18 +163,20 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
# return False
if notify_action == 'on_concurrent':
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_current_activity()
user_sessions = []
if result:
user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']]
if jellypy.CONFIG.NOTIFY_CONCURRENT_BY_IP:
evaluated = len(
Counter(s['ip_address'] for s in user_sessions)) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
else:
evaluated = len(user_sessions) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
pass
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# result = pms_connect.get_current_activity()
#
# user_sessions = []
# if result:
# user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']]
#
# if jellypy.CONFIG.NOTIFY_CONCURRENT_BY_IP:
# evaluated = len(
# Counter(s['ip_address'] for s in user_sessions)) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
# else:
# evaluated = len(user_sessions) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
elif notify_action == 'on_newdevice':
data_factory = datafactory.DataFactory()
@@ -536,10 +537,11 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
notify_params.update(media_part_info)
child_metadata = grandchild_metadata = []
for key in kwargs.pop('child_keys', []):
child_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key))
for key in kwargs.pop('grandchild_keys', []):
grandchild_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key))
# TODO: Jellyfin
# for key in kwargs.pop('child_keys', []):
# child_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key))
# for key in kwargs.pop('grandchild_keys', []):
# grandchild_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key))
# Session values
session = session or {}
@@ -1097,7 +1099,8 @@ def build_server_notify_params(notify_action=None, **kwargs):
date_format = jellypy.CONFIG.DATE_FORMAT.replace('Do', '')
time_format = jellypy.CONFIG.TIME_FORMAT.replace('Do', '')
update_channel = pmsconnect.PmsConnect().get_server_update_channel()
# TODO: Jellyfin
# update_channel = pmsconnect.PmsConnect().get_server_update_channel()
pms_download_info = defaultdict(str, kwargs.pop('pms_download_info', {}))
plexpy_download_info = defaultdict(str, kwargs.pop('plexpy_download_info', {}))
@@ -1146,7 +1149,8 @@ def build_server_notify_params(notify_action=None, **kwargs):
'update_url': pms_download_info['download_url'],
'update_release_date': arrow.get(pms_download_info['release_date']).format(date_format)
if pms_download_info['release_date'] else '',
'update_channel': 'Beta' if update_channel == 'beta' else 'Public',
# TODO: Jellyfin
# 'update_channel': 'Beta' if update_channel == 'beta' else 'Public',
'update_platform': pms_download_info['platform'],
'update_distro': pms_download_info['distro'],
'update_distro_build': pms_download_info['build'],
@@ -1410,32 +1414,34 @@ def get_img_info(img=None, rating_key=None, title='', width=1000, height=1500,
img_info = database_img_info[0]
elif not database_img_info and img:
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_image(refresh=True, **image_info)
if result and result[0]:
img_url = delete_hash = ''
if service == 'imgur':
img_url, delete_hash = helpers.upload_to_imgur(img_data=result[0],
img_title=title,
rating_key=rating_key,
fallback=fallback)
elif service == 'cloudinary':
img_url = helpers.upload_to_cloudinary(img_data=result[0],
img_title=title,
rating_key=rating_key,
fallback=fallback)
if img_url:
img_hash = set_hash_image_info(**image_info)
data_factory.set_img_info(img_hash=img_hash,
img_title=title,
img_url=img_url,
delete_hash=delete_hash,
service=service)
img_info = {'img_title': title, 'img_url': img_url}
pass
# TODO: Jellyfin
# pms_connect = pmsconnect.PmsConnect()
# result = pms_connect.get_image(refresh=True, **image_info)
#
# if result and result[0]:
# img_url = delete_hash = ''
#
# if service == 'imgur':
# img_url, delete_hash = helpers.upload_to_imgur(img_data=result[0],
# img_title=title,
# rating_key=rating_key,
# fallback=fallback)
# elif service == 'cloudinary':
# img_url = helpers.upload_to_cloudinary(img_data=result[0],
# img_title=title,
# rating_key=rating_key,
# fallback=fallback)
#
# if img_url:
# img_hash = set_hash_image_info(**image_info)
# data_factory.set_img_info(img_hash=img_hash,
# img_title=title,
# img_url=img_url,
# delete_hash=delete_hash,
# service=service)
#
# img_info = {'img_title': title, 'img_url': img_url}
if img_info['img_url'] and service == 'cloudinary':
# Transform image using Cloudinary

View File

@@ -61,7 +61,6 @@ from jellypy import database
from jellypy import helpers
from jellypy import logger
from jellypy import mobile_app
from jellypy import pmsconnect
from jellypy import request
from jellypy import users
@@ -1517,29 +1516,30 @@ class GROUPME(Notifier):
if self.config['incl_poster'] and kwargs.get('parameters'):
pretty_metadata = PrettyMetadata(kwargs.get('parameters'))
# Retrieve the poster from Plex
result = pmsconnect.PmsConnect().get_image(img=pretty_metadata.parameters.get('poster_thumb', ''))
if result and result[0]:
poster_content = result[0]
else:
poster_content = ''
logger.error("Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME))
if poster_content:
headers = {'X-Access-Token': self.config['access_token'],
'Content-Type': 'image/png'}
r = requests.post('https://image.groupme.com/pictures', headers=headers, data=poster_content)
if r.status_code == 200:
logger.info("Tautulli Notifiers :: {name} poster sent.".format(name=self.NAME))
r_content = r.json()
data['attachments'] = [{'type': 'image',
'url': r_content['payload']['picture_url']}]
else:
logger.error("Tautulli Notifiers :: {name} poster failed: "
"[{r.status_code}] {r.reason}".format(name=self.NAME, r=r))
logger.debug("Tautulli Notifiers :: Request response: {}".format(request.server_message(r, True)))
# TODO: Jellyfin
# # Retrieve the poster from Plex
# result = pmsconnect.PmsConnect().get_image(img=pretty_metadata.parameters.get('poster_thumb', ''))
# if result and result[0]:
# poster_content = result[0]
# else:
# poster_content = ''
# logger.error("Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME))
#
# if poster_content:
# headers = {'X-Access-Token': self.config['access_token'],
# 'Content-Type': 'image/png'}
#
# r = requests.post('https://image.groupme.com/pictures', headers=headers, data=poster_content)
#
# if r.status_code == 200:
# logger.info("Tautulli Notifiers :: {name} poster sent.".format(name=self.NAME))
# r_content = r.json()
# data['attachments'] = [{'type': 'image',
# 'url': r_content['payload']['picture_url']}]
# else:
# logger.error("Tautulli Notifiers :: {name} poster failed: "
# "[{r.status_code}] {r.reason}".format(name=self.NAME, r=r))
# logger.debug("Tautulli Notifiers :: Request response: {}".format(request.server_message(r, True)))
return self.make_request('https://api.groupme.com/v3/bots/post', json=data)

View File

@@ -1,978 +0,0 @@
# -*- 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/>.
import json
import jellypy
from jellypy import common
from jellypy import helpers
from jellypy import http_handler
from jellypy import logger
from jellypy import users
from jellypy import pmsconnect
from jellypy import session
def get_server_resources(return_presence=False, return_server=False, return_info=False, **kwargs):
if not return_presence and not return_info:
logger.info("Tautulli PlexTV :: Requesting resources for server...")
server = {'pms_name': jellypy.CONFIG.PMS_NAME,
'pms_version': jellypy.CONFIG.PMS_VERSION,
'pms_platform': jellypy.CONFIG.PMS_PLATFORM,
'pms_ip': jellypy.CONFIG.PMS_IP,
'pms_port': jellypy.CONFIG.PMS_PORT,
'pms_ssl': jellypy.CONFIG.PMS_SSL,
'pms_is_remote': jellypy.CONFIG.PMS_IS_REMOTE,
'pms_is_cloud': jellypy.CONFIG.PMS_IS_CLOUD,
'pms_url': jellypy.CONFIG.PMS_URL,
'pms_url_manual': jellypy.CONFIG.PMS_URL_MANUAL,
'pms_identifier': jellypy.CONFIG.PMS_IDENTIFIER,
'pms_plexpass': jellypy.CONFIG.PMS_PLEXPASS
}
if return_info:
return server
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
logger.info("Tautulli PlexTV :: Selected server: %s (%s) (%s - Version %s)",
server['pms_name'], server['pms_url'], server['pms_platform'], server['pms_version'])
jellypy.CONFIG.process_kwargs(server)
jellypy.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 = jellypy.CONFIG.PMS_TIMEOUT
self.ssl_verify = jellypy.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 = jellypy.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 = '/api/v2/users/signin'
headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'application/xml'}
data = {'login': self.username,
'password': self.password,
'rememberMe': True}
request = self.request_handler.make_request(uri=uri,
request_type='POST',
headers=headers,
data=data,
output_format=output_format,
no_token=True,
encode_multipart=False)
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('authToken'),
'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'] == jellypy.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']
jellypy.CONFIG.__setattr__('PMS_TOKEN', token)
jellypy.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') == jellypy.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_plextv_geoip(self, ip_address='', output_format=''):
uri = '/api/v2/geoip?ip_address=%s' % ip_address
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=jellypy.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_active": 1,
"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_active": 1,
"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 = jellypy.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') == jellypy.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(jellypy.CONFIG.PMS_PLATFORM, jellypy.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 jellypy.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."
% jellypy.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'] == jellypy.CONFIG.PMS_UPDATE_DISTRO and
r['build'] == jellypy.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':
jellypy.CONFIG.__setattr__('PMS_PLEXPASS', 1)
jellypy.CONFIG.write()
return True
else:
logger.debug("Tautulli PlexTV :: Plex Pass subscription not found.")
jellypy.CONFIG.__setattr__('PMS_PLEXPASS', 0)
jellypy.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') == jellypy.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
def get_geoip_lookup(self, ip_address=''):
if not ip_address or not helpers.is_valid_ip(ip_address):
return
geoip_data = self.get_plextv_geoip(ip_address=ip_address, output_format='xml')
try:
xml_head = geoip_data.getElementsByTagName('location')
except Exception as e:
logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_geoip_lookup: %s." % e)
return None
for a in xml_head:
coordinates = helpers.get_xml_attr(a, 'coordinates').split(',')
latitude = longitude = None
if len(coordinates) == 2:
latitude, longitude = [helpers.cast_to_float(c) for c in coordinates]
geo_info = {"code": helpers.get_xml_attr(a, 'code') or None,
"country": helpers.get_xml_attr(a, 'country') or None,
"region": helpers.get_xml_attr(a, 'subdivisions') or None,
"city": helpers.get_xml_attr(a, 'city') or None,
"postal_code": helpers.get_xml_attr(a, 'postal_code') or None,
"timezone": helpers.get_xml_attr(a, 'time_zone') or None,
"latitude": latitude,
"longitude": longitude,
"continent": None, # keep for backwards compatibility with GeoLite2
"accuracy": None # keep for backwards compatibility with GeoLite2
}
return geo_info

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,6 @@ from jellypy import datatables
from jellypy import helpers
from jellypy import libraries
from jellypy import logger
from jellypy import plextv
from jellypy import session

View File

@@ -33,7 +33,6 @@ from jellypy.database import MonitorDatabase
from jellypy.helpers import timestamp
from jellypy.password import check_hash
from jellypy.users import Users, refresh_users
from jellypy.plextv import PlexTV
# Monkey patch SameSite support into cookies.
# https://stackoverflow.com/a/50813092
@@ -51,21 +50,22 @@ def plex_user_login(username=None, password=None, token=None, headers=None):
user_token = None
user_id = None
# Try to login to Plex.tv to check if the user has a vaild account
if username and password:
plex_tv = PlexTV(username=username, password=password, headers=headers)
plex_user = plex_tv.get_token()
if plex_user:
user_token = plex_user['auth_token']
user_id = plex_user['user_id']
elif token:
plex_tv = PlexTV(token=token, headers=headers)
plex_user = plex_tv.get_plex_account_details()
if plex_user:
user_token = token
user_id = plex_user['user_id']
else:
return None
# TODO: Jellyfin
# # Try to login to Plex.tv to check if the user has a vaild account
# if username and password:
# plex_tv = PlexTV(username=username, password=password, headers=headers)
# plex_user = plex_tv.get_token()
# if plex_user:
# user_token = plex_user['auth_token']
# user_id = plex_user['user_id']
# elif token:
# plex_tv = PlexTV(token=token, headers=headers)
# plex_user = plex_tv.get_plex_account_details()
# if plex_user:
# user_token = token
# user_id = plex_user['user_id']
# else:
# return None
if user_token and user_id:
# Try to retrieve the user from the database.
@@ -86,10 +86,12 @@ def plex_user_login(username=None, password=None, token=None, headers=None):
if not jellypy.CONFIG.ALLOW_GUEST_ACCESS:
return None
# The user is in the database, and guest access is enabled, so try to retrieve a server token.
# If a server token is returned, then the user is a valid friend of the server.
plex_tv = PlexTV(token=user_token, headers=headers)
server_token = plex_tv.get_server_token()
# TODO: Jellyfin
# # The user is in the database, and guest access is enabled, so try to retrieve a server token.
# # If a server token is returned, then the user is a valid friend of the server.
# plex_tv = PlexTV(token=user_token, headers=headers)
# server_token = plex_tv.get_server_token()
server_token = None
if server_token:
# Register the new user / update the access tokens.

File diff suppressed because it is too large Load Diff