diff --git a/data/interfaces/default/current_activity_instance.html b/data/interfaces/default/current_activity_instance.html index 474d4a6a..b726ab85 100644 --- a/data/interfaces/default/current_activity_instance.html +++ b/data/interfaces/default/current_activity_instance.html @@ -471,7 +471,7 @@ DOCUMENTATION :: END S${data['parent_media_index']} · E${data['media_index']} % else: - Live TV + ${data['originally_available_at']} % endif % else: Live TV diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js index e7166314..1bd0b54c 100644 --- a/data/interfaces/default/js/tables/history_table.js +++ b/data/interfaces/default/js/tables/history_table.js @@ -174,6 +174,7 @@ history_table_options = { icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television'; icon_title = (rowData['live']) ? 'Live TV' : 'Movie'; if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; } + else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; } media_type = ''; thumb_popover = '' + cellData + parent_info + '' $(td).html('
' + media_type + ' ' + thumb_popover + '
'); diff --git a/data/interfaces/default/js/tables/history_table_modal.js b/data/interfaces/default/js/tables/history_table_modal.js index e28b069d..f478cd94 100644 --- a/data/interfaces/default/js/tables/history_table_modal.js +++ b/data/interfaces/default/js/tables/history_table_modal.js @@ -115,6 +115,7 @@ history_table_modal_options = { icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television'; icon_title = (rowData['live']) ? 'Live TV' : 'Movie'; if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; } + else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; } media_type = ''; thumb_popover = '' + cellData + parent_info + '' $(td).html('
' + media_type + ' ' + thumb_popover + '
'); diff --git a/data/interfaces/default/js/tables/libraries.js b/data/interfaces/default/js/tables/libraries.js index 060580b2..45393b1e 100644 --- a/data/interfaces/default/js/tables/libraries.js +++ b/data/interfaces/default/js/tables/libraries.js @@ -154,6 +154,7 @@ libraries_list_table_options = { icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television'; icon_title = (rowData['live']) ? 'Live TV' : 'Movie'; if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; } + else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; } media_type = ''; thumb_popover = '' + cellData + parent_info + '' $(td).html('
' + media_type + ' ' + thumb_popover + '
'); diff --git a/data/interfaces/default/js/tables/user_ips.js b/data/interfaces/default/js/tables/user_ips.js index eea188ab..ac198715 100644 --- a/data/interfaces/default/js/tables/user_ips.js +++ b/data/interfaces/default/js/tables/user_ips.js @@ -99,6 +99,7 @@ user_ip_table_options = { icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television'; icon_title = (rowData['live']) ? 'Live TV' : 'Movie'; if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; } + else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; } media_type = ''; thumb_popover = '' + cellData + parent_info + '' $(td).html('
' + media_type + ' ' + thumb_popover + '
'); diff --git a/data/interfaces/default/js/tables/users.js b/data/interfaces/default/js/tables/users.js index 75ab6a8f..58d38327 100644 --- a/data/interfaces/default/js/tables/users.js +++ b/data/interfaces/default/js/tables/users.js @@ -174,6 +174,7 @@ users_list_table_options = { icon = (rowData['live']) ? 'fa-broadcast-tower' : 'fa-television'; icon_title = (rowData['live']) ? 'Live TV' : 'Movie'; if (!isNaN(parseInt(rowData['parent_media_index'])) && !isNaN(parseInt(rowData['media_index']))) { parent_info = ' (S' + rowData['parent_media_index'] + ' · E' + rowData['media_index'] + ')'; } + else if (rowData['live'] && rowData['originally_available_at']) { parent_info = ' (' + rowData['originally_available_at'] + ')'; } media_type = ''; thumb_popover = '' + cellData + parent_info + '' $(td).html('
' + media_type + ' ' + thumb_popover + '
'); diff --git a/plexpy/__init__.py b/plexpy/__init__.py index 15188922..d105bac9 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -585,6 +585,7 @@ def dbcheck(): 'media_index INTEGER, parent_media_index INTEGER, ' 'thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, year INTEGER, ' 'parent_rating_key INTEGER, grandparent_rating_key INTEGER, ' + 'originally_available_at TEXT, added_at INTEGER, ' 'view_offset INTEGER DEFAULT 0, duration INTEGER, video_decision TEXT, audio_decision TEXT, ' 'transcode_decision TEXT, container TEXT, bitrate INTEGER, width INTEGER, height INTEGER, ' 'video_codec TEXT, video_bitrate INTEGER, video_resolution TEXT, video_width INTEGER, video_height INTEGER, ' @@ -1236,6 +1237,18 @@ def dbcheck(): 'ALTER TABLE sessions ADD COLUMN channel_thumb TEXT' ) + # Upgrade sessions table from earlier versions + try: + c_db.execute('SELECT originally_available_at FROM sessions') + except sqlite3.OperationalError: + logger.debug(u"Altering database. Updating database table sessions.") + c_db.execute( + 'ALTER TABLE sessions ADD COLUMN originally_available_at TEXT' + ) + c_db.execute( + 'ALTER TABLE sessions ADD COLUMN added_at INTEGER' + ) + # Upgrade session_history table from earlier versions try: c_db.execute('SELECT reference_id FROM session_history') diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py index 84ccc76c..ebea8757 100644 --- a/plexpy/activity_processor.py +++ b/plexpy/activity_processor.py @@ -61,6 +61,8 @@ class ActivityProcessor(object): 'platform': session.get('platform', ''), 'parent_rating_key': session.get('parent_rating_key', ''), 'grandparent_rating_key': session.get('grandparent_rating_key', ''), + 'originally_available_at': session.get('originally_available_at', ''), + 'added_at': session.get('added_at', ''), 'view_offset': session.get('view_offset', ''), 'duration': session.get('duration', ''), 'video_decision': session.get('video_decision', ''), diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 0b35d705..254713a0 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with Tautulli. If not, see . -import arrow import json from itertools import groupby @@ -97,6 +96,8 @@ class DataFactory(object): 'session_history_metadata.parent_thumb', 'session_history_metadata.grandparent_thumb', 'session_history_metadata.live', + 'session_history_metadata.added_at', + 'session_history_metadata.originally_available_at', 'MAX((CASE WHEN (view_offset IS NULL OR view_offset = "") THEN 0.1 ELSE view_offset * 1.0 END) / \ (CASE WHEN (session_history_metadata.duration IS NULL OR session_history_metadata.duration = "") \ THEN 1.0 ELSE session_history_metadata.duration * 1.0 END) * 100) AS percent_complete', @@ -146,6 +147,8 @@ class DataFactory(object): 'parent_thumb', 'grandparent_thumb', 'live', + 'originally_available_at', + 'added_at', 'MAX((CASE WHEN (view_offset IS NULL OR view_offset = "") THEN 0.1 ELSE view_offset * 1.0 END) / \ (CASE WHEN (duration IS NULL OR duration = "") \ THEN 1.0 ELSE duration * 1.0 END) * 100) AS percent_complete', @@ -220,6 +223,10 @@ class DataFactory(object): else: watched_status = 0 + # Fake Live TV air date using added_at timestamp + if item['live'] and not item['originally_available_at']: + item['originally_available_at'] = helpers.timestamp_to_iso_date(item['added_at']) + # Rename Mystery platform names platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']) @@ -251,6 +258,7 @@ class DataFactory(object): 'media_index': item['media_index'], 'parent_media_index': item['parent_media_index'], 'thumb': thumb, + 'originally_available_at': item['originally_available_at'], 'transcode_decision': item['transcode_decision'], 'percent_complete': int(round(item['percent_complete'])), 'watched_status': watched_status, @@ -1052,11 +1060,9 @@ class DataFactory(object): else: section_name = '' - if item['live']: - # Fake Live TV air date using added_at timestamp - originally_available_at = item['originally_available_at'] or arrow.get(item['added_at']).format('YYYY-MM-DD') - else: - originally_available_at = item['originally_available_at'] + # Fake Live TV air date using added_at timestamp + if item['live'] and not item['originally_available_at']: + item['originally_available_at'] = helpers.timestamp_to_iso_date(item['added_at']) directors = item['directors'].split(';') if item['directors'] else [] writers = item['writers'].split(';') if item['writers'] else [] @@ -1098,7 +1104,7 @@ class DataFactory(object): 'parent_thumb': item['parent_thumb'], 'grandparent_thumb': item['grandparent_thumb'], 'art': item['art'], - 'originally_available_at': originally_available_at, + 'originally_available_at': item['originally_available_at'], 'added_at': item['added_at'], 'updated_at': item['updated_at'], 'last_viewed_at': item['last_viewed_at'], diff --git a/plexpy/helpers.py b/plexpy/helpers.py index ae47f5cd..078524b8 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -221,6 +221,10 @@ def utc_now_iso(): return utcnow.isoformat() +def timestamp_to_iso_date(timestamp): + return datetime.datetime.fromtimestamp(cast_to_int(timestamp)).strftime("%Y-%m-%d") + + def human_duration(s, sig='dhms'): hd = '' diff --git a/plexpy/libraries.py b/plexpy/libraries.py index 5fef3a03..f6ae9b41 100644 --- a/plexpy/libraries.py +++ b/plexpy/libraries.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with Tautulli. If not, see . -import arrow import json import os @@ -286,6 +285,9 @@ class Libraries(object): 'session_history_metadata.parent_media_index', 'session_history_metadata.content_rating', 'session_history_metadata.labels', + 'session_history_metadata.live', + 'session_history_metadata.added_at', + 'session_history_metadata.originally_available_at', 'library_sections.do_notify', 'library_sections.do_notify_created', 'library_sections.keep_history' @@ -327,6 +329,10 @@ class Libraries(object): else: library_thumb = common.DEFAULT_COVER_THUMB + # Fake Live TV air date using added_at timestamp + if item['live'] and not item['originally_available_at']: + item['originally_available_at'] = helpers.timestamp_to_iso_date(item['added_at']) + row = {'section_id': item['section_id'], 'section_name': item['section_name'], 'section_type': item['section_type'], @@ -349,6 +355,8 @@ class Libraries(object): 'parent_media_index': item['parent_media_index'], 'content_rating': item['content_rating'], 'labels': item['labels'].split(';') if item['labels'] else (), + 'live': item['live'], + 'originally_available_at': item['originally_available_at'], 'do_notify': helpers.checked(item['do_notify']), 'do_notify_created': helpers.checked(item['do_notify_created']), 'keep_history': helpers.checked(item['keep_history']) @@ -905,12 +913,9 @@ class Libraries(object): else: thumb = row['thumb'] - if row['live']: - # Fake Live TV air date using added_at timestamp - originally_available_at = row['originally_available_at'] or arrow.get(row['added_at']).format( - 'YYYY-MM-DD') - else: - originally_available_at = row['originally_available_at'] + # Fake Live TV air date using added_at timestamp + if row['live'] and not row['originally_available_at']: + row['originally_available_at'] = helpers.timestamp_to_iso_date(row['added_at']) recent_output = {'row_id': row['id'], 'media_type': row['media_type'], @@ -925,7 +930,7 @@ class Libraries(object): 'media_index': row['media_index'], 'parent_media_index': row['parent_media_index'], 'year': row['year'], - 'originally_available_at': originally_available_at, + 'originally_available_at': row['originally_available_at'], 'live': row['live'], 'time': row['started'], 'user': row['user'], diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index d83cfd16..8adaedf7 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -1961,6 +1961,10 @@ class PmsConnect(object): source_subtitle_details = next((p for p in source_media_part_streams if p['id'] == subtitle_id), next((p for p in source_media_part_streams if p['type'] == '3'), source_subtitle_details)) + # Fake Live TV air date using added_at timestamp + if stream_details['live'] and not metadata_details['originally_available_at']: + metadata_details['originally_available_at'] = helpers.timestamp_to_iso_date(metadata_details['added_at']) + # Overrides for live sessions if stream_details['live'] and transcode_session: stream_details['stream_container_decision'] = 'transcode' diff --git a/plexpy/users.py b/plexpy/users.py index 5bdc331f..88bec3bf 100644 --- a/plexpy/users.py +++ b/plexpy/users.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with Tautulli. If not, see . -import arrow import httpagentparser import time @@ -118,6 +117,8 @@ class Users(object): 'session_history_metadata.media_index', 'session_history_metadata.parent_media_index', 'session_history_metadata.live', + 'session_history_metadata.added_at', + 'session_history_metadata.originally_available_at', 'session_history_media_info.transcode_decision', 'users.do_notify as do_notify', 'users.keep_history as keep_history', @@ -160,6 +161,10 @@ class Users(object): else: user_thumb = common.DEFAULT_USER_THUMB + # Fake Live TV air date using added_at timestamp + if item['live'] and not item['originally_available_at']: + item['originally_available_at'] = helpers.timestamp_to_iso_date(item['added_at']) + # Rename Mystery platform names platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']) @@ -182,6 +187,7 @@ class Users(object): 'media_index': item['media_index'], 'parent_media_index': item['parent_media_index'], 'live': item['live'], + 'originally_available_at': item['originally_available_at'], 'transcode_decision': item['transcode_decision'], 'do_notify': helpers.checked(item['do_notify']), 'keep_history': helpers.checked(item['keep_history']), @@ -229,6 +235,8 @@ class Users(object): 'session_history_metadata.media_index', 'session_history_metadata.parent_media_index', 'session_history_metadata.live', + 'session_history_metadata.added_at', + 'session_history_metadata.originally_available_at', 'session_history_media_info.transcode_decision', 'session_history.user', 'session_history.user_id as custom_user_id', @@ -266,6 +274,10 @@ class Users(object): else: thumb = item["thumb"] + # Fake Live TV air date using added_at timestamp + if item['live'] and not item['originally_available_at']: + item['originally_available_at'] = helpers.timestamp_to_iso_date(item['added_at']) + # Rename Mystery platform names platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"]) @@ -284,6 +296,7 @@ class Users(object): 'media_index': item['media_index'], 'parent_media_index': item['parent_media_index'], 'live': item['live'], + 'originally_available_at': item['originally_available_at'], 'transcode_decision': item['transcode_decision'], 'friendly_name': item['friendly_name'], 'user_id': item['custom_user_id'] @@ -565,11 +578,9 @@ class Users(object): else: thumb = row['thumb'] - if row['live']: - # Fake Live TV air date using added_at timestamp - originally_available_at = row['originally_available_at'] or arrow.get(row['added_at']).format('YYYY-MM-DD') - else: - originally_available_at = row['originally_available_at'] + # Fake Live TV air date using added_at timestamp + if row['live'] and not row['originally_available_at']: + row['originally_available_at'] = helpers.timestamp_to_iso_date(row['added_at']) recent_output = {'row_id': row['id'], 'media_type': row['media_type'], @@ -584,7 +595,7 @@ class Users(object): 'media_index': row['media_index'], 'parent_media_index': row['parent_media_index'], 'year': row['year'], - 'originally_available_at': originally_available_at, + 'originally_available_at': item['originally_available_at'], 'live': row['live'], 'time': row['started'], 'user': row['user']