mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 15:56:07 -07:00
Add very early attempt at retrieving IP address per stream.
This commit is contained in:
parent
9d5dabca14
commit
7a5cad1a31
5 changed files with 125 additions and 34 deletions
|
@ -22,10 +22,13 @@ thumb Returns the location of the item's thumbnail. Use with p
|
||||||
art Returns the location of the item's artwork
|
art Returns the location of the item's artwork
|
||||||
progress_percent Returns the current progress of the item. 0 to 100.
|
progress_percent Returns the current progress of the item. 0 to 100.
|
||||||
user Returns the name of the user owning the session.
|
user Returns the name of the user owning the session.
|
||||||
|
user_id Returns the Plex user id if available.
|
||||||
|
machine_id Returns the machine id of the players being used.
|
||||||
friendly_name Returns the friendlly name of the user owning the session.
|
friendly_name Returns the friendlly name of the user owning the session.
|
||||||
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||||
title Returns the name of the episode, movie or music track.
|
title Returns the name of the episode, movie or music track.
|
||||||
player Returns the name of the platform used to play the stream.
|
player Returns the name of the platform used to play the stream.
|
||||||
|
platform Returns the type of platform used to play the stream.
|
||||||
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
audio_decision Returns the audio transcode decision. Either 'transcode', 'copy' or 'direct play'.
|
||||||
audio_codec Returns the name of the audio codec.
|
audio_codec Returns the name of the audio codec.
|
||||||
audio_channels Returns the number of audio channels.
|
audio_channels Returns the number of audio channels.
|
||||||
|
@ -134,7 +137,7 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$("#platform-${a['session_key']}").html("<img src='" + getPlatformImagePath('${a['player']}') + "'>");
|
$("#platform-${a['session_key']}").html("<img src='" + getPlatformImagePath('${a['platform']}') + "'>");
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
% endfor
|
% endfor
|
||||||
|
|
|
@ -18,7 +18,7 @@ import re
|
||||||
import os
|
import os
|
||||||
import plexpy
|
import plexpy
|
||||||
|
|
||||||
def get_log_tail(window=20):
|
def get_log_tail(window=20, parsed=True):
|
||||||
|
|
||||||
if plexpy.CONFIG.PMS_LOGS_FOLDER:
|
if plexpy.CONFIG.PMS_LOGS_FOLDER:
|
||||||
log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, 'Plex Media Server.log')
|
log_file = os.path.join(plexpy.CONFIG.PMS_LOGS_FOLDER, 'Plex Media Server.log')
|
||||||
|
@ -33,24 +33,27 @@ def get_log_tail(window=20):
|
||||||
|
|
||||||
log_lines = tail(logfile, window)
|
log_lines = tail(logfile, window)
|
||||||
|
|
||||||
line_error = False
|
if parsed:
|
||||||
clean_lines = []
|
line_error = False
|
||||||
for i in log_lines:
|
clean_lines = []
|
||||||
try:
|
for i in log_lines:
|
||||||
log_time = i.split(' [')[0]
|
try:
|
||||||
log_level = i.split('] ', 1)[1].split(' - ',1)[0]
|
log_time = i.split(' [')[0]
|
||||||
log_msg = i.split('] ', 1)[1].split(' - ',1)[1]
|
log_level = i.split('] ', 1)[1].split(' - ',1)[0]
|
||||||
full_line = [log_time, log_level, log_msg]
|
log_msg = i.split('] ', 1)[1].split(' - ',1)[1]
|
||||||
clean_lines.append(full_line)
|
full_line = [log_time, log_level, log_msg]
|
||||||
except:
|
clean_lines.append(full_line)
|
||||||
line_error = True
|
except:
|
||||||
full_line = ['', '', 'Unable to parse log line.']
|
line_error = True
|
||||||
clean_lines.append(full_line)
|
full_line = ['', '', 'Unable to parse log line.']
|
||||||
|
clean_lines.append(full_line)
|
||||||
|
|
||||||
if line_error:
|
if line_error:
|
||||||
logger.error('PlexPy was unable to parse some lines of the Plex Media Server log.')
|
logger.error('PlexPy was unable to parse some lines of the Plex Media Server log.')
|
||||||
|
|
||||||
return clean_lines
|
return clean_lines
|
||||||
|
|
||||||
|
return log_lines
|
||||||
|
|
||||||
# http://stackoverflow.com/a/13790289/2405162
|
# http://stackoverflow.com/a/13790289/2405162
|
||||||
def tail(f, lines=1, _buffer=4098):
|
def tail(f, lines=1, _buffer=4098):
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, helpers, plexwatch, pmsconnect, notification_handler, config
|
from plexpy import logger, helpers, plexwatch, pmsconnect, notification_handler, config, log_reader
|
||||||
|
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from httplib import HTTPSConnection
|
from httplib import HTTPSConnection
|
||||||
|
@ -23,6 +23,8 @@ import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import threading
|
import threading
|
||||||
import plexpy
|
import plexpy
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
monitor_lock = threading.Lock()
|
monitor_lock = threading.Lock()
|
||||||
|
|
||||||
|
@ -32,6 +34,7 @@ def check_active_sessions():
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
session_list = pms_connect.get_current_activity()
|
session_list = pms_connect.get_current_activity()
|
||||||
monitor_db = MonitorDatabase()
|
monitor_db = MonitorDatabase()
|
||||||
|
# logger.debug(u"Checking for active streams.")
|
||||||
|
|
||||||
if session_list['stream_count'] != '0':
|
if session_list['stream_count'] != '0':
|
||||||
media_container = session_list['sessions']
|
media_container = session_list['sessions']
|
||||||
|
@ -46,6 +49,7 @@ def check_active_sessions():
|
||||||
title = session['title']
|
title = session['title']
|
||||||
parent_title = session['parent_title']
|
parent_title = session['parent_title']
|
||||||
grandparent_title = session['grandparent_title']
|
grandparent_title = session['grandparent_title']
|
||||||
|
machine_id = session['machine_id']
|
||||||
|
|
||||||
write_session = monitor_db.write_session_key(session_key, rating_key, media_type)
|
write_session = monitor_db.write_session_key(session_key, rating_key, media_type)
|
||||||
if write_session == 'insert':
|
if write_session == 'insert':
|
||||||
|
@ -60,6 +64,12 @@ def check_active_sessions():
|
||||||
pushmessage = '%s (%s) starting playing %s' % (friendly_name, platform, item_title)
|
pushmessage = '%s (%s) starting playing %s' % (friendly_name, platform, item_title)
|
||||||
notification_handler.push_nofitications(pushmessage, 'PlexPy Playback started', 'Playback Started')
|
notification_handler.push_nofitications(pushmessage, 'PlexPy Playback started', 'Playback Started')
|
||||||
|
|
||||||
|
# Try and grab IP address from logs
|
||||||
|
if plexpy.CONFIG.PMS_LOGS_FOLDER:
|
||||||
|
monitor_processing = MonitorProcessing()
|
||||||
|
ip_address = monitor_processing.find_session_ip(rating_key=rating_key,
|
||||||
|
machine_id=machine_id)
|
||||||
|
|
||||||
keys = {'session_key': session_key,
|
keys = {'session_key': session_key,
|
||||||
'rating_key': rating_key}
|
'rating_key': rating_key}
|
||||||
active_streams.append(keys)
|
active_streams.append(keys)
|
||||||
|
@ -186,3 +196,54 @@ class MonitorDatabase(object):
|
||||||
|
|
||||||
# We want to know if it was an update or insert
|
# We want to know if it was an update or insert
|
||||||
return trans_type
|
return trans_type
|
||||||
|
|
||||||
|
|
||||||
|
class MonitorProcessing(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def find_session_ip(self, rating_key=None, machine_id=None):
|
||||||
|
|
||||||
|
logger.debug(u"Requesting log lines...")
|
||||||
|
log_lines = log_reader.get_log_tail(window=5000, parsed=False)
|
||||||
|
|
||||||
|
rating_key_line = 'metadata%2F' + rating_key
|
||||||
|
machine_id_line = 'session=' + machine_id
|
||||||
|
|
||||||
|
for line in reversed(log_lines):
|
||||||
|
# We're good if we find a line with both machine id and rating key
|
||||||
|
# This is usually when there is a transcode session
|
||||||
|
if machine_id_line in line and rating_key_line in line:
|
||||||
|
# Currently only checking for ipv4 addresses
|
||||||
|
ipv4 = re.findall(r'[0-9]+(?:\.[0-9]+){3}', line)
|
||||||
|
if ipv4:
|
||||||
|
# The logged IP will always be the first match and we don't want localhost entries
|
||||||
|
if ipv4[0] != '127.0.0.1':
|
||||||
|
logger.debug(u"Matched IP address (%s) for stream ratingKey %s and machineIdentifier %s."
|
||||||
|
% (ipv4[0], rating_key, machine_id))
|
||||||
|
return ipv4[0]
|
||||||
|
|
||||||
|
logger.debug(u"Unable to find IP address on first pass. Attempting fallback check in 5 seconds...")
|
||||||
|
|
||||||
|
# Wait for the log to catch up and read in new lines
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
logger.debug(u"Requesting log lines...")
|
||||||
|
log_lines = log_reader.get_log_tail(window=5000, parsed=False)
|
||||||
|
|
||||||
|
for line in reversed(log_lines):
|
||||||
|
if 'GET /:/timeline' in line and rating_key_line in line:
|
||||||
|
# Currently only checking for ipv4 addresses
|
||||||
|
# This method can return the wrong IP address if more than one user
|
||||||
|
# starts watching the same media item around the same time.
|
||||||
|
ipv4 = re.findall(r'[0-9]+(?:\.[0-9]+){3}', line)
|
||||||
|
if ipv4:
|
||||||
|
# The logged IP will always be the first match and we don't want localhost entries
|
||||||
|
if ipv4[0] != '127.0.0.1':
|
||||||
|
logger.debug(u"Matched IP address (%s) for stream ratingKey %s." % (ipv4[0], rating_key))
|
||||||
|
return ipv4[0]
|
||||||
|
|
||||||
|
logger.debug(u"Unable to find IP address on fallback search. Not logging IP address.")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
@ -24,7 +24,7 @@ class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.protocol = 'HTTPS'
|
self.protocol = 'HTTP'
|
||||||
self.request_handler = http_handler.HTTPHandler(host=plexpy.CONFIG.PMS_IP,
|
self.request_handler = http_handler.HTTPHandler(host=plexpy.CONFIG.PMS_IP,
|
||||||
port=plexpy.CONFIG.PMS_PORT,
|
port=plexpy.CONFIG.PMS_PORT,
|
||||||
token=plexpy.CONFIG.PMS_TOKEN)
|
token=plexpy.CONFIG.PMS_TOKEN)
|
||||||
|
@ -196,7 +196,7 @@ class PmsConnect(object):
|
||||||
for a in xml_head:
|
for a in xml_head:
|
||||||
if a.getAttribute('size'):
|
if a.getAttribute('size'):
|
||||||
if a.getAttribute('size') == '0':
|
if a.getAttribute('size') == '0':
|
||||||
output = {'recently_added': None}
|
output = {'recently_added': []}
|
||||||
return output
|
return output
|
||||||
|
|
||||||
if a.getElementsByTagName('Directory'):
|
if a.getElementsByTagName('Directory'):
|
||||||
|
@ -459,14 +459,24 @@ class PmsConnect(object):
|
||||||
duration = helpers.get_xml_attr(media_info, 'duration')
|
duration = helpers.get_xml_attr(media_info, 'duration')
|
||||||
progress = helpers.get_xml_attr(session, 'viewOffset')
|
progress = helpers.get_xml_attr(session, 'viewOffset')
|
||||||
|
|
||||||
|
user_details = plex_watch.get_user_details(
|
||||||
|
user=helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'))
|
||||||
|
|
||||||
|
if helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier').endswith('_Track'):
|
||||||
|
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')[:-6]
|
||||||
|
else:
|
||||||
|
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')
|
||||||
|
|
||||||
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
||||||
'art': helpers.get_xml_attr(session, 'art'),
|
'art': helpers.get_xml_attr(session, 'art'),
|
||||||
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
||||||
'thumb': helpers.get_xml_attr(session, 'thumb'),
|
'thumb': helpers.get_xml_attr(session, 'thumb'),
|
||||||
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||||
'friendly_name': plex_watch.get_user_friendly_name(
|
'user_id': user_details['user_id'],
|
||||||
helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
'friendly_name': user_details['friendly_name'],
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
|
'machine_id': machine_id,
|
||||||
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||||
|
@ -528,15 +538,25 @@ class PmsConnect(object):
|
||||||
thumb = helpers.get_xml_attr(session, 'thumb')
|
thumb = helpers.get_xml_attr(session, 'thumb')
|
||||||
use_indexes = 0
|
use_indexes = 0
|
||||||
|
|
||||||
|
user_details = plex_watch.get_user_details(
|
||||||
|
user=helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'))
|
||||||
|
|
||||||
|
if helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier').endswith('_Video'):
|
||||||
|
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')[:-6]
|
||||||
|
else:
|
||||||
|
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')
|
||||||
|
|
||||||
if helpers.get_xml_attr(session, 'type') == 'episode':
|
if helpers.get_xml_attr(session, 'type') == 'episode':
|
||||||
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
|
||||||
'art': helpers.get_xml_attr(session, 'art'),
|
'art': helpers.get_xml_attr(session, 'art'),
|
||||||
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
||||||
'thumb': thumb,
|
'thumb': thumb,
|
||||||
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||||
'friendly_name': plex_watch.get_user_friendly_name(
|
'user_id': user_details['user_id'],
|
||||||
helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
'friendly_name': user_details['friendly_name'],
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
|
'machine_id': machine_id,
|
||||||
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||||
|
@ -561,9 +581,11 @@ class PmsConnect(object):
|
||||||
'thumb': thumb,
|
'thumb': thumb,
|
||||||
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
||||||
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||||
'friendly_name': plex_watch.get_user_friendly_name(
|
'user_id': user_details['user_id'],
|
||||||
helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
'friendly_name': user_details['friendly_name'],
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
|
'machine_id': machine_id,
|
||||||
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||||
|
@ -588,9 +610,11 @@ class PmsConnect(object):
|
||||||
'thumb': thumb,
|
'thumb': thumb,
|
||||||
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'),
|
||||||
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
'user': helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title'),
|
||||||
'friendly_name': plex_watch.get_user_friendly_name(
|
'user_id': user_details['user_id'],
|
||||||
helpers.get_xml_attr(session.getElementsByTagName('User')[0], 'title')),
|
'friendly_name': user_details['friendly_name'],
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
|
'machine_id': machine_id,
|
||||||
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
'state': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'state'),
|
||||||
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'),
|
||||||
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(session, 'parentTitle'),
|
||||||
|
|
|
@ -952,4 +952,4 @@ class WebInterface(object):
|
||||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
logger.warn('Unable to retrieve data.')
|
logger.warn('Unable to retrieve data.')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue