diff --git a/data/interfaces/default/current_activity_instance.html b/data/interfaces/default/current_activity_instance.html
index 9b8253ff..2e840763 100644
--- a/data/interfaces/default/current_activity_instance.html
+++ b/data/interfaces/default/current_activity_instance.html
@@ -75,36 +75,36 @@ DOCUMENTATION :: END
% endif
% if not data['art'].startswith('interfaces') or not data['thumb'].startswith('interfaces'):
- % if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
-
- % elif (data['media_type'] == 'episode' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
-
- % elif data['indexes']:
-
- % else:
- % if data['media_type'] == 'track':
-
-
- % elif data['media_type'] == 'clip':
- % if data['art'].startswith('http'):
-
- % elif data['thumb'].startswith('http'):
-
- % else:
- % if data['art']:
-
-
- % else:
-
-
- % endif
- % endif
- % elif data['media_type'] == 'photo':
-
- % else:
-
- % endif
- % endif
+ % if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
+
+ % elif (data['media_type'] == 'episode' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
+
+ % elif data['indexes']:
+
+ % else:
+ % if data['media_type'] == 'track':
+
+
+ % elif data['media_type'] == 'clip':
+ % if data['art'].startswith('http'):
+
+ % elif data['thumb'].startswith('http'):
+
+ % else:
+ % if data['art']:
+
+
+ % else:
+
+
+ % endif
+ % endif
+ % elif data['media_type'] == 'photo':
+
+ % else:
+
+ % endif
+ % endif
% else:
% endif
diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html
index 7f887eda..f5958390 100644
--- a/data/interfaces/default/settings.html
+++ b/data/interfaces/default/settings.html
@@ -1586,13 +1586,21 @@
{username} |
The username of the person streaming. |
+
+ {device} |
+ The type of client device being used for playback. |
+
{platform} |
- The type of client being used for playback. |
+ The type of client platform being used for playback. |
+
+
+ {product} |
+ The type of client product being used for playback. |
{player} |
- The name of the device being used for playback. |
+ The name of the player being used for playback. |
{ip_address} |
@@ -1644,12 +1652,16 @@
{quality_profile} |
- The Plex quality profile of the stream. (e.g. 4 Mbps 720p) |
+ The Plex quality profile of the stream. (e.g. Original, 4 Mbps 720p, etc.) |
{optimized_version} |
If the stream is an optimized version. (0 or 1) |
+
+ {optimized_version_profile} |
+ The optimized version profile of the stream. |
+
{stream_local} |
If the stream is local. (0 or 1) |
@@ -1681,14 +1693,26 @@
{stream_video_codec} |
The video codec of the stream. |
+
+ {stream_video_codec_level} |
+ The video codec level of the stream. |
+
{stream_video_bitrate} |
The video bitrate (in kbps) of the stream. |
+
+ {stream_video_bit_depth} |
+ The video bit depth of the stream. |
+
{stream_video_framerate} |
The video framerate of the stream. |
+
+ {stream_video_ref_frames} |
+ The video reference frames of the stream. |
+
{stream_video_resolution} |
The video resolution of the stream. |
@@ -1715,7 +1739,7 @@
{stream_audio_bitrate_mode} |
- The audio bitrage mode of the stream. (cbr or vbr) |
+ The audio bitrate mode of the stream. (cbr or vbr) |
{stream_audio_codec} |
@@ -1725,6 +1749,14 @@
{stream_audio_channels} |
The audio channels of the stream. |
+
+ {stream_audio_channel_layout} |
+ The audio channel layout of the stream. |
+
+
+ {stream_audio_sample_rate} |
+ The audio sample rate (in Hz) of the stream. |
+
{stream_audio_language} |
The audio language of the stream. |
@@ -1745,6 +1777,10 @@
{stream_subtitle_format} |
The subtitle format of the stream. |
+
+ {stream_subtitle_forced} |
+ If the subtitles are forced. (0 or 1) |
+
{stream_subtitle_language} |
The subtitle language of the stream. |
@@ -1820,46 +1856,6 @@
{media_type} |
The type of media. (movie, show, season, episode, artist, album, track) |
-
- {container} |
- The media container of the original media. |
-
-
- {aspect_ratio} |
- The aspect ratio of the original media. |
-
-
- {video_codec} |
- The video codec of the original media. |
-
-
- {video_bitrate} |
- The video bitrate of the original media. |
-
-
- {video_framerate} |
- The video framerate of the original media. |
-
-
- {video_resolution} |
- The video resolution of the original media. |
-
-
- {video_width} |
- The video width of the original media. |
-
-
- {video_height} |
- The video height of the original media. |
-
-
- {audio_codec} |
- The audio codec of the original media. |
-
-
- {audio_channels} |
- The audio channels of the original media. |
-
{title} |
The full title of the item. |
@@ -2037,6 +2033,118 @@
{trakt_url} |
The trakt.tv URL for the movie or TV show. |
+
+ {container} |
+ The media container of the original media. |
+
+
+ {bitrate} |
+ The bitrate of the original media. |
+
+
+ {aspect_ratio} |
+ The aspect ratio of the original media. |
+
+
+ {video_codec} |
+ The video codec of the original media. |
+
+
+ {video_codec_level} |
+ The video codec level of the original media. |
+
+
+ {video_bitrate} |
+ The video bitrate of the original media. |
+
+
+ {video_bit_depth} |
+ The video bit depth of the original media. |
+
+
+ {video_framerate} |
+ The video framerate of the original media. |
+
+
+ {video_ref_frames} |
+ The video reference frames of the original media. |
+
+
+ {video_resolution} |
+ The video resolution of the original media. |
+
+
+ {video_height} |
+ The video height of the original media. |
+
+
+ {video_width} |
+ The video width of the original media. |
+
+
+ {video_language} |
+ The video language of the original media. |
+
+
+ {video_language_code} |
+ The video language code of the original media. |
+
+
+ {audio_bitrate} |
+ The audio bitrate of the original media. |
+
+
+ {audio_bitrate_mode} |
+ The audio bitrate mode of the original media. (cbr or vbr) |
+
+
+ {audio_codec} |
+ The audio codec of the original media. |
+
+
+ {audio_channels} |
+ The audio channels of the original media. |
+
+
+ {audio_channel_layout} |
+ The audio channel layout of the original media. |
+
+
+ {audio_language} |
+ The audio language of the original media. |
+
+
+ {audio_language_code} |
+ The audio language code of the original media. |
+
+
+ {subtitle_codec} |
+ The subtitle codec of the original media. |
+
+
+ {subtitle_container} |
+ The subtitle container of the original media. |
+
+
+ {subtitle_format} |
+ The subtitle format of the original media. |
+
+
+ {subtitle_forced} |
+ If the subtitles are forced. (0 or 1) |
+
+
+ {subtitle_location} |
+ The subtitle location of the original media. |
+
+
+ {subtitle_language} |
+ The subtitle language of the original media. |
+
+
+ {subtitle_language_code} |
+ The subtitle language code of the original media. |
+
{file} |
The file path to the item. |
diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py
index cdadda65..ac74e8b0 100644
--- a/plexpy/activity_processor.py
+++ b/plexpy/activity_processor.py
@@ -36,69 +36,69 @@ class ActivityProcessor(object):
def write_session(self, session=None, notify=True):
if session:
- values = {'session_key': session['session_key'],
- 'transcode_key': session['transcode_key'],
- 'section_id': session['section_id'],
- 'rating_key': session['rating_key'],
- 'media_type': session['media_type'],
- 'state': session['state'],
- 'user_id': session['user_id'],
- 'user': session['user'],
- 'machine_id': session['machine_id'],
- 'title': session['title'],
- 'parent_title': session['parent_title'],
- 'grandparent_title': session['grandparent_title'],
- 'full_title': session['full_title'],
- 'media_index': session['media_index'],
- 'parent_media_index': session['parent_media_index'],
- 'thumb': session['thumb'],
- 'parent_thumb': session['parent_thumb'],
- 'grandparent_thumb': session['grandparent_thumb'],
- 'year': session['year'],
- 'friendly_name': session['friendly_name'],
- #'ip_address': session['ip_address'],
- 'player': session['player'],
- 'platform': session['platform'],
- 'parent_rating_key': session['parent_rating_key'],
- 'grandparent_rating_key': session['grandparent_rating_key'],
- 'view_offset': session['view_offset'],
- 'duration': session['duration'],
- 'video_decision': session['video_decision'],
- 'audio_decision': session['audio_decision'],
- 'transcode_decision': session['transcode_decision'],
- 'width': session['width'],
- 'height': session['height'],
- 'container': session['container'],
- 'video_codec': session['video_codec'],
- 'audio_codec': session['audio_codec'],
- 'bitrate': session['bitrate'],
- 'video_resolution': session['video_resolution'],
- 'video_framerate': session['video_framerate'],
- 'aspect_ratio': session['aspect_ratio'],
- 'audio_channels': session['audio_channels'],
- 'transcode_protocol': session['transcode_protocol'],
- 'transcode_container': session['transcode_container'],
- 'transcode_video_codec': session['transcode_video_codec'],
- 'transcode_audio_codec': session['transcode_audio_codec'],
- 'transcode_audio_channels': session['transcode_audio_channels'],
- 'transcode_width': session['transcode_width'],
- 'transcode_height': session['transcode_height'],
+ values = {'session_key': session.get('session_key', ''),
+ 'transcode_key': session.get('transcode_key', ''),
+ 'section_id': session.get('section_id', ''),
+ 'rating_key': session.get('rating_key', ''),
+ 'media_type': session.get('media_type', ''),
+ 'state': session.get('state', ''),
+ 'user_id': session.get('user_id', ''),
+ 'user': session.get('user', ''),
+ 'machine_id': session.get('machine_id', ''),
+ 'title': session.get('title', ''),
+ 'parent_title': session.get('parent_title', ''),
+ 'grandparent_title': session.get('grandparent_title', ''),
+ 'full_title': session.get('full_title', ''),
+ 'media_index': session.get('media_index', ''),
+ 'parent_media_index': session.get('parent_media_index', ''),
+ 'thumb': session.get('thumb', ''),
+ 'parent_thumb': session.get('parent_thumb', ''),
+ 'grandparent_thumb': session.get('grandparent_thumb', ''),
+ 'year': session.get('year', ''),
+ 'friendly_name': session.get('friendly_name', ''),
+ #'ip_address': session.get('ip_address', ''),
+ 'player': session.get('player', ''),
+ 'platform': session.get('platform', ''),
+ 'parent_rating_key': session.get('parent_rating_key', ''),
+ 'grandparent_rating_key': session.get('grandparent_rating_key', ''),
+ 'view_offset': session.get('view_offset', ''),
+ 'duration': session.get('duration', ''),
+ 'video_decision': session.get('video_decision', ''),
+ 'audio_decision': session.get('audio_decision', ''),
+ 'transcode_decision': session.get('transcode_decision', ''),
+ 'width': session.get('width', ''),
+ 'height': session.get('height', ''),
+ 'container': session.get('container', ''),
+ 'video_codec': session.get('video_codec', ''),
+ 'audio_codec': session.get('audio_codec', ''),
+ 'bitrate': session.get('bitrate', ''),
+ 'video_resolution': session.get('video_resolution', ''),
+ 'video_framerate': session.get('video_framerate', ''),
+ 'aspect_ratio': session.get('aspect_ratio', ''),
+ 'audio_channels': session.get('audio_channels', ''),
+ 'transcode_protocol': session.get('transcode_protocol', ''),
+ 'transcode_container': session.get('transcode_container', ''),
+ 'transcode_video_codec': session.get('transcode_video_codec', ''),
+ 'transcode_audio_codec': session.get('transcode_audio_codec', ''),
+ 'transcode_audio_channels': session.get('transcode_audio_channels', ''),
+ 'transcode_width': session.get('transcode_width', ''),
+ 'transcode_height': session.get('transcode_height', ''),
'stopped': None
}
# Add ip_address back into values
if session['ip_address']:
- values.update({'ip_address': session['ip_address']})
+ values.update({'ip_address': session.get('ip_address', 'N/A')})
- keys = {'session_key': session['session_key'],
- 'rating_key': session['rating_key']}
+ keys = {'session_key': session.get('session_key', ''),
+ 'rating_key': session.get('rating_key', '')}
result = self.db.upsert('sessions', values, keys)
if result == 'insert':
# Check if any notification agents have notifications enabled
if notify:
- values.update({'ip_address': session['ip_address']})
+ values.update({'ip_address': session.get('ip_address', 'N/A')})
plexpy.NOTIFY_QUEUE.put({'stream_data': values, 'notify_action': 'on_play'})
# If it's our first write then time stamp it.
diff --git a/plexpy/libraries.py b/plexpy/libraries.py
index bd5ca6cb..2e5b479d 100644
--- a/plexpy/libraries.py
+++ b/plexpy/libraries.py
@@ -404,9 +404,10 @@ class Libraries(object):
for item in children_list:
## TODO: Check list of media info items, currently only grabs first item
media_info = item['media_info'][0] if item['media_info'] else {}
+ media_part_info = media_info['parts'][0] if media_info['parts'] else {}
cached_file_size = cached_items.get(item['rating_key'], None)
- file_size = cached_file_size if cached_file_size else media_info.get('file_size', '')
+ file_size = cached_file_size if cached_file_size else media_part_info.get('file_size', '')
row = {'section_id': library_details['section_id'],
'section_type': library_details['section_type'],
@@ -567,6 +568,7 @@ class Libraries(object):
for child_metadata in metadata:
## TODO: Check list of media info items, currently only grabs first item
media_info = item['media_info'][0] if item['media_info'] else {}
+ media_part_info = media_info['parts'][0] if media_info['parts'] else {}
file_size += helpers.cast_to_int(media_info.get('file_size', 0))
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index 5980c81d..d3ce24b5 100644
--- a/plexpy/notification_handler.py
+++ b/plexpy/notification_handler.py
@@ -118,6 +118,10 @@ def add_notifier_each(notify_action=None, stream_data=None, timeline_data=None,
def notify_conditions(notifier=None, notify_action=None, stream_data=None, timeline_data=None):
if stream_data:
+
+ if stream_data['media_type'] == 'clip':
+ return False
+
# Check if notifications enabled for user and library
user_data = users.Users()
user_details = user_data.get_details(user_id=stream_data['user_id'])
@@ -283,6 +287,16 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
## TODO: Check list of media info items, currently only grabs first item
media_info = metadata['media_info'][0] if metadata['media_info'] else {}
+ media_part_info = media_info['parts'][0] if media_info['parts'] else {}
+
+ stream_video = stream_audio = stream_subtitle = False
+ for stream in media_part_info['streams']:
+ if not stream_video and stream['type'] == '1':
+ media_part_info.update(stream)
+ if not stream_audio and stream['type'] == '2':
+ media_part_info.update(stream)
+ if not stream_subtitle and stream['type'] == '3':
+ media_part_info.update(stream)
child_metadata = grandchild_metadata = []
for key in kwargs.pop('child_keys', []):
@@ -414,7 +428,9 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
'user_streams': user_stream_count,
'user': session.get('friendly_name',''),
'username': session.get('user',''),
+ 'device': session.get('device',''),
'platform': session.get('platform',''),
+ 'product': session.get('product',''),
'player': session.get('player',''),
'ip_address': session.get('ip_address','N/A'),
'stream_duration': stream_duration,
@@ -430,6 +446,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
'subtitle_decision': session.get('subtitle_decision',''),
'quality_profile': session.get('quality_profile',''),
'optimized_version': session.get('optimized_version',''),
+ 'optimized_version_profile': session.get('optimized_version_profile',''),
'stream_local': session.get('local', ''),
'stream_location': session.get('location', ''),
'stream_bandwidth': session.get('bandwidth', ''),
@@ -437,22 +454,28 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
'stream_bitrate': session.get('stream_bitrate', ''),
'stream_aspect_ratio': session.get('stream_aspect_ratio', ''),
'stream_video_codec': session.get('stream_video_codec', ''),
+ 'stream_video_codec_level': session.get('stream_video_codec_level', ''),
'stream_video_bitrate': session.get('stream_video_bitrate', ''),
+ 'stream_video_bit_depth': session.get('stream_video_bit_depth', ''),
'stream_video_framerate': session.get('stream_video_framerate', ''),
+ 'stream_video_ref_frames': session.get('stream_video_ref_frames', ''),
'stream_video_resolution': session.get('stream_video_resolution', ''),
- 'stream_video_height': session.get('stream_height', ''),
- 'stream_video_width': session.get('stream_width', ''),
+ 'stream_video_height': session.get('stream_video_height', ''),
+ 'stream_video_width': session.get('stream_video_width', ''),
'stream_video_language': session.get('stream_video_language', ''),
'stream_video_language_code': session.get('stream_video_language_code', ''),
'stream_audio_bitrate': session.get('stream_audio_bitrate', ''),
'stream_audio_bitrate_mode': session.get('stream_audio_bitrate_mode', ''),
'stream_audio_codec': session.get('stream_audio_codec', ''),
'stream_audio_channels': session.get('stream_audio_channels', ''),
+ 'stream_audio_channel_layout': session.get('stream_audio_channel_layout', ''),
+ 'stream_audio_sample_rate': session.get('stream_audio_sample_rate', ''),
'stream_audio_language': session.get('stream_audio_language', ''),
'stream_audio_language_code': session.get('stream_audio_language_code', ''),
'stream_subtitle_codec': session.get('stream_subtitle_codec', ''),
'stream_subtitle_container': session.get('stream_subtitle_container', ''),
'stream_subtitle_format': session.get('stream_subtitle_format', ''),
+ 'stream_subtitle_forced': session.get('stream_subtitle_forced', ''),
'stream_subtitle_language': session.get('stream_subtitle_language', ''),
'stream_subtitle_language_code': session.get('stream_subtitle_language_code', ''),
'stream_subtitle_location': session.get('stream_subtitle_location', ''),
@@ -470,16 +493,6 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
'machine_id': session.get('machine_id',''),
# Source metadata parameters
'media_type': metadata['media_type'],
- 'container': session.get('container', media_info.get('container','')),
- 'aspect_ratio': session.get('aspect_ratio', media_info.get('aspect_ratio','')),
- 'video_codec': session.get('video_codec', media_info.get('video_codec','')),
- 'video_bitrate': session.get('bitrate', media_info.get('bitrate','')),
- 'video_framerate': session.get('video_framerate', media_info.get('video_framerate','')),
- 'video_resolution': session.get('video_resolution', media_info.get('video_resolution','')),
- 'video_width': session.get('width', media_info.get('width','')),
- 'video_height': session.get('height', media_info.get('height','')),
- 'audio_codec': session.get('audio_codec', media_info.get('audio_codec','')),
- 'audio_channels': session.get('audio_channels', media_info.get('audio_channels','')),
'title': metadata['full_title'],
'library_name': metadata['library_name'],
'show_name': show_name,
@@ -525,8 +538,38 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, *
'themoviedb_url': metadata.get('themoviedb_url',''),
'lastfm_url': metadata.get('lastfm_url',''),
'trakt_url': metadata.get('trakt_url',''),
- 'file': media_info.get('file',''),
- 'file_size': helpers.humanFileSize(media_info.get('file_size','')),
+ 'container': session.get('container', media_info.get('container','')),
+ 'bitrate': session.get('bitrate', media_info.get('bitrate','')),
+ 'aspect_ratio': session.get('aspect_ratio', media_info.get('aspect_ratio','')),
+ 'video_codec': session.get('video_codec', media_info.get('video_codec','')),
+ 'video_codec_level': session.get('video_codec_level', media_info.get('video_codec_level','')),
+ 'video_bitrate': session.get('video_bitrate', media_info.get('video_bitrate','')),
+ 'video_bit_depth': session.get('video_bit_depth', media_info.get('video_bit_depth','')),
+ 'video_framerate': session.get('video_framerate', media_info.get('video_framerate','')),
+ 'video_ref_frames': session.get('video_ref_frames', media_info.get('video_ref_frames','')),
+ 'video_resolution': session.get('video_resolution', media_info.get('video_resolution','')),
+ 'video_height': session.get('height', media_info.get('height','')),
+ 'video_width': session.get('width', media_info.get('width','')),
+ 'video_language': session.get('video_language', media_info.get('video_language','')),
+ 'video_language_code': session.get('video_language_code', media_info.get('video_language_code','')),
+ 'audio_bitrate': session.get('audio_bitrate', media_info.get('audio_bitrate','')),
+ 'audio_bitrate_mode': session.get('audio_bitrate_mode', media_info.get('audio_bitrate_mode','')),
+ 'audio_codec': session.get('audio_codec', media_info.get('audio_codec','')),
+ 'audio_channels': session.get('audio_channels', media_info.get('audio_channels','')),
+ 'audio_channel_layout': session.get('audio_channel_layout', media_info.get('audio_channel_layout','')),
+ 'audio_sample_rate': session.get('audio_sample_rate', media_info.get('audio_sample_rate','')),
+ 'audio_language': session.get('audio_language', media_info.get('audio_language','')),
+ 'audio_language_code': session.get('audio_language_code', media_info.get('audio_language_code','')),
+ 'subtitle_codec': session.get('subtitle_codec', media_info.get('subtitle_codec','')),
+ 'subtitle_container': session.get('subtitle_container', media_info.get('subtitle_container','')),
+ 'subtitle_format': session.get('subtitle_format', media_info.get('subtitle_format','')),
+ 'subtitle_forced': session.get('subtitle_forced', media_info.get('subtitle_forced','')),
+ 'subtitle_location': session.get('subtitle_location', media_info.get('subtitle_location','')),
+ 'subtitle_language': session.get('subtitle_language', media_info.get('subtitle_language','')),
+ 'subtitle_language_code': session.get('subtitle_language_code', media_info.get('subtitle_language_code','')),
+ 'file': media_part_info.get('file',''),
+ 'file_size': helpers.humanFileSize(media_part_info.get('file_size','')),
+ 'indexes': media_part_info.get('indexes',''),
'section_id': metadata['section_id'],
'rating_key': metadata['rating_key'],
'parent_rating_key': metadata['parent_rating_key'],
diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py
index 5b6911c8..24857cc5 100644
--- a/plexpy/pmsconnect.py
+++ b/plexpy/pmsconnect.py
@@ -573,14 +573,14 @@ class PmsConnect(object):
xml_head = metadata.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_metadata: %s." % e)
- return []
+ return {}
- metadata_list = []
+ metadata = {}
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') != '1':
- return metadata_list
+ return metadata
if a.getElementsByTagName('Directory'):
metadata_main = a.getElementsByTagName('Directory')[0]
@@ -598,7 +598,7 @@ class PmsConnect(object):
metadata_type = helpers.get_xml_attr(metadata_main, 'type')
else:
logger.debug(u"PlexPy Pmsconnect :: Metadata failed")
- return []
+ return {}
section_id = helpers.get_xml_attr(a, 'librarySectionID')
library_name = helpers.get_xml_attr(a, 'librarySectionTitle')
@@ -664,7 +664,6 @@ class PmsConnect(object):
'labels': labels,
'full_title': helpers.get_xml_attr(metadata_main, 'title')
}
- metadata_list.append(metadata)
elif metadata_type == 'show':
metadata = {'media_type': metadata_type,
@@ -701,7 +700,6 @@ class PmsConnect(object):
'labels': labels,
'full_title': helpers.get_xml_attr(metadata_main, 'title')
}
- metadata_list.append(metadata)
elif metadata_type == 'season':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
@@ -741,7 +739,6 @@ class PmsConnect(object):
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
helpers.get_xml_attr(metadata_main, 'title'))
}
- metadata_list.append(metadata)
elif metadata_type == 'episode':
grandparent_rating_key = helpers.get_xml_attr(metadata_main, 'grandparentRatingKey')
@@ -781,7 +778,6 @@ class PmsConnect(object):
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
helpers.get_xml_attr(metadata_main, 'title'))
}
- metadata_list.append(metadata)
elif metadata_type == 'artist':
metadata = {'media_type': metadata_type,
@@ -818,7 +814,6 @@ class PmsConnect(object):
'labels': labels,
'full_title': helpers.get_xml_attr(metadata_main, 'title')
}
- metadata_list.append(metadata)
elif metadata_type == 'album':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
@@ -858,7 +853,6 @@ class PmsConnect(object):
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
helpers.get_xml_attr(metadata_main, 'title'))
}
- metadata_list.append(metadata)
elif metadata_type == 'track':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
@@ -898,7 +892,6 @@ class PmsConnect(object):
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
helpers.get_xml_attr(metadata_main, 'title'))
}
- metadata_list.append(metadata)
elif metadata_type == 'photo_album':
metadata = {'media_type': metadata_type,
@@ -935,7 +928,6 @@ class PmsConnect(object):
'labels': labels,
'full_title': helpers.get_xml_attr(metadata_main, 'title')
}
- metadata_list.append(metadata)
elif metadata_type == 'photo':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
@@ -975,36 +967,84 @@ class PmsConnect(object):
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
helpers.get_xml_attr(metadata_main, 'title'))
}
- metadata_list.append(metadata)
else:
- return []
+ return {}
- media_info_list = []
- media_items = metadata_main.getElementsByTagName('Media')
- for item in media_items:
- media_info = {'id': helpers.get_xml_attr(item, 'id'),
- 'container': helpers.get_xml_attr(item, 'container'),
- 'bitrate': helpers.get_xml_attr(item, 'bitrate'),
- 'height': helpers.get_xml_attr(item, 'height'),
- 'width': helpers.get_xml_attr(item, 'width'),
- 'aspect_ratio': helpers.get_xml_attr(item, 'aspectRatio'),
- 'video_codec': helpers.get_xml_attr(item, 'videoCodec'),
- 'video_resolution': helpers.get_xml_attr(item, 'videoResolution'),
- 'video_framerate': helpers.get_xml_attr(item, 'videoFrameRate'),
- 'audio_codec': helpers.get_xml_attr(item, 'audioCodec'),
- 'audio_channels': helpers.get_xml_attr(item, 'audioChannels'),
- 'file': helpers.get_xml_attr(item.getElementsByTagName('Part')[0], 'file'),
- 'file_size': helpers.get_xml_attr(item.getElementsByTagName('Part')[0], 'size')
- }
- media_info_list.append(media_info)
+ if metadata:
+ medias = []
+ media_items = metadata_main.getElementsByTagName('Media')
+ for media in media_items:
+
+ parts = []
+ part_items = media.getElementsByTagName('Part')
+ for part in part_items:
+
+ streams = []
+ stream_items = part.getElementsByTagName('Stream')
+ for stream in stream_items:
+ if helpers.get_xml_attr(stream, 'streamType') == '1':
+ streams.append({'id': helpers.get_xml_attr(stream, 'id'),
+ 'type': helpers.get_xml_attr(stream, 'streamType'),
+ 'video_bitrate': helpers.get_xml_attr(stream, 'bitrate'),
+ 'video_bit_depth': helpers.get_xml_attr(stream, 'bitDepth'),
+ 'video_codec_level': helpers.get_xml_attr(stream, 'level'),
+ 'video_ref_frames': helpers.get_xml_attr(stream, 'refFrames'),
+ 'video_language': helpers.get_xml_attr(stream, 'language'),
+ 'video_language_code': helpers.get_xml_attr(stream, 'languageCode')
+ })
+
+ elif helpers.get_xml_attr(stream, 'streamType') == '2':
+ streams.append({'id': helpers.get_xml_attr(stream, 'id'),
+ 'type': helpers.get_xml_attr(stream, 'streamType'),
+ 'audio_bitrate': helpers.get_xml_attr(stream, 'bitrate'),
+ 'audio_bitrate_mode': helpers.get_xml_attr(stream, 'bitrateMode'),
+ 'audio_channel_layout': helpers.get_xml_attr(stream, 'audioChannelLayout'),
+ 'audio_sample_rate': helpers.get_xml_attr(stream, 'samplingRate'),
+ 'audio_language': helpers.get_xml_attr(stream, 'language'),
+ 'audio_language_code': helpers.get_xml_attr(stream, 'languageCode'),
+ })
+
+ elif helpers.get_xml_attr(stream, 'streamType') == '3':
+ streams.append({'id': helpers.get_xml_attr(stream, 'id'),
+ 'type': helpers.get_xml_attr(stream, 'streamType'),
+ 'subtitle_codec': helpers.get_xml_attr(stream, 'codec'),
+ 'subtitle_container': helpers.get_xml_attr(stream, 'container'),
+ 'subtitle_format': helpers.get_xml_attr(stream, 'format'),
+ 'subtitle_forced': 1 if helpers.get_xml_attr(stream, 'forced') == '1' else 0,
+ 'subtitle_location': 'external' if helpers.get_xml_attr(stream, 'key') else 'embedded',
+ 'subtitle_language': helpers.get_xml_attr(stream, 'language'),
+ 'subtitle_language_code': helpers.get_xml_attr(stream, 'languageCode')
+ })
+
+ parts.append({'id': helpers.get_xml_attr(part, 'id'),
+ 'file': helpers.get_xml_attr(part, 'file'),
+ 'file_size': helpers.get_xml_attr(part, 'size'),
+ 'indexes': 1 if helpers.get_xml_attr(part, 'indexes') == 'sd' else 0,
+ 'streams': streams
+ })
+
+ medias.append({'id': helpers.get_xml_attr(media, 'id'),
+ 'container': helpers.get_xml_attr(media, 'container'),
+ 'bitrate': helpers.get_xml_attr(media, 'bitrate'),
+ 'height': helpers.get_xml_attr(media, 'height'),
+ 'width': helpers.get_xml_attr(media, 'width'),
+ 'aspect_ratio': helpers.get_xml_attr(media, 'aspectRatio'),
+ 'video_codec': helpers.get_xml_attr(media, 'videoCodec'),
+ 'video_resolution': helpers.get_xml_attr(media, 'videoResolution'),
+ 'video_framerate': helpers.get_xml_attr(media, 'videoFrameRate'),
+ 'audio_codec': helpers.get_xml_attr(media, 'audioCodec'),
+ 'audio_channels': helpers.get_xml_attr(media, 'audioChannels'),
+ 'optimized_version': 1 if helpers.get_xml_attr(media, 'optimizedForStreaming') else 0,
+ 'parts': parts
+ })
- metadata['media_info'] = media_info_list
+ metadata['media_info'] = medias
- if metadata_list:
- return metadata_list[0]
+ if metadata:
+ return metadata
else:
- return []
+ return {}
def get_metadata_children_details(self, rating_key='', get_children=False):
"""
@@ -1257,9 +1297,14 @@ class PmsConnect(object):
media_parts_stream_info = []
for stream_media_info in media_info_all:
- stream_media_parts_info = stream_media_info.getElementsByTagName('Part')[0]
- if stream_media_parts_info.getElementsByTagName('Stream'):
- media_parts_stream_info = stream_media_parts_info.getElementsByTagName('Stream')
+ media_part_info_all = stream_media_info.getElementsByTagName('Part')
+
+ for stream_media_parts_info in media_part_info_all:
+ if stream_media_parts_info.getElementsByTagName('Stream'):
+ media_parts_stream_info = stream_media_parts_info.getElementsByTagName('Stream')
+ break
+
+ if media_parts_stream_info:
break
# Get the stream details
@@ -1274,30 +1319,40 @@ class PmsConnect(object):
elif helpers.get_xml_attr(stream, 'streamType') == '3':
subtitle_stream_info = stream
+ video_id = audio_id = subtitle_id = None
video_details = audio_details = subtitle_details = {}
if video_stream_info:
+ video_id = helpers.get_xml_attr(video_stream_info, 'id')
video_details = {'stream_video_bitrate': helpers.get_xml_attr(video_stream_info, 'bitrate'),
+ 'stream_video_bit_depth': helpers.get_xml_attr(video_stream_info, 'bitDepth'),
+ 'stream_video_codec_level': helpers.get_xml_attr(video_stream_info, 'level'),
+ 'stream_video_ref_frames': helpers.get_xml_attr(video_stream_info, 'refFrames'),
'stream_video_language': helpers.get_xml_attr(video_stream_info, 'language'),
'stream_video_language_code': helpers.get_xml_attr(video_stream_info, 'languageCode'),
'stream_video_decision': helpers.get_xml_attr(video_stream_info, 'decision') or 'direct play'
}
if audio_stream_info:
+ audio_id = helpers.get_xml_attr(audio_stream_info, 'id')
audio_details = {'stream_audio_bitrate': helpers.get_xml_attr(audio_stream_info, 'bitrate'),
'stream_audio_bitrate_mode': helpers.get_xml_attr(audio_stream_info, 'bitrateMode'),
+ 'stream_audio_channel_layout': helpers.get_xml_attr(audio_stream_info, 'audioChannelLayout'),
+ 'stream_audio_sample_rate': helpers.get_xml_attr(audio_stream_info, 'samplingRate'),
'stream_audio_language': helpers.get_xml_attr(audio_stream_info, 'language'),
'stream_audio_language_code': helpers.get_xml_attr(audio_stream_info, 'languageCode'),
'stream_audio_decision': helpers.get_xml_attr(audio_stream_info, 'decision') or 'direct play'
}
if subtitle_stream_info:
- subtitle_details = {'stream_subtitle_codec': helpers.get_xml_attr(audio_stream_info, 'codec'),
- 'stream_subtitle_container': helpers.get_xml_attr(audio_stream_info, 'container'),
- 'stream_subtitle_format': helpers.get_xml_attr(audio_stream_info, 'format'),
- 'stream_subtitle_language': helpers.get_xml_attr(audio_stream_info, 'language'),
- 'stream_subtitle_language_code': helpers.get_xml_attr(audio_stream_info, 'languageCode'),
- 'stream_subtitle_location': helpers.get_xml_attr(audio_stream_info, 'location'),
- 'stream_subtitle_decision': helpers.get_xml_attr(audio_stream_info, 'decision')
+ subtitle_id = helpers.get_xml_attr(subtitle_stream_info, 'id')
+ subtitle_details = {'stream_subtitle_codec': helpers.get_xml_attr(subtitle_stream_info, 'codec'),
+ 'stream_subtitle_container': helpers.get_xml_attr(subtitle_stream_info, 'container'),
+ 'stream_subtitle_format': helpers.get_xml_attr(subtitle_stream_info, 'format'),
+ 'stream_subtitle_forced': 1 if helpers.get_xml_attr(subtitle_stream_info, 'forced') == '1' else 0,
+ 'stream_subtitle_location': helpers.get_xml_attr(subtitle_stream_info, 'location'),
+ 'stream_subtitle_language': helpers.get_xml_attr(subtitle_stream_info, 'language'),
+ 'stream_subtitle_language_code': helpers.get_xml_attr(subtitle_stream_info, 'languageCode'),
+ 'stream_subtitle_decision': helpers.get_xml_attr(subtitle_stream_info, 'decision')
}
# Get the bif thumbnail
@@ -1309,22 +1364,12 @@ class PmsConnect(object):
else:
bif_thumb = ''
- # Check if it is an optimized version
- if helpers.get_xml_attr(stream_media_parts_info, 'optimizedForStreaming'):
- optimized_version = 1
- else:
- optimized_version = 0
-
# Get the quality profile
if video_details:
quality_profile = common.QUALITY_PROFILES.get(video_details['stream_video_bitrate'], 'Original')
else:
quality_profile = ''
- transcode_decision = helpers.get_xml_attr(stream_media_parts_info, 'decision')
- if transcode_decision == 'directplay':
- transcode_decision = 'direct play'
-
# Check if it is an optimized version
stream_details = {'stream_container': helpers.get_xml_attr(stream_media_info, 'container'),
'stream_bitrate': helpers.get_xml_attr(stream_media_info, 'bitrate'),
@@ -1337,8 +1382,9 @@ class PmsConnect(object):
'stream_video_height': helpers.get_xml_attr(stream_media_info, 'height'),
'stream_video_width': helpers.get_xml_attr(stream_media_info, 'width'),
'stream_duration': helpers.get_xml_attr(stream_media_info, 'duration'),
- 'transcode_decision': transcode_decision,
- 'optimized_version': optimized_version,
+ 'transcode_decision': helpers.get_xml_attr(stream_media_parts_info, 'decision').replace('directplay', 'direct play'),
+ 'optimized_version': 1 if helpers.get_xml_attr(stream_media_info, 'optimizedForStreaming') else 0,
+ 'optimized_version_profile': helpers.get_xml_attr(stream_media_info, 'title'),
'quality_profile': quality_profile,
'indexes': 1 if indexes == 'sd' else 0,
'bif_thumb': bif_thumb,
@@ -1346,23 +1392,62 @@ class PmsConnect(object):
}
# Get the source media info
- media_id = helpers.get_xml_attr(stream_media_info, 'id')
+ media_type = helpers.get_xml_attr(session, 'type')
- metadata_details = self.get_metadata_details(rating_key=helpers.get_xml_attr(session, 'ratingKey'))
- source_media_details = next((m for m in metadata_details.pop('media_info') if m['id'] == media_id), {})
+ source_media_details = source_media_part_details = \
+ source_video_details = source_audio_details = source_subtitle_details = {}
+
+ if media_type == 'clip':
+ clip_media = session.getElementsByTagName('Media')[0]
+ metadata_details = {'rating_key': helpers.get_xml_attr(session, 'ratingKey'),
+ 'title': helpers.get_xml_attr(session, 'title'),
+ 'summary': helpers.get_xml_attr(session, 'summary'),
+ 'duration': helpers.get_xml_attr(session, 'duration'),
+ 'year': helpers.get_xml_attr(session, 'year'),
+ 'thumb': helpers.get_xml_attr(session, 'thumb'),
+ 'art': helpers.get_xml_attr(session, 'art'),
+ 'full_title': helpers.get_xml_attr(session, 'title'),
+ 'container': helpers.get_xml_attr(clip_media, 'container'),
+ 'height': helpers.get_xml_attr(clip_media, 'height'),
+ 'width': helpers.get_xml_attr(clip_media, 'width'),
+ 'video_codec': helpers.get_xml_attr(clip_media, 'videoCodec'),
+ 'video_resolution': helpers.get_xml_attr(clip_media, 'videoResolution'),
+ 'audio_codec': helpers.get_xml_attr(clip_media, 'audioCodec'),
+ 'audio_channels': helpers.get_xml_attr(clip_media, 'audioChannels'),
+ }
+ else:
+ media_id = helpers.get_xml_attr(stream_media_info, 'id')
+ part_id = helpers.get_xml_attr(stream_media_parts_info, 'id')
+
+ metadata_details = self.get_metadata_details(rating_key=helpers.get_xml_attr(session, 'ratingKey'))
+ source_media_details = next((m for m in metadata_details.pop('media_info', []) if m['id'] == media_id), {})
+ source_media_part_details = next((p for p in source_media_details.pop('parts', []) if p['id'] == part_id), {})
+ source_media_part_streams = source_media_part_details.pop('streams', [])
+
+ if video_id:
+ source_video_details = next((p for p in source_media_part_streams if p['id'] == video_id), {})
+ if audio_id:
+ source_audio_details = next((p for p in source_media_part_streams if p['id'] == audio_id), {})
+ if subtitle_id:
+ source_subtitle_details = next((p for p in source_media_part_streams if p['id'] == subtitle_id), {})
# Entire session output (single dict for backwards compatibility)
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
+ 'media_type': media_type,
'view_offset': progress,
'progress_percent': str(helpers.get_percent(progress, stream_details['stream_duration'])),
'user': user_details['username'] # Keep for backwards compatibility
}
+
+ if 'rating_key' not in session_output:
+ session_output['rating_key'] = helpers.get_xml_attr(session, 'ratingKey')
- if helpers.get_xml_attr(session, 'ratingKey').isdigit():
- session_output['media_type'] = helpers.get_xml_attr(session, 'type')
- else:
- session_output['media_type'] = 'clip'
-
+ session_output.update(metadata_details)
+ session_output.update(source_media_details)
+ session_output.update(source_media_part_details)
+ session_output.update(source_video_details)
+ session_output.update(source_audio_details)
+ session_output.update(source_subtitle_details)
session_output.update(user_details)
session_output.update(player_details)
session_output.update(session_details)
@@ -1371,8 +1456,6 @@ class PmsConnect(object):
session_output.update(video_details)
session_output.update(audio_details)
session_output.update(subtitle_details)
- session_output.update(metadata_details)
- session_output.update(source_media_details)
return session_output
@@ -1776,7 +1859,7 @@ class PmsConnect(object):
return labels_list
- def get_image(self, img=None, width='1000', height='1500'):
+ def get_image(self, img=None, width='1000', height='1500', clip=False):
"""
Return image data as array.
Array contains the image content type and image binary
@@ -1788,7 +1871,11 @@ class PmsConnect(object):
"""
if img:
- params = {'url': 'http://127.0.0.1:32400%s?%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
+ if clip:
+ params = {'url': '%s&%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
+ else:
+ params = {'url': 'http://127.0.0.1:32400%s?%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
+
if width.isdigit() and height.isdigit():
params['width'] = width
params['height'] = height
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 2e11f9a2..0972afb2 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -3509,7 +3509,7 @@ class WebInterface(object):
@addtoapi('pms_image_proxy')
def real_pms_image_proxy(self, img='', rating_key=None, width='0', height='0',
- fallback=None, refresh=False, **kwargs):
+ fallback=None, refresh=False, clip=False, **kwargs):
""" Gets an image from the PMS and saves it to the image cache directory.
```
@@ -3545,6 +3545,8 @@ class WebInterface(object):
if not os.path.exists(c_dir):
os.mkdir(c_dir)
+ clip = True if clip == 'true' else False
+
try:
if not plexpy.CONFIG.CACHE_IMAGES or refresh or 'indexes' in img:
raise NotFound
@@ -3555,7 +3557,7 @@ class WebInterface(object):
# the image does not exist, download it from pms
try:
pms_connect = pmsconnect.PmsConnect()
- result = pms_connect.get_image(img, width, height)
+ result = pms_connect.get_image(img, width, height, clip=clip)
if result and result[0]:
cherrypy.response.headers['Content-type'] = result[1]