mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-03-12 04:35:40 -07:00
* Remove Python 2 update modal * Remove Python 2 handling code * Remove backports dependencies * Remove uses of future and __future__ * Fix import * Remove requirements * Update lib folder * Clean up imports and blank lines --------- Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
2458 lines
124 KiB
Python
2458 lines
124 KiB
Python
# -*- 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 plexpy
|
|
from plexpy import common
|
|
from plexpy import database
|
|
from plexpy import datatables
|
|
from plexpy import helpers
|
|
from plexpy import logger
|
|
from plexpy import pmsconnect
|
|
from plexpy import session
|
|
from plexpy import users
|
|
|
|
# Temporarily store update_metadata row ids in memory to prevent rating_key collisions
|
|
_UPDATE_METADATA_IDS = {
|
|
'grandparent_rating_key_ids': set(),
|
|
'parent_rating_key_ids': set(),
|
|
'rating_key_ids': set()
|
|
}
|
|
|
|
|
|
class DataFactory(object):
|
|
"""
|
|
Retrieve and process data from the monitor database
|
|
"""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def get_datatables_history(self, kwargs=None, custom_where=None, grouping=None, include_activity=None):
|
|
data_tables = datatables.DataTables()
|
|
|
|
if custom_where is None:
|
|
custom_where = []
|
|
|
|
if grouping is None:
|
|
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
|
|
|
if include_activity is None:
|
|
include_activity = plexpy.CONFIG.HISTORY_TABLE_ACTIVITY
|
|
|
|
if session.get_session_user_id():
|
|
session_user_id = str(session.get_session_user_id())
|
|
added = False
|
|
|
|
for c_where in custom_where:
|
|
if 'user_id' in c_where[0]:
|
|
if isinstance(c_where[1], list) and session_user_id not in c_where[1]:
|
|
c_where[1].append(session_user_id)
|
|
elif isinstance(c_where[1], str) and c_where[1] != session_user_id:
|
|
c_where[1] = [c_where[1], session_user_id]
|
|
added = True
|
|
break
|
|
|
|
if not added:
|
|
custom_where.append(['session_history.user_id', [session.get_session_user_id()]])
|
|
|
|
group_by = ['session_history.reference_id'] if grouping else ['session_history.id']
|
|
|
|
columns = [
|
|
"session_history.reference_id",
|
|
"session_history.id AS row_id",
|
|
"MAX(started) AS date",
|
|
"MIN(started) AS started",
|
|
"MAX(stopped) AS stopped",
|
|
"SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - \
|
|
SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS play_duration",
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter",
|
|
"session_history.view_offset",
|
|
"session_history.user_id",
|
|
"session_history.user",
|
|
"(CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = '' \
|
|
THEN users.username ELSE users.friendly_name END) AS friendly_name",
|
|
"users.thumb AS user_thumb",
|
|
"users.custom_avatar_url AS custom_thumb",
|
|
"platform",
|
|
"product",
|
|
"player",
|
|
"ip_address",
|
|
"machine_id",
|
|
"location",
|
|
"secure",
|
|
"relayed",
|
|
"session_history.media_type",
|
|
"(CASE WHEN session_history_metadata.live = 1 THEN 'live' ELSE session_history.media_type END) \
|
|
AS media_type_live",
|
|
"session_history_metadata.rating_key",
|
|
"session_history_metadata.parent_rating_key",
|
|
"session_history_metadata.grandparent_rating_key",
|
|
"session_history_metadata.full_title",
|
|
"session_history_metadata.title",
|
|
"session_history_metadata.parent_title",
|
|
"session_history_metadata.grandparent_title",
|
|
"session_history_metadata.original_title",
|
|
"session_history_metadata.year",
|
|
"session_history_metadata.media_index",
|
|
"session_history_metadata.parent_media_index",
|
|
"session_history_metadata.thumb",
|
|
"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",
|
|
"session_history_metadata.guid",
|
|
"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",
|
|
"session_history_metadata.duration",
|
|
"session_history_metadata.marker_credits_first",
|
|
"session_history_metadata.marker_credits_final",
|
|
"session_history_media_info.transcode_decision",
|
|
"COUNT(*) AS group_count",
|
|
"GROUP_CONCAT(session_history.id) AS group_ids",
|
|
"NULL AS state",
|
|
"NULL AS session_key"
|
|
]
|
|
|
|
if include_activity:
|
|
table_name_union = 'sessions'
|
|
# Very hacky way to match the custom where parameters for the unioned table
|
|
custom_where_union = [[c[0].split('.')[-1], c[1]] for c in custom_where]
|
|
group_by_union = ['session_key']
|
|
|
|
columns_union = [
|
|
"NULL AS reference_id",
|
|
"NULL AS row_id",
|
|
"started AS date",
|
|
"started",
|
|
"stopped",
|
|
"SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE (strftime('%s', 'now') - started) END) - \
|
|
SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS play_duration",
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter",
|
|
"view_offset",
|
|
"user_id",
|
|
"user",
|
|
"(CASE WHEN friendly_name IS NULL OR TRIM(friendly_name) = '' \
|
|
THEN user ELSE friendly_name END) AS friendly_name",
|
|
"NULL AS user_thumb",
|
|
"NULL AS custom_thumb",
|
|
"platform",
|
|
"product",
|
|
"player",
|
|
"ip_address",
|
|
"machine_id",
|
|
"location",
|
|
"secure",
|
|
"relayed",
|
|
"media_type",
|
|
"(CASE WHEN live = 1 THEN 'live' ELSE media_type END) AS media_type_live",
|
|
"rating_key",
|
|
"parent_rating_key",
|
|
"grandparent_rating_key",
|
|
"full_title",
|
|
"title",
|
|
"parent_title",
|
|
"grandparent_title",
|
|
"original_title",
|
|
"year",
|
|
"media_index",
|
|
"parent_media_index",
|
|
"thumb",
|
|
"parent_thumb",
|
|
"grandparent_thumb",
|
|
"live",
|
|
"added_at",
|
|
"originally_available_at",
|
|
"guid",
|
|
"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",
|
|
"duration",
|
|
"NULL AS marker_credits_first",
|
|
"NULL AS marker_credits_final",
|
|
"transcode_decision",
|
|
"NULL AS group_count",
|
|
"NULL AS group_ids",
|
|
"state",
|
|
"session_key"
|
|
]
|
|
|
|
else:
|
|
table_name_union = None
|
|
custom_where_union = group_by_union = columns_union = []
|
|
|
|
try:
|
|
query = data_tables.ssp_query(table_name='session_history',
|
|
table_name_union=table_name_union,
|
|
columns=columns,
|
|
columns_union=columns_union,
|
|
custom_where=custom_where,
|
|
custom_where_union=custom_where_union,
|
|
group_by=group_by,
|
|
group_by_union=group_by_union,
|
|
join_types=['LEFT OUTER JOIN',
|
|
'JOIN',
|
|
'JOIN'],
|
|
join_tables=['users',
|
|
'session_history_metadata',
|
|
'session_history_media_info'],
|
|
join_evals=[['session_history.user_id', 'users.user_id'],
|
|
['session_history.id', 'session_history_metadata.id'],
|
|
['session_history.id', 'session_history_media_info.id']],
|
|
kwargs=kwargs)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_history: %s." % e)
|
|
return {'recordsFiltered': 0,
|
|
'recordsTotal': 0,
|
|
'draw': 0,
|
|
'data': [],
|
|
'filter_duration': '0',
|
|
'total_duration': '0'}
|
|
|
|
history = query['result']
|
|
|
|
filter_duration = 0
|
|
total_duration = self.get_total_duration(custom_where=custom_where)
|
|
|
|
watched_percent = {'movie': plexpy.CONFIG.MOVIE_WATCHED_PERCENT,
|
|
'episode': plexpy.CONFIG.TV_WATCHED_PERCENT,
|
|
'track': plexpy.CONFIG.MUSIC_WATCHED_PERCENT,
|
|
'photo': 0,
|
|
'clip': plexpy.CONFIG.TV_WATCHED_PERCENT
|
|
}
|
|
|
|
rows = []
|
|
|
|
users_lookup = {}
|
|
|
|
for item in history:
|
|
if item['state']:
|
|
# Get user thumb from database for current activity
|
|
if not users_lookup:
|
|
# Cache user lookup
|
|
users_lookup = {u['user_id']: u['thumb'] for u in users.Users().get_users()}
|
|
|
|
item['user_thumb'] = users_lookup.get(item['user_id'])
|
|
|
|
filter_duration += helpers.cast_to_int(item['play_duration'])
|
|
|
|
if item['media_type'] == 'episode' and item['parent_thumb']:
|
|
thumb = item['parent_thumb']
|
|
elif item['media_type'] == 'episode':
|
|
thumb = item['grandparent_thumb']
|
|
else:
|
|
thumb = item['thumb']
|
|
|
|
if item['live']:
|
|
item['percent_complete'] = 100
|
|
|
|
base_watched_value = watched_percent[item['media_type']] / 4.0
|
|
|
|
if item['live'] or helpers.check_watched(
|
|
item['media_type'], item['view_offset'], item['duration'],
|
|
item['marker_credits_first'], item['marker_credits_final']
|
|
):
|
|
watched_status = 1
|
|
elif item['percent_complete'] >= base_watched_value * 3.0:
|
|
watched_status = 0.75
|
|
elif item['percent_complete'] >= base_watched_value * 2.0:
|
|
watched_status = 0.50
|
|
elif item['percent_complete'] >= base_watched_value:
|
|
watched_status = 0.25
|
|
else:
|
|
watched_status = 0
|
|
|
|
# Rename Mystery platform names
|
|
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
|
|
|
|
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
|
|
user_thumb = item['custom_thumb']
|
|
elif item['user_thumb']:
|
|
user_thumb = item['user_thumb']
|
|
else:
|
|
user_thumb = common.DEFAULT_USER_THUMB
|
|
|
|
row = {'reference_id': item['reference_id'],
|
|
'row_id': item['row_id'],
|
|
'id': item['row_id'],
|
|
'date': item['date'],
|
|
'started': item['started'],
|
|
'stopped': item['stopped'],
|
|
'duration': item['play_duration'], # Keep for backwards compatibility
|
|
'play_duration': item['play_duration'],
|
|
'paused_counter': item['paused_counter'],
|
|
'user_id': item['user_id'],
|
|
'user': item['user'],
|
|
'friendly_name': item['friendly_name'],
|
|
'user_thumb': user_thumb,
|
|
'platform': platform,
|
|
'product': item['product'],
|
|
'player': item['player'],
|
|
'ip_address': item['ip_address'],
|
|
'live': item['live'],
|
|
'machine_id': item['machine_id'],
|
|
'location': item['location'],
|
|
'secure': item['secure'],
|
|
'relayed': item['relayed'],
|
|
'media_type': item['media_type'],
|
|
'rating_key': item['rating_key'],
|
|
'parent_rating_key': item['parent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'full_title': item['full_title'],
|
|
'title': item['title'],
|
|
'parent_title': item['parent_title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'original_title': item['original_title'],
|
|
'year': item['year'],
|
|
'media_index': item['media_index'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'thumb': thumb,
|
|
'originally_available_at': item['originally_available_at'],
|
|
'guid': item['guid'],
|
|
'transcode_decision': item['transcode_decision'],
|
|
'percent_complete': int(round(item['percent_complete'])),
|
|
'watched_status': watched_status,
|
|
'group_count': item['group_count'],
|
|
'group_ids': item['group_ids'],
|
|
'state': item['state'],
|
|
'session_key': item['session_key']
|
|
}
|
|
|
|
rows.append(row)
|
|
|
|
dict = {'recordsFiltered': query['filteredCount'],
|
|
'recordsTotal': query['totalCount'],
|
|
'data': session.friendly_name_to_username(rows),
|
|
'draw': query['draw'],
|
|
'filter_duration': helpers.human_duration(filter_duration, units='s'),
|
|
'total_duration': helpers.human_duration(total_duration, units='s')
|
|
}
|
|
|
|
return dict
|
|
|
|
def get_home_stats(self, grouping=None, time_range=30, stats_type='plays',
|
|
stats_start=0, stats_count=10, stat_id='', stats_cards=None,
|
|
section_id=None, user_id=None, before=None, after=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
time_range = helpers.cast_to_int(time_range)
|
|
|
|
stats_start = helpers.cast_to_int(stats_start)
|
|
stats_count = helpers.cast_to_int(stats_count)
|
|
if stat_id:
|
|
stats_cards = [stat_id]
|
|
if grouping is None:
|
|
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
|
if stats_cards is None:
|
|
stats_cards = plexpy.CONFIG.HOME_STATS_CARDS
|
|
|
|
where_timeframe = ''
|
|
if before:
|
|
where_timeframe += "AND strftime('%%Y-%%m-%%d', datetime(started, 'unixepoch', 'localtime')) <= '%s' " % before
|
|
if not after:
|
|
timestamp = helpers.YMD_to_timestamp(before) - time_range * 24 * 60 * 60
|
|
where_timeframe += "AND session_history.stopped >= %s " % timestamp
|
|
if after:
|
|
where_timeframe += "AND strftime('%%Y-%%m-%%d', datetime(started, 'unixepoch', 'localtime')) >= '%s' " % after
|
|
if not before:
|
|
timestamp = helpers.YMD_to_timestamp(after) + time_range * 24 * 60 * 60
|
|
where_timeframe += "AND session_history.stopped <= %s " % timestamp
|
|
if not (before and after):
|
|
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
|
where_timeframe += "AND session_history.stopped >= %s" % timestamp
|
|
|
|
where_id = ''
|
|
if section_id:
|
|
where_id += 'AND session_history.section_id = %s ' % section_id
|
|
if user_id:
|
|
where_id += 'AND session_history.user_id = %s ' % user_id
|
|
|
|
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
|
sort_type = 'total_duration' if stats_type == 'duration' else 'total_plays'
|
|
|
|
home_stats = []
|
|
|
|
for stat in stats_cards:
|
|
if stat == 'top_movies':
|
|
top_movies = []
|
|
try:
|
|
query = "SELECT sh.id, shm.full_title, shm.year, sh.rating_key, shm.thumb, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'movie' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.full_title, shm.year " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_movies: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['full_title'],
|
|
'year': item['year'],
|
|
'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'users_watched': '',
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': '',
|
|
'last_play': item['last_watch'],
|
|
'grandparent_thumb': '',
|
|
'thumb': item['thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
top_movies.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Watched Movies',
|
|
'rows': session.mask_session_info(top_movies)})
|
|
|
|
elif stat == 'popular_movies':
|
|
popular_movies = []
|
|
try:
|
|
query = "SELECT sh.id, shm.full_title, shm.year, sh.rating_key, shm.thumb, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"COUNT(DISTINCT sh.user_id) AS users_watched, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) as total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'movie' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.full_title, shm.year " \
|
|
"ORDER BY users_watched DESC, %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: popular_movies: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['full_title'],
|
|
'year': item['year'],
|
|
'users_watched': item['users_watched'],
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': '',
|
|
'last_play': item['last_watch'],
|
|
'total_plays': item['total_plays'],
|
|
'grandparent_thumb': '',
|
|
'thumb': item['thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
popular_movies.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_title': 'Most Popular Movies',
|
|
'rows': session.mask_session_info(popular_movies)})
|
|
|
|
elif stat == 'top_tv':
|
|
top_tv = []
|
|
try:
|
|
query = "SELECT sh.id, shm.grandparent_title, sh.grandparent_rating_key, " \
|
|
"shm.grandparent_thumb, sh.section_id, " \
|
|
"shm.year, sh.rating_key, shm.art, sh.media_type, " \
|
|
"shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'episode' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.grandparent_title " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_tv: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['grandparent_title'],
|
|
'year': item['year'],
|
|
'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'users_watched': '',
|
|
'rating_key': item['rating_key'] if item['live'] else item['grandparent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'last_play': item['last_watch'],
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
top_tv.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Watched TV Shows',
|
|
'rows': session.mask_session_info(top_tv)})
|
|
|
|
elif stat == 'popular_tv':
|
|
popular_tv = []
|
|
try:
|
|
query = "SELECT sh.id, shm.grandparent_title, sh.grandparent_rating_key, " \
|
|
"shm.grandparent_thumb, sh.section_id, " \
|
|
"shm.year, sh.rating_key, shm.art, sh.media_type, " \
|
|
"shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"COUNT(DISTINCT sh.user_id) AS users_watched, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) as total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'episode' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.grandparent_title " \
|
|
"ORDER BY users_watched DESC, %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: popular_tv: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['grandparent_title'],
|
|
'year': item['year'],
|
|
'users_watched': item['users_watched'],
|
|
'rating_key': item['rating_key'] if item['live'] else item['grandparent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'last_play': item['last_watch'],
|
|
'total_plays': item['total_plays'],
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
popular_tv.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_title': 'Most Popular TV Shows',
|
|
'rows': session.mask_session_info(popular_tv)})
|
|
|
|
elif stat == 'top_music':
|
|
top_music = []
|
|
try:
|
|
query = "SELECT sh.id, shm.grandparent_title, shm.original_title, shm.year, " \
|
|
"sh.grandparent_rating_key, shm.grandparent_thumb, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'track' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.original_title, shm.grandparent_title " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_music: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['original_title'] or item['grandparent_title'],
|
|
'year': item['year'],
|
|
'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'users_watched': '',
|
|
'rating_key': item['grandparent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'last_play': item['last_watch'],
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
top_music.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Played Artists',
|
|
'rows': session.mask_session_info(top_music)})
|
|
|
|
elif stat == 'popular_music':
|
|
popular_music = []
|
|
try:
|
|
query = "SELECT sh.id, shm.grandparent_title, shm.original_title, shm.year, " \
|
|
"sh.grandparent_rating_key, shm.grandparent_thumb, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, sh.started, shm.live, shm.guid, " \
|
|
"COUNT(DISTINCT sh.user_id) AS users_watched, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) as total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE session_history.media_type = 'track' %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"GROUP BY shm.original_title, shm.grandparent_title " \
|
|
"ORDER BY users_watched DESC, %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe, where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: popular_music: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
row = {'title': item['original_title'] or item['grandparent_title'],
|
|
'year': item['year'],
|
|
'users_watched': item['users_watched'],
|
|
'rating_key': item['grandparent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'last_play': item['last_watch'],
|
|
'total_plays': item['total_plays'],
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'platform': '',
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
popular_music.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_title': 'Most Popular Artists',
|
|
'rows': session.mask_session_info(popular_music)})
|
|
|
|
elif stat == 'top_libraries':
|
|
top_libraries = []
|
|
try:
|
|
query = "SELECT sh.id, shm.title, shm.grandparent_title, shm.full_title, shm.year, " \
|
|
"shm.media_index, shm.parent_media_index, " \
|
|
"sh.rating_key, shm.grandparent_rating_key, shm.thumb, shm.grandparent_thumb, " \
|
|
"sh.user, sh.user_id, sh.player, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, shm.live, shm.guid, " \
|
|
"ls.section_name, ls.section_type, " \
|
|
"ls.thumb AS library_thumb, ls.custom_thumb_url AS custom_thumb, " \
|
|
"ls.art AS library_art, ls.custom_art_url AS custom_art, " \
|
|
"sh.started, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"LEFT OUTER JOIN (SELECT * FROM library_sections WHERE deleted_section = 0) " \
|
|
" AS ls ON sh.section_id = ls.section_id " \
|
|
"GROUP BY sh.section_id " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe[4:], where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_libraries: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
if item['custom_thumb'] and item['custom_thumb'] != item['library_thumb']:
|
|
library_thumb = item['custom_thumb']
|
|
elif item['library_thumb']:
|
|
library_thumb = item['library_thumb']
|
|
else:
|
|
library_thumb = common.DEFAULT_COVER_THUMB
|
|
|
|
if item['custom_art'] and item['custom_art'] != item['library_art']:
|
|
library_art = item['custom_art']
|
|
elif item['library_art'] == common.DEFAULT_LIVE_TV_ART_FULL:
|
|
library_art = common.DEFAULT_LIVE_TV_ART
|
|
else:
|
|
library_art = item['library_art']
|
|
|
|
if not item['grandparent_thumb'] or item['grandparent_thumb'] == '':
|
|
thumb = item['thumb']
|
|
else:
|
|
thumb = item['grandparent_thumb']
|
|
|
|
row = {
|
|
'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'section_type': item['section_type'],
|
|
'section_name': item['section_name'],
|
|
'section_id': item['section_id'],
|
|
'last_play': item['last_watch'],
|
|
'library_thumb': library_thumb,
|
|
'library_art': library_art,
|
|
'thumb': thumb,
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'users_watched': '',
|
|
'platform': '',
|
|
'title': item['full_title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'grandchild_title': item['title'],
|
|
'year': item['year'],
|
|
'media_index': item['media_index'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
top_libraries.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Active Libraries',
|
|
'rows': session.mask_session_info(top_libraries)})
|
|
|
|
elif stat == 'top_users':
|
|
top_users = []
|
|
try:
|
|
query = "SELECT sh.id, shm.title, shm.grandparent_title, shm.full_title, shm.year, " \
|
|
"shm.media_index, shm.parent_media_index, " \
|
|
"sh.rating_key, shm.grandparent_rating_key, shm.thumb, shm.grandparent_thumb, " \
|
|
"sh.user, sh.user_id, sh.player, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, shm.live, shm.guid, " \
|
|
"u.thumb AS user_thumb, u.custom_avatar_url AS custom_thumb, " \
|
|
"sh.started, " \
|
|
"(CASE WHEN u.friendly_name IS NULL OR TRIM(u.friendly_name) = ''" \
|
|
" THEN u.username ELSE u.friendly_name END) " \
|
|
" AS friendly_name, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"LEFT OUTER JOIN users AS u ON sh.user_id = u.user_id " \
|
|
"GROUP BY sh.user_id " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe[4:], where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_users: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
|
|
user_thumb = item['custom_thumb']
|
|
elif item['user_thumb']:
|
|
user_thumb = item['user_thumb']
|
|
else:
|
|
user_thumb = common.DEFAULT_USER_THUMB
|
|
|
|
if not item['grandparent_thumb'] or item['grandparent_thumb'] == '':
|
|
thumb = item['thumb']
|
|
else:
|
|
thumb = item['grandparent_thumb']
|
|
|
|
row = {'user': item['user'],
|
|
'user_id': item['user_id'],
|
|
'friendly_name': item['friendly_name'],
|
|
'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'last_play': item['last_watch'],
|
|
'user_thumb': user_thumb,
|
|
'thumb': thumb,
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'users_watched': '',
|
|
'platform': '',
|
|
'title': item['full_title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'grandchild_title': item['title'],
|
|
'year': item['year'],
|
|
'media_index': item['media_index'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
top_users.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Active Users',
|
|
'rows': session.mask_session_info(top_users)})
|
|
|
|
elif stat == 'top_platforms':
|
|
top_platform = []
|
|
|
|
try:
|
|
query = "SELECT sh.platform, sh.started, " \
|
|
"MAX(sh.started) AS last_watch, COUNT(sh.id) AS total_plays, SUM(sh.d) AS total_duration " \
|
|
"FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - " \
|
|
" (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) " \
|
|
" AS d " \
|
|
" FROM session_history " \
|
|
" WHERE %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"GROUP BY sh.platform " \
|
|
"ORDER BY %s DESC, sh.started DESC " \
|
|
"LIMIT %s OFFSET %s " % (where_timeframe[4:], where_id, group_by, sort_type, stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: top_platforms: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
# Rename Mystery platform names
|
|
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
|
|
platform_name = next((v for k, v in common.PLATFORM_NAMES.items() if k in platform.lower()), 'default')
|
|
|
|
row = {'total_plays': item['total_plays'],
|
|
'total_duration': item['total_duration'],
|
|
'last_play': item['last_watch'],
|
|
'platform': platform,
|
|
'platform_name': platform_name,
|
|
'title': '',
|
|
'thumb': '',
|
|
'grandparent_thumb': '',
|
|
'art': '',
|
|
'users_watched': '',
|
|
'rating_key': '',
|
|
'grandparent_rating_key': '',
|
|
'user': '',
|
|
'friendly_name': '',
|
|
'row_id': ''
|
|
}
|
|
top_platform.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_type': sort_type,
|
|
'stat_title': 'Most Active Platforms',
|
|
'rows': session.mask_session_info(top_platform, mask_metadata=False)})
|
|
|
|
elif stat == 'last_watched':
|
|
|
|
movie_watched_percent = plexpy.CONFIG.MOVIE_WATCHED_PERCENT
|
|
tv_watched_percent = plexpy.CONFIG.TV_WATCHED_PERCENT
|
|
|
|
if plexpy.CONFIG.WATCHED_MARKER == 1:
|
|
watched_threshold = (
|
|
"(CASE WHEN shm.marker_credits_final IS NULL "
|
|
"THEN sh._duration * (CASE WHEN sh.media_type = 'movie' THEN %d ELSE %d END) / 100.0 "
|
|
"ELSE shm.marker_credits_final END) "
|
|
"AS watched_threshold"
|
|
) % (movie_watched_percent, tv_watched_percent)
|
|
watched_where = "_view_offset >= watched_threshold"
|
|
elif plexpy.CONFIG.WATCHED_MARKER == 2:
|
|
watched_threshold = (
|
|
"(CASE WHEN shm.marker_credits_first IS NULL "
|
|
"THEN sh._duration * (CASE WHEN sh.media_type = 'movie' THEN %d ELSE %d END) / 100.0 "
|
|
"ELSE shm.marker_credits_first END) "
|
|
"AS watched_threshold"
|
|
) % (movie_watched_percent, tv_watched_percent)
|
|
watched_where = "_view_offset >= watched_threshold"
|
|
elif plexpy.CONFIG.WATCHED_MARKER == 3:
|
|
watched_threshold = (
|
|
"MIN("
|
|
"(CASE WHEN shm.marker_credits_first IS NULL "
|
|
"THEN sh._duration * (CASE WHEN sh.media_type = 'movie' THEN %d ELSE %d END) / 100.0 "
|
|
"ELSE shm.marker_credits_first END), "
|
|
"sh._duration * (CASE WHEN sh.media_type = 'movie' THEN %d ELSE %d END) / 100.0) "
|
|
"AS watched_threshold"
|
|
) % (movie_watched_percent, tv_watched_percent, movie_watched_percent, tv_watched_percent)
|
|
watched_where = "_view_offset >= watched_threshold"
|
|
else:
|
|
watched_threshold = "NULL AS watched_threshold"
|
|
watched_where = (
|
|
"sh.media_type == 'movie' AND percent_complete >= %d "
|
|
"OR sh.media_type == 'episode' AND percent_complete >= %d"
|
|
) % (movie_watched_percent, tv_watched_percent)
|
|
|
|
last_watched = []
|
|
try:
|
|
query = "SELECT sh.id, shm.title, shm.grandparent_title, shm.full_title, shm.year, " \
|
|
"shm.media_index, shm.parent_media_index, " \
|
|
"sh.rating_key, shm.grandparent_rating_key, shm.thumb, shm.grandparent_thumb, " \
|
|
"sh.user, sh.user_id, u.custom_avatar_url as user_thumb, sh.player, sh.section_id, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, shm.live, shm.guid, " \
|
|
"(CASE WHEN u.friendly_name IS NULL OR TRIM(u.friendly_name) = ''" \
|
|
" THEN u.username ELSE u.friendly_name END) " \
|
|
" AS friendly_name, " \
|
|
"MAX(sh.started) AS last_watch, sh._view_offset, sh._duration, " \
|
|
"(sh._view_offset / sh._duration * 100) AS percent_complete, " \
|
|
"%s " \
|
|
"FROM (SELECT *, MAX(session_history.id), " \
|
|
" (CASE WHEN view_offset IS NULL THEN 0.1 ELSE view_offset * 1.0 END) AS _view_offset, " \
|
|
" (CASE WHEN duration IS NULL THEN 1.0 ELSE duration * 1.0 END) AS _duration " \
|
|
" FROM session_history " \
|
|
" JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
" WHERE (session_history.media_type = 'movie' " \
|
|
" OR session_history.media_type = 'episode') %s %s " \
|
|
" GROUP BY %s) AS sh " \
|
|
"JOIN session_history_metadata AS shm ON shm.id = sh.id " \
|
|
"LEFT OUTER JOIN users AS u ON sh.user_id = u.user_id " \
|
|
"WHERE %s " \
|
|
"GROUP BY sh.id " \
|
|
"ORDER BY last_watch DESC " \
|
|
"LIMIT %s OFFSET %s" % (watched_threshold,
|
|
where_timeframe, where_id, group_by, watched_where,
|
|
stats_count, stats_start)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: last_watched: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
if not item['grandparent_thumb'] or item['grandparent_thumb'] == '':
|
|
thumb = item['thumb']
|
|
else:
|
|
thumb = item['grandparent_thumb']
|
|
|
|
row = {'row_id': item['id'],
|
|
'user': item['user'],
|
|
'friendly_name': item['friendly_name'],
|
|
'user_id': item['user_id'],
|
|
'user_thumb': item['user_thumb'],
|
|
'title': item['full_title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'grandchild_title': item['title'],
|
|
'year': item['year'],
|
|
'media_index': item['media_index'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'thumb': thumb,
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'section_id': item['section_id'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'last_watch': item['last_watch'],
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'player': item['player']
|
|
}
|
|
last_watched.append(row)
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_title': 'Recently Watched',
|
|
'rows': session.mask_session_info(last_watched)})
|
|
|
|
elif stat == 'most_concurrent':
|
|
|
|
def calc_most_concurrent(title, result):
|
|
'''
|
|
Function to calculate most concurrent streams
|
|
Input: Stat title, SQLite query result
|
|
Output: Dict {title, count, started, stopped}
|
|
'''
|
|
times = []
|
|
for item in result:
|
|
times.append({'time': str(item['started']) + 'B', 'count': 1})
|
|
times.append({'time': str(item['stopped']) + 'A', 'count': -1})
|
|
times = sorted(times, key=lambda k: k['time'])
|
|
|
|
count = 0
|
|
last_count = 0
|
|
last_start = ''
|
|
concurrent = {'title': title,
|
|
'count': 0,
|
|
'started': None,
|
|
'stopped': None
|
|
}
|
|
|
|
for d in times:
|
|
if d['count'] == 1:
|
|
count += d['count']
|
|
if count >= last_count:
|
|
last_start = d['time']
|
|
else:
|
|
if count >= last_count:
|
|
last_count = count
|
|
concurrent['count'] = count
|
|
concurrent['started'] = last_start[:-1]
|
|
concurrent['stopped'] = d['time'][:-1]
|
|
count += d['count']
|
|
|
|
return concurrent
|
|
|
|
most_concurrent = []
|
|
|
|
try:
|
|
base_query = "SELECT sh.started, sh.stopped " \
|
|
"FROM session_history AS sh " \
|
|
"JOIN session_history_media_info AS shmi ON sh.id = shmi.id " \
|
|
"WHERE %s " % where_timeframe[4:].replace('session_history.', 'sh.')
|
|
|
|
title = 'Concurrent Streams'
|
|
query = base_query
|
|
result = monitor_db.select(query)
|
|
if result:
|
|
most_concurrent.append(calc_most_concurrent(title, result))
|
|
|
|
title = 'Concurrent Transcodes'
|
|
query = base_query \
|
|
+ "AND shmi.transcode_decision = 'transcode' "
|
|
result = monitor_db.select(query)
|
|
if result:
|
|
most_concurrent.append(calc_most_concurrent(title, result))
|
|
|
|
title = 'Concurrent Direct Streams'
|
|
query = base_query \
|
|
+ "AND shmi.transcode_decision = 'copy' "
|
|
result = monitor_db.select(query)
|
|
if result:
|
|
most_concurrent.append(calc_most_concurrent(title, result))
|
|
|
|
title = 'Concurrent Direct Plays'
|
|
query = base_query \
|
|
+ "AND shmi.transcode_decision = 'direct play' "
|
|
result = monitor_db.select(query)
|
|
if result:
|
|
most_concurrent.append(calc_most_concurrent(title, result))
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_home_stats: most_concurrent: %s." % e)
|
|
return None
|
|
|
|
home_stats.append({'stat_id': stat,
|
|
'stat_title': 'Most Concurrent Streams',
|
|
'rows': most_concurrent})
|
|
|
|
if stat_id and home_stats:
|
|
return home_stats[0]
|
|
return home_stats
|
|
|
|
def get_library_stats(self, library_cards=[]):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if session.get_session_shared_libraries():
|
|
library_cards = session.get_session_shared_libraries()
|
|
|
|
library_stats = []
|
|
|
|
try:
|
|
query = "SELECT ls.id, ls.section_id, ls.section_name, ls.section_type, ls.thumb AS library_thumb, " \
|
|
"ls.custom_thumb_url AS custom_thumb, ls.art AS library_art, ls.custom_art_url AS custom_art, " \
|
|
"ls.count, ls.parent_count, ls.child_count, " \
|
|
"sh.id, shm.title, shm.grandparent_title, shm.full_title, shm.year, " \
|
|
"shm.media_index, shm.parent_media_index, " \
|
|
"sh.rating_key, shm.grandparent_rating_key, shm.thumb, shm.grandparent_thumb, " \
|
|
"sh.user, sh.user_id, sh.player, " \
|
|
"shm.art, sh.media_type, shm.content_rating, shm.labels, shm.live, shm.guid, " \
|
|
"MAX(sh.started) AS last_watch " \
|
|
"FROM library_sections AS ls " \
|
|
"LEFT OUTER JOIN session_history AS sh ON ls.section_id = sh.section_id " \
|
|
"LEFT OUTER JOIN session_history_metadata AS shm ON sh.id = shm.id " \
|
|
"WHERE ls.section_id IN (%s) AND ls.deleted_section = 0 " \
|
|
"GROUP BY ls.id " \
|
|
"ORDER BY ls.section_type, ls.count DESC, ls.parent_count DESC, ls.child_count DESC " % ",".join(library_cards)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_library_stats: %s." % e)
|
|
return None
|
|
|
|
for item in result:
|
|
if item['custom_thumb'] and item['custom_thumb'] != item['library_thumb']:
|
|
library_thumb = item['custom_thumb']
|
|
elif item['library_thumb']:
|
|
library_thumb = item['library_thumb']
|
|
else:
|
|
library_thumb = common.DEFAULT_COVER_THUMB
|
|
|
|
if item['custom_art'] and item['custom_art'] != item['library_art']:
|
|
library_art = item['custom_art']
|
|
else:
|
|
library_art = item['library_art']
|
|
|
|
if not item['grandparent_thumb'] or item['grandparent_thumb'] == '':
|
|
thumb = item['thumb']
|
|
else:
|
|
thumb = item['grandparent_thumb']
|
|
|
|
library = {'section_id': item['section_id'],
|
|
'section_name': item['section_name'],
|
|
'section_type': item['section_type'],
|
|
'library_thumb': library_thumb,
|
|
'library_art': library_art,
|
|
'count': item['count'],
|
|
'child_count': item['parent_count'],
|
|
'grandchild_count': item['child_count'],
|
|
'thumb': thumb or '',
|
|
'grandparent_thumb': item['grandparent_thumb'] or '',
|
|
'art': item['art'] or '',
|
|
'title': item['full_title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'grandchild_title': item['title'],
|
|
'year': item['year'],
|
|
'media_index': item['media_index'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'rating_key': item['rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'media_type': item['media_type'],
|
|
'content_rating': item['content_rating'],
|
|
'labels': item['labels'].split(';') if item['labels'] else (),
|
|
'live': item['live'],
|
|
'guid': item['guid'],
|
|
'row_id': item['id']
|
|
}
|
|
library_stats.append(library)
|
|
|
|
library_stats = session.mask_session_info(library_stats)
|
|
library_stats = helpers.group_by_keys(library_stats, 'section_type')
|
|
|
|
return library_stats
|
|
|
|
def get_watch_time_stats(self, rating_key=None, guid=None, media_type=None, grouping=None, query_days=None):
|
|
if rating_key is None and guid is None:
|
|
return []
|
|
|
|
if grouping is None:
|
|
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
|
|
|
if query_days and query_days is not None:
|
|
query_days = map(helpers.cast_to_int, str(query_days).split(','))
|
|
else:
|
|
query_days = [1, 7, 30, 0]
|
|
|
|
timestamp = helpers.timestamp()
|
|
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
item_watch_time_stats = []
|
|
|
|
section_ids = set()
|
|
|
|
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
|
|
|
if media_type in ('collection', 'playlist'):
|
|
pms_connect = pmsconnect.PmsConnect()
|
|
result = pms_connect.get_item_children(rating_key=rating_key, media_type=media_type)
|
|
rating_keys = [child['rating_key'] for child in result['children_list']]
|
|
else:
|
|
rating_keys = [rating_key]
|
|
|
|
rating_keys_arg = ','.join(['?'] * len(rating_keys))
|
|
|
|
for days in query_days:
|
|
timestamp_query = timestamp - days * 24 * 60 * 60
|
|
|
|
try:
|
|
if days > 0:
|
|
if str(rating_key).isdigit():
|
|
query = "SELECT (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"WHERE stopped >= ? " \
|
|
"AND (session_history.grandparent_rating_key IN (%s) " \
|
|
"OR session_history.parent_rating_key IN (%s) " \
|
|
"OR session_history.rating_key IN (%s))" % (
|
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
|
)
|
|
|
|
result = monitor_db.select(query, args=[timestamp_query] + rating_keys * 3)
|
|
elif guid:
|
|
query = "SELECT (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"WHERE stopped >= ? " \
|
|
"AND session_history_metadata.guid = ? " % group_by
|
|
|
|
result = monitor_db.select(query, args=[timestamp_query, guid])
|
|
else:
|
|
result = []
|
|
else:
|
|
if str(rating_key).isdigit():
|
|
query = "SELECT (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"WHERE (session_history.grandparent_rating_key IN (%s) " \
|
|
"OR session_history.parent_rating_key IN (%s) " \
|
|
"OR session_history.rating_key IN (%s))" % (
|
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
|
)
|
|
|
|
result = monitor_db.select(query, args=rating_keys * 3)
|
|
elif guid:
|
|
query = "SELECT (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"WHERE session_history_metadata.guid = ? " % group_by
|
|
|
|
result = monitor_db.select(query, args=[guid])
|
|
else:
|
|
result = []
|
|
except Exception as e:
|
|
logger.warn("Tautulli Libraries :: Unable to execute database query for get_watch_time_stats: %s." % e)
|
|
result = []
|
|
|
|
for item in result:
|
|
section_ids.add(item['section_id'])
|
|
|
|
if item['total_time']:
|
|
total_time = item['total_time']
|
|
total_plays = item['total_plays']
|
|
else:
|
|
total_time = 0
|
|
total_plays = 0
|
|
|
|
row = {'query_days': days,
|
|
'total_time': total_time,
|
|
'total_plays': total_plays
|
|
}
|
|
|
|
item_watch_time_stats.append(row)
|
|
|
|
if any(not session.allow_session_library(section_id) for section_id in section_ids):
|
|
return []
|
|
|
|
return item_watch_time_stats
|
|
|
|
def get_user_stats(self, rating_key=None, guid=None, media_type=None, grouping=None):
|
|
if grouping is None:
|
|
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
|
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
user_stats = []
|
|
|
|
section_ids = set()
|
|
|
|
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
|
|
|
if media_type in ('collection', 'playlist'):
|
|
pms_connect = pmsconnect.PmsConnect()
|
|
result = pms_connect.get_item_children(rating_key=rating_key, media_type=media_type)
|
|
rating_keys = [child['rating_key'] for child in result['children_list']]
|
|
else:
|
|
rating_keys = [rating_key]
|
|
|
|
rating_keys_arg = ','.join(['?'] * len(rating_keys))
|
|
|
|
try:
|
|
if str(rating_key).isdigit():
|
|
query = "SELECT (CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = '' " \
|
|
"THEN users.username ELSE users.friendly_name END) AS friendly_name, " \
|
|
"users.user_id, users.username, users.thumb, users.custom_avatar_url AS custom_thumb, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"JOIN users ON users.user_id = session_history.user_id " \
|
|
"WHERE (session_history.grandparent_rating_key IN (%s) " \
|
|
"OR session_history.parent_rating_key IN (%s) " \
|
|
"OR session_history.rating_key IN (%s)) " \
|
|
"GROUP BY users.user_id " \
|
|
"ORDER BY total_plays DESC, total_time DESC" % (
|
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
|
)
|
|
|
|
result = monitor_db.select(query, args=rating_keys * 3)
|
|
elif guid:
|
|
query = "SELECT (CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = '' " \
|
|
"THEN users.username ELSE users.friendly_name END) AS friendly_name, " \
|
|
"users.user_id, users.username, users.thumb, users.custom_avatar_url AS custom_thumb, " \
|
|
"COUNT(DISTINCT %s) AS total_plays, (SUM(stopped - started) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END)) AS total_time, " \
|
|
"section_id " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"JOIN users ON users.user_id = session_history.user_id " \
|
|
"WHERE session_history_metadata.guid = ? " \
|
|
"GROUP BY users.user_id " \
|
|
"ORDER BY total_plays DESC, total_time DESC" % group_by
|
|
|
|
result = monitor_db.select(query, args=[guid])
|
|
else:
|
|
result = []
|
|
except Exception as e:
|
|
logger.warn("Tautulli Libraries :: Unable to execute database query for get_user_stats: %s." % e)
|
|
result = []
|
|
|
|
for item in result:
|
|
section_ids.add(item['section_id'])
|
|
|
|
if item['custom_thumb'] and item['custom_thumb'] != item['thumb']:
|
|
user_thumb = item['custom_thumb']
|
|
elif item['thumb']:
|
|
user_thumb = item['thumb']
|
|
else:
|
|
user_thumb = common.DEFAULT_USER_THUMB
|
|
|
|
row = {'friendly_name': item['friendly_name'],
|
|
'user_id': item['user_id'],
|
|
'user_thumb': user_thumb,
|
|
'username': item['username'],
|
|
'total_plays': item['total_plays'],
|
|
'total_time': item['total_time']
|
|
}
|
|
user_stats.append(row)
|
|
|
|
if any(not session.allow_session_library(section_id) for section_id in section_ids):
|
|
return []
|
|
|
|
return session.mask_session_info(user_stats, mask_metadata=False)
|
|
|
|
def get_stream_details(self, row_id=None, session_key=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
user_cond = ''
|
|
table = 'session_history' if row_id else 'sessions'
|
|
if session.get_session_user_id():
|
|
user_cond = "AND %s.user_id = %s " % (table, session.get_session_user_id())
|
|
|
|
if row_id:
|
|
query = "SELECT bitrate, video_full_resolution, " \
|
|
"optimized_version, optimized_version_profile, optimized_version_title, " \
|
|
"synced_version, synced_version_profile, " \
|
|
"container, video_codec, video_bitrate, video_width, video_height, video_framerate, " \
|
|
"video_dynamic_range, aspect_ratio, " \
|
|
"audio_codec, audio_bitrate, audio_channels, audio_language, audio_language_code, " \
|
|
"subtitle_codec, subtitle_forced, subtitle_language, " \
|
|
"stream_bitrate, stream_video_full_resolution, quality_profile, stream_container_decision, stream_container, " \
|
|
"stream_video_decision, stream_video_codec, stream_video_bitrate, stream_video_width, stream_video_height, " \
|
|
"stream_video_framerate, stream_video_dynamic_range, " \
|
|
"stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, " \
|
|
"stream_audio_language, stream_audio_language_code, " \
|
|
"subtitles, stream_subtitle_decision, stream_subtitle_codec, stream_subtitle_forced, stream_subtitle_language, " \
|
|
"transcode_hw_decoding, transcode_hw_encoding, " \
|
|
"video_decision, audio_decision, transcode_decision, width, height, container, " \
|
|
"transcode_container, transcode_video_codec, transcode_audio_codec, transcode_audio_channels, " \
|
|
"transcode_width, transcode_height, " \
|
|
"session_history_metadata.media_type, title, grandparent_title, original_title " \
|
|
"FROM session_history_media_info " \
|
|
"JOIN session_history ON session_history_media_info.id = session_history.id " \
|
|
"JOIN session_history_metadata ON session_history_media_info.id = session_history_metadata.id " \
|
|
"WHERE session_history_media_info.id = ? %s" % user_cond
|
|
result = monitor_db.select(query, args=[row_id])
|
|
elif session_key:
|
|
query = "SELECT bitrate, video_full_resolution, " \
|
|
"optimized_version, optimized_version_profile, optimized_version_title, " \
|
|
"synced_version, synced_version_profile, " \
|
|
"container, video_codec, video_bitrate, video_width, video_height, video_framerate, " \
|
|
"video_dynamic_range, aspect_ratio, " \
|
|
"audio_codec, audio_bitrate, audio_channels, audio_language, audio_language_code, " \
|
|
"subtitle_codec, subtitle_forced, subtitle_language, " \
|
|
"stream_bitrate, stream_video_full_resolution, quality_profile, stream_container_decision, stream_container, " \
|
|
"stream_video_decision, stream_video_codec, stream_video_bitrate, stream_video_width, stream_video_height, " \
|
|
"stream_video_framerate, stream_video_dynamic_range, " \
|
|
"stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, " \
|
|
"stream_audio_language, stream_audio_language_code, " \
|
|
"subtitles, stream_subtitle_decision, stream_subtitle_codec, stream_subtitle_forced, stream_subtitle_language, " \
|
|
"transcode_hw_decoding, transcode_hw_encoding, " \
|
|
"video_decision, audio_decision, transcode_decision, width, height, container, " \
|
|
"transcode_container, transcode_video_codec, transcode_audio_codec, transcode_audio_channels, " \
|
|
"transcode_width, transcode_height, " \
|
|
"media_type, title, grandparent_title, original_title " \
|
|
"FROM sessions " \
|
|
"WHERE session_key = ? %s" % user_cond
|
|
result = monitor_db.select(query, args=[session_key])
|
|
else:
|
|
return None
|
|
|
|
stream_output = {}
|
|
|
|
for item in result:
|
|
pre_tautulli = 0
|
|
|
|
# For backwards compatibility. Pick one new Tautulli key to check and override with old values.
|
|
if not item['stream_container']:
|
|
item['stream_video_full_resolution'] = item['video_full_resolution']
|
|
item['stream_container'] = item['transcode_container'] or item['container']
|
|
item['stream_video_decision'] = item['video_decision']
|
|
item['stream_video_codec'] = item['transcode_video_codec'] or item['video_codec']
|
|
item['stream_video_width'] = item['transcode_width'] or item['width']
|
|
item['stream_video_height'] = item['transcode_height'] or item['height']
|
|
item['stream_audio_decision'] = item['audio_decision']
|
|
item['stream_audio_codec'] = item['transcode_audio_codec'] or item['audio_codec']
|
|
item['stream_audio_channels'] = item['transcode_audio_channels'] or item['audio_channels']
|
|
item['video_width'] = item['width']
|
|
item['video_height'] = item['height']
|
|
pre_tautulli = 1
|
|
|
|
stream_output = {'bitrate': item['bitrate'],
|
|
'video_full_resolution': item['video_full_resolution'],
|
|
'optimized_version': item['optimized_version'],
|
|
'optimized_version_profile': item['optimized_version_profile'],
|
|
'optimized_version_title': item['optimized_version_title'],
|
|
'synced_version': item['synced_version'],
|
|
'synced_version_profile': item['synced_version_profile'],
|
|
'container': item['container'],
|
|
'video_codec': item['video_codec'],
|
|
'video_bitrate': item['video_bitrate'],
|
|
'video_width': item['video_width'],
|
|
'video_height': item['video_height'],
|
|
'video_framerate': item['video_framerate'],
|
|
'video_dynamic_range': item['video_dynamic_range'],
|
|
'aspect_ratio': item['aspect_ratio'],
|
|
'audio_codec': item['audio_codec'],
|
|
'audio_bitrate': item['audio_bitrate'],
|
|
'audio_channels': item['audio_channels'],
|
|
'audio_language': item['audio_language'],
|
|
'audio_language_code': item['audio_language_code'],
|
|
'subtitle_codec': item['subtitle_codec'],
|
|
'subtitle_forced': item['subtitle_forced'],
|
|
'subtitle_language': item['subtitle_language'],
|
|
'stream_bitrate': item['stream_bitrate'],
|
|
'stream_video_full_resolution': item['stream_video_full_resolution'],
|
|
'quality_profile': item['quality_profile'],
|
|
'stream_container_decision': item['stream_container_decision'],
|
|
'stream_container': item['stream_container'],
|
|
'stream_video_decision': item['stream_video_decision'],
|
|
'stream_video_codec': item['stream_video_codec'],
|
|
'stream_video_bitrate': item['stream_video_bitrate'],
|
|
'stream_video_width': item['stream_video_width'],
|
|
'stream_video_height': item['stream_video_height'],
|
|
'stream_video_framerate': item['stream_video_framerate'],
|
|
'stream_video_dynamic_range': item['stream_video_dynamic_range'],
|
|
'stream_audio_decision': item['stream_audio_decision'],
|
|
'stream_audio_codec': item['stream_audio_codec'],
|
|
'stream_audio_bitrate': item['stream_audio_bitrate'],
|
|
'stream_audio_channels': item['stream_audio_channels'],
|
|
'stream_audio_language': item['stream_audio_language'],
|
|
'stream_audio_language_code': item['stream_audio_language_code'],
|
|
'subtitles': item['subtitles'],
|
|
'stream_subtitle_decision': item['stream_subtitle_decision'],
|
|
'stream_subtitle_codec': item['stream_subtitle_codec'],
|
|
'stream_subtitle_forced': item['stream_subtitle_forced'],
|
|
'stream_subtitle_language': item['stream_subtitle_language'],
|
|
'transcode_hw_decoding': item['transcode_hw_decoding'],
|
|
'transcode_hw_encoding': item['transcode_hw_encoding'],
|
|
'video_decision': item['video_decision'],
|
|
'audio_decision': item['audio_decision'],
|
|
'media_type': item['media_type'],
|
|
'title': item['title'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'original_title': item['original_title'],
|
|
'current_session': 1 if session_key else 0,
|
|
'pre_tautulli': pre_tautulli
|
|
}
|
|
|
|
stream_output = {k: v or '' for k, v in stream_output.items()}
|
|
return stream_output
|
|
|
|
def get_metadata_details(self, rating_key='', guid=''):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if rating_key or guid:
|
|
if guid:
|
|
where = "session_history_metadata.guid LIKE ?"
|
|
args = [guid.split('?')[0] + '%'] # SQLite LIKE wildcard
|
|
else:
|
|
where = "session_history_metadata.rating_key = ?"
|
|
args = [rating_key]
|
|
|
|
query = "SELECT session_history.section_id, session_history_metadata.id, " \
|
|
"session_history_metadata.rating_key, session_history_metadata.parent_rating_key, " \
|
|
"session_history_metadata.grandparent_rating_key, session_history_metadata.title, " \
|
|
"session_history_metadata.parent_title, session_history_metadata.grandparent_title, " \
|
|
"session_history_metadata.original_title, session_history_metadata.full_title, " \
|
|
"library_sections.section_name, " \
|
|
"session_history_metadata.media_index, session_history_metadata.parent_media_index, " \
|
|
"session_history_metadata.thumb, " \
|
|
"session_history_metadata.parent_thumb, session_history_metadata.grandparent_thumb, " \
|
|
"session_history_metadata.art, session_history_metadata.media_type, session_history_metadata.year, " \
|
|
"session_history_metadata.originally_available_at, session_history_metadata.added_at, " \
|
|
"session_history_metadata.updated_at, session_history_metadata.last_viewed_at, " \
|
|
"session_history_metadata.content_rating, session_history_metadata.summary, " \
|
|
"session_history_metadata.tagline, session_history_metadata.rating, session_history_metadata.duration, " \
|
|
"session_history_metadata.guid, session_history_metadata.directors, session_history_metadata.writers, " \
|
|
"session_history_metadata.actors, session_history_metadata.genres, session_history_metadata.studio, " \
|
|
"session_history_metadata.labels, " \
|
|
"session_history_media_info.container, session_history_media_info.bitrate, " \
|
|
"session_history_media_info.video_codec, session_history_media_info.video_resolution, " \
|
|
"session_history_media_info.video_full_resolution, " \
|
|
"session_history_media_info.video_framerate, session_history_media_info.audio_codec, " \
|
|
"session_history_media_info.audio_channels, session_history_metadata.live, " \
|
|
"session_history_metadata.channel_call_sign, session_history_metadata.channel_id, " \
|
|
"session_history_metadata.channel_identifier, session_history_metadata.channel_title, " \
|
|
"session_history_metadata.channel_thumb, session_history_metadata.channel_vcn " \
|
|
"FROM session_history_metadata " \
|
|
"JOIN library_sections ON session_history.section_id = library_sections.section_id " \
|
|
"JOIN session_history ON session_history_metadata.id = session_history.id " \
|
|
"JOIN session_history_media_info ON session_history_metadata.id = session_history_media_info.id " \
|
|
"WHERE %s " \
|
|
"ORDER BY session_history_metadata.id DESC " \
|
|
"LIMIT 1" % where
|
|
result = monitor_db.select(query=query, args=args)
|
|
else:
|
|
result = []
|
|
|
|
metadata_list = []
|
|
|
|
for item in result:
|
|
directors = item['directors'].split(';') if item['directors'] else []
|
|
writers = item['writers'].split(';') if item['writers'] else []
|
|
actors = item['actors'].split(';') if item['actors'] else []
|
|
genres = item['genres'].split(';') if item['genres'] else []
|
|
labels = item['labels'].split(';') if item['labels'] else []
|
|
|
|
media_info = [{'container': item['container'],
|
|
'bitrate': item['bitrate'],
|
|
'video_codec': item['video_codec'],
|
|
'video_resolution': item['video_resolution'],
|
|
'video_full_resolution': item['video_full_resolution'],
|
|
'video_framerate': item['video_framerate'],
|
|
'audio_codec': item['audio_codec'],
|
|
'audio_channels': item['audio_channels'],
|
|
'channel_call_sign': item['channel_call_sign'],
|
|
'channel_id': item['channel_id'],
|
|
'channel_identifier': item['channel_identifier'],
|
|
'channel_title': item['channel_title'],
|
|
'channel_thumb': item['channel_thumb'],
|
|
'channel_vcn': item['channel_vcn']
|
|
}]
|
|
|
|
metadata = {'media_type': item['media_type'],
|
|
'rating_key': item['rating_key'],
|
|
'parent_rating_key': item['parent_rating_key'],
|
|
'grandparent_rating_key': item['grandparent_rating_key'],
|
|
'grandparent_title': item['grandparent_title'],
|
|
'original_title': item['original_title'],
|
|
'parent_media_index': item['parent_media_index'],
|
|
'parent_title': item['parent_title'],
|
|
'media_index': item['media_index'],
|
|
'studio': item['studio'],
|
|
'title': item['title'],
|
|
'full_title': item['full_title'],
|
|
'content_rating': item['content_rating'],
|
|
'summary': item['summary'],
|
|
'tagline': item['tagline'],
|
|
'rating': item['rating'],
|
|
'duration': item['duration'],
|
|
'year': item['year'],
|
|
'thumb': item['thumb'],
|
|
'parent_thumb': item['parent_thumb'],
|
|
'grandparent_thumb': item['grandparent_thumb'],
|
|
'art': item['art'],
|
|
'originally_available_at': item['originally_available_at'],
|
|
'added_at': item['added_at'],
|
|
'updated_at': item['updated_at'],
|
|
'last_viewed_at': item['last_viewed_at'],
|
|
'guid': item['guid'],
|
|
'directors': directors,
|
|
'writers': writers,
|
|
'actors': actors,
|
|
'genres': genres,
|
|
'labels': labels,
|
|
'library_name': item['section_name'],
|
|
'section_id': item['section_id'],
|
|
'live': item['live'],
|
|
'media_info': media_info
|
|
}
|
|
metadata_list.append(metadata)
|
|
|
|
filtered_metadata_list = session.filter_session_info(metadata_list, filter_key='section_id')
|
|
|
|
if filtered_metadata_list:
|
|
return filtered_metadata_list[0]
|
|
else:
|
|
return []
|
|
|
|
def get_total_duration(self, custom_where=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
where, args = datatables.build_custom_where(custom_where=custom_where)
|
|
|
|
try:
|
|
query = "SELECT SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - " \
|
|
"SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration, " \
|
|
"(CASE WHEN session_history_metadata.live = 1 THEN 'live' ELSE session_history.media_type END) " \
|
|
"AS media_type_live " \
|
|
"FROM session_history " \
|
|
"JOIN session_history_metadata ON session_history_metadata.id = session_history.id " \
|
|
"JOIN session_history_media_info ON session_history_media_info.id = session_history.id " \
|
|
"%s " % where
|
|
result = monitor_db.select(query, args=args)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_total_duration: %s." % e)
|
|
return None
|
|
|
|
total_duration = 0
|
|
for item in result:
|
|
total_duration = item['total_duration']
|
|
|
|
return total_duration
|
|
|
|
def get_session_ip(self, session_key=''):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
ip_address = 'N/A'
|
|
|
|
user_cond = ''
|
|
if session.get_session_user_id():
|
|
user_cond = 'AND user_id = %s ' % session.get_session_user_id()
|
|
|
|
if session_key:
|
|
try:
|
|
query = "SELECT ip_address FROM sessions WHERE session_key = %d %s" % (int(session_key), user_cond)
|
|
result = monitor_db.select(query)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_session_ip: %s." % e)
|
|
return ip_address
|
|
else:
|
|
return ip_address
|
|
|
|
for item in result:
|
|
ip_address = item['ip_address']
|
|
|
|
return ip_address
|
|
|
|
def get_img_info(self, img=None, rating_key=None, width=None, height=None,
|
|
opacity=None, background=None, blur=None, fallback=None,
|
|
order_by='', service=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
img_info = []
|
|
|
|
where_params = []
|
|
args = []
|
|
|
|
if img is not None:
|
|
where_params.append('img')
|
|
args.append(img)
|
|
if rating_key is not None:
|
|
where_params.append('rating_key')
|
|
args.append(rating_key)
|
|
if width is not None:
|
|
where_params.append('width')
|
|
args.append(width)
|
|
if height is not None:
|
|
where_params.append('height')
|
|
args.append(height)
|
|
if opacity is not None:
|
|
where_params.append('opacity')
|
|
args.append(opacity)
|
|
if background is not None:
|
|
where_params.append('background')
|
|
args.append(background)
|
|
if blur is not None:
|
|
where_params.append('blur')
|
|
args.append(blur)
|
|
if fallback is not None:
|
|
where_params.append('fallback')
|
|
args.append(fallback)
|
|
|
|
where = ''
|
|
if where_params:
|
|
where = "WHERE " + " AND ".join([w + " = ?" for w in where_params])
|
|
|
|
if order_by:
|
|
order_by = "ORDER BY " + order_by + " DESC"
|
|
|
|
if service == 'imgur':
|
|
query = "SELECT imgur_title AS img_title, imgur_url AS img_url FROM imgur_lookup " \
|
|
"JOIN image_hash_lookup ON imgur_lookup.img_hash = image_hash_lookup.img_hash " \
|
|
"%s %s" % (where, order_by)
|
|
elif service == 'cloudinary':
|
|
query = "SELECT cloudinary_title AS img_title, cloudinary_url AS img_url FROM cloudinary_lookup " \
|
|
"JOIN image_hash_lookup ON cloudinary_lookup.img_hash = image_hash_lookup.img_hash " \
|
|
"%s %s" % (where, order_by)
|
|
else:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_img_info: "
|
|
"service not provided.")
|
|
return img_info
|
|
|
|
try:
|
|
img_info = monitor_db.select(query, args=args)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_img_info: %s." % e)
|
|
|
|
return img_info
|
|
|
|
def set_img_info(self, img_hash=None, img_title=None, img_url=None, delete_hash=None, service=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
keys = {'img_hash': img_hash}
|
|
|
|
if service == 'imgur':
|
|
table = 'imgur_lookup'
|
|
values = {'imgur_title': img_title,
|
|
'imgur_url': img_url,
|
|
'delete_hash': delete_hash}
|
|
elif service == 'cloudinary':
|
|
table = 'cloudinary_lookup'
|
|
values = {'cloudinary_title': img_title,
|
|
'cloudinary_url': img_url}
|
|
else:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for set_img_info: "
|
|
"service not provided.")
|
|
return
|
|
|
|
monitor_db.upsert(table, key_dict=keys, value_dict=values)
|
|
|
|
def delete_img_info(self, rating_key=None, service='', delete_all=False):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if not delete_all:
|
|
service = helpers.get_img_service()
|
|
|
|
if not rating_key and not delete_all:
|
|
logger.error("Tautulli DataFactory :: Unable to delete hosted images: rating_key not provided.")
|
|
return False
|
|
|
|
where = ''
|
|
args = []
|
|
log_msg = ''
|
|
if rating_key:
|
|
where = "WHERE rating_key = ?"
|
|
args = [rating_key]
|
|
log_msg = ' for rating_key %s' % rating_key
|
|
|
|
if service.lower() == 'imgur':
|
|
# Delete from Imgur
|
|
query = "SELECT imgur_title, delete_hash, fallback FROM imgur_lookup " \
|
|
"JOIN image_hash_lookup ON imgur_lookup.img_hash = image_hash_lookup.img_hash %s" % where
|
|
results = monitor_db.select(query, args=args)
|
|
|
|
for imgur_info in results:
|
|
if imgur_info['delete_hash']:
|
|
helpers.delete_from_imgur(delete_hash=imgur_info['delete_hash'],
|
|
img_title=imgur_info['imgur_title'],
|
|
fallback=imgur_info['fallback'])
|
|
|
|
logger.info("Tautulli DataFactory :: Deleting Imgur info%s from the database."
|
|
% log_msg)
|
|
result = monitor_db.action("DELETE FROM imgur_lookup WHERE img_hash "
|
|
"IN (SELECT img_hash FROM image_hash_lookup %s)" % where,
|
|
args)
|
|
|
|
elif service.lower() == 'cloudinary':
|
|
# Delete from Cloudinary
|
|
query = "SELECT cloudinary_title, rating_key, fallback FROM cloudinary_lookup " \
|
|
"JOIN image_hash_lookup ON cloudinary_lookup.img_hash = image_hash_lookup.img_hash %s " \
|
|
"GROUP BY rating_key" % where
|
|
results = monitor_db.select(query, args=args)
|
|
|
|
if delete_all:
|
|
helpers.delete_from_cloudinary(delete_all=delete_all)
|
|
else:
|
|
for cloudinary_info in results:
|
|
helpers.delete_from_cloudinary(rating_key=cloudinary_info['rating_key'])
|
|
|
|
logger.info("Tautulli DataFactory :: Deleting Cloudinary info%s from the database."
|
|
% log_msg)
|
|
result = monitor_db.action("DELETE FROM cloudinary_lookup WHERE img_hash "
|
|
"IN (SELECT img_hash FROM image_hash_lookup %s)" % where,
|
|
args)
|
|
|
|
else:
|
|
logger.error("Tautulli DataFactory :: Unable to delete hosted images: invalid service '%s' provided."
|
|
% service)
|
|
|
|
return service
|
|
|
|
def get_poster_info(self, rating_key='', metadata=None, service=None):
|
|
poster_key = ''
|
|
if str(rating_key).isdigit():
|
|
poster_key = rating_key
|
|
elif metadata:
|
|
if metadata['media_type'] in ('movie', 'show', 'artist', 'collection'):
|
|
poster_key = metadata['rating_key']
|
|
elif metadata['media_type'] in ('season', 'album'):
|
|
poster_key = metadata['rating_key']
|
|
elif metadata['media_type'] in ('episode', 'track'):
|
|
poster_key = metadata['parent_rating_key']
|
|
|
|
poster_info = {}
|
|
|
|
if poster_key:
|
|
service = service or helpers.get_img_service()
|
|
|
|
if service:
|
|
img_info = self.get_img_info(rating_key=poster_key,
|
|
order_by='height',
|
|
fallback='poster',
|
|
service=service)
|
|
if img_info:
|
|
poster_info = {'poster_title': img_info[0]['img_title'],
|
|
'poster_url': img_info[0]['img_url'],
|
|
'img_service': service.capitalize()}
|
|
|
|
return poster_info
|
|
|
|
def get_lookup_info(self, rating_key='', metadata=None):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
lookup_key = ''
|
|
if str(rating_key).isdigit():
|
|
lookup_key = rating_key
|
|
elif metadata:
|
|
if metadata['media_type'] in ('movie', 'show', 'artist', 'album', 'track'):
|
|
lookup_key = metadata['rating_key']
|
|
elif metadata['media_type'] == 'season':
|
|
lookup_key = metadata['parent_rating_key']
|
|
elif metadata['media_type'] == 'episode':
|
|
lookup_key = metadata['grandparent_rating_key']
|
|
|
|
lookup_info = {'tvmaze_id': '',
|
|
'themoviedb_id': '',
|
|
'musizbrainz_id': ''}
|
|
|
|
if lookup_key:
|
|
try:
|
|
query = 'SELECT tvmaze_id FROM tvmaze_lookup ' \
|
|
'WHERE rating_key = ?'
|
|
tvmaze_info = monitor_db.select_single(query, args=[lookup_key])
|
|
if tvmaze_info:
|
|
lookup_info['tvmaze_id'] = tvmaze_info['tvmaze_id']
|
|
|
|
query = 'SELECT themoviedb_id FROM themoviedb_lookup ' \
|
|
'WHERE rating_key = ?'
|
|
themoviedb_info = monitor_db.select_single(query, args=[lookup_key])
|
|
if themoviedb_info:
|
|
lookup_info['themoviedb_id'] = themoviedb_info['themoviedb_id']
|
|
|
|
query = 'SELECT musicbrainz_id FROM musicbrainz_lookup ' \
|
|
'WHERE rating_key = ?'
|
|
musicbrainz_info = monitor_db.select_single(query, args=[lookup_key])
|
|
if musicbrainz_info:
|
|
lookup_info['musicbrainz_id'] = musicbrainz_info['musicbrainz_id']
|
|
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_lookup_info: %s." % e)
|
|
|
|
return lookup_info
|
|
|
|
def delete_lookup_info(self, rating_key='', service='', delete_all=False):
|
|
if not rating_key and not delete_all:
|
|
logger.error("Tautulli DataFactory :: Unable to delete lookup info: rating_key not provided.")
|
|
return False
|
|
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if rating_key:
|
|
logger.info("Tautulli DataFactory :: Deleting lookup info for rating_key %s from the database."
|
|
% rating_key)
|
|
result_themoviedb = monitor_db.action("DELETE FROM themoviedb_lookup WHERE rating_key = ?", [rating_key])
|
|
result_tvmaze = monitor_db.action("DELETE FROM tvmaze_lookup WHERE rating_key = ?", [rating_key])
|
|
result_musicbrainz = monitor_db.action("DELETE FROM musicbrainz_lookup WHERE rating_key = ?", [rating_key])
|
|
return bool(result_themoviedb or result_tvmaze or result_musicbrainz)
|
|
elif service and delete_all:
|
|
if service.lower() in ('themoviedb', 'tvmaze', 'musicbrainz'):
|
|
logger.info("Tautulli DataFactory :: Deleting all lookup info for '%s' from the database."
|
|
% service)
|
|
result = monitor_db.action("DELETE FROM %s_lookup" % service.lower())
|
|
return bool(result)
|
|
else:
|
|
logger.error("Tautulli DataFactory :: Unable to delete lookup info: invalid service '%s' provided."
|
|
% service)
|
|
|
|
def get_search_query(self, rating_key=''):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if rating_key:
|
|
query = "SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, " \
|
|
"media_index, parent_media_index, year, media_type " \
|
|
"FROM session_history_metadata " \
|
|
"WHERE rating_key = ? " \
|
|
"OR parent_rating_key = ? " \
|
|
"OR grandparent_rating_key = ? " \
|
|
"LIMIT 1"
|
|
result = monitor_db.select(query=query, args=[rating_key, rating_key, rating_key])
|
|
else:
|
|
result = []
|
|
|
|
query = {}
|
|
query_string = None
|
|
media_type = None
|
|
|
|
for item in result:
|
|
title = item['title']
|
|
parent_title = item['parent_title']
|
|
grandparent_title = item['grandparent_title']
|
|
media_index = item['media_index']
|
|
parent_media_index = item['parent_media_index']
|
|
year = item['year']
|
|
|
|
if str(item['rating_key']) == rating_key:
|
|
query_string = item['title']
|
|
media_type = item['media_type']
|
|
|
|
elif str(item['parent_rating_key']) == rating_key:
|
|
if item['media_type'] == 'episode':
|
|
query_string = item['grandparent_title']
|
|
media_type = 'season'
|
|
elif item['media_type'] == 'track':
|
|
query_string = item['parent_title']
|
|
media_type = 'album'
|
|
|
|
elif str(item['grandparent_rating_key']) == rating_key:
|
|
if item['media_type'] == 'episode':
|
|
query_string = item['grandparent_title']
|
|
media_type = 'show'
|
|
elif item['media_type'] == 'track':
|
|
query_string = item['grandparent_title']
|
|
media_type = 'artist'
|
|
|
|
if query_string and media_type:
|
|
query = {'query_string': query_string,
|
|
'title': title,
|
|
'parent_title': parent_title,
|
|
'grandparent_title': grandparent_title,
|
|
'media_index': media_index,
|
|
'parent_media_index': parent_media_index,
|
|
'year': year,
|
|
'media_type': media_type,
|
|
'rating_key': rating_key
|
|
}
|
|
else:
|
|
return None
|
|
|
|
return query
|
|
|
|
def get_rating_keys_list(self, rating_key='', media_type=''):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if media_type == 'movie':
|
|
key_list = {0: {'rating_key': int(rating_key)}}
|
|
return key_list
|
|
|
|
if media_type == 'artist' or media_type == 'album' or media_type == 'track':
|
|
match_type = 'title'
|
|
else:
|
|
match_type = 'index'
|
|
|
|
# Get the grandparent rating key
|
|
try:
|
|
query = "SELECT rating_key, parent_rating_key, grandparent_rating_key " \
|
|
"FROM session_history_metadata " \
|
|
"WHERE rating_key = ? " \
|
|
"OR parent_rating_key = ? " \
|
|
"OR grandparent_rating_key = ? " \
|
|
"LIMIT 1"
|
|
result = monitor_db.select(query=query, args=[rating_key, rating_key, rating_key])
|
|
|
|
grandparent_rating_key = result[0]['grandparent_rating_key']
|
|
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_rating_keys_list: %s." % e)
|
|
return {}
|
|
|
|
query = "SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, " \
|
|
"media_index, parent_media_index " \
|
|
"FROM session_history_metadata " \
|
|
"WHERE {0} = ? " \
|
|
"GROUP BY {1} " \
|
|
"ORDER BY {1} DESC "
|
|
|
|
# get grandparent_rating_keys
|
|
grandparents = {}
|
|
result = monitor_db.select(query=query.format('grandparent_rating_key', 'grandparent_rating_key'),
|
|
args=[grandparent_rating_key])
|
|
for item in result:
|
|
# get parent_rating_keys
|
|
parents = {}
|
|
result = monitor_db.select(query=query.format('grandparent_rating_key', 'parent_rating_key'),
|
|
args=[item['grandparent_rating_key']])
|
|
for item in result:
|
|
# get rating_keys
|
|
children = {}
|
|
result = monitor_db.select(query=query.format('parent_rating_key', 'rating_key'),
|
|
args=[item['parent_rating_key']])
|
|
for item in result:
|
|
key = item['media_index'] if item['media_index'] else str(item['title']).lower()
|
|
children.update({key: {'rating_key': item['rating_key']}})
|
|
|
|
key = item['parent_media_index'] if match_type == 'index' else str(item['parent_title']).lower()
|
|
parents.update({key:
|
|
{'rating_key': item['parent_rating_key'],
|
|
'children': children}
|
|
})
|
|
|
|
key = 0 if match_type == 'index' else str(item['grandparent_title']).lower()
|
|
grandparents.update({key:
|
|
{'rating_key': item['grandparent_rating_key'],
|
|
'children': parents}
|
|
})
|
|
|
|
key_list = grandparents
|
|
|
|
return key_list
|
|
|
|
def update_metadata(self, old_key_list='', new_key_list='', media_type='', single_update=False):
|
|
pms_connect = pmsconnect.PmsConnect()
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
# function to map rating keys pairs
|
|
def get_pairs(old, new):
|
|
pairs = {}
|
|
for k, v in old.items():
|
|
if k in new:
|
|
pairs.update({v['rating_key']: new[k]['rating_key']})
|
|
if 'children' in old[k]:
|
|
pairs.update(get_pairs(old[k]['children'], new[k]['children']))
|
|
|
|
return pairs
|
|
|
|
# map rating keys pairs
|
|
mapping = {}
|
|
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.")
|
|
|
|
global _UPDATE_METADATA_IDS
|
|
if single_update:
|
|
_UPDATE_METADATA_IDS = {
|
|
'grandparent_rating_key_ids': set(),
|
|
'parent_rating_key_ids': set(),
|
|
'rating_key_ids': set()
|
|
}
|
|
|
|
for old_key, new_key in mapping.items():
|
|
metadata = pms_connect.get_metadata_details(new_key)
|
|
|
|
if metadata:
|
|
logger.debug("Tautulli DataFactory :: Mapping for rating_key %s -> %s (%s)",
|
|
old_key, new_key, metadata['media_type'])
|
|
|
|
if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
|
|
# check grandparent_rating_key (2 tables)
|
|
query = (
|
|
"SELECT id FROM session_history "
|
|
"WHERE grandparent_rating_key = ? "
|
|
)
|
|
args = [old_key]
|
|
|
|
if _UPDATE_METADATA_IDS['grandparent_rating_key_ids']:
|
|
query += "AND id NOT IN (%s)" % ",".join(_UPDATE_METADATA_IDS['grandparent_rating_key_ids'])
|
|
|
|
ids = [str(row['id']) for row in monitor_db.select(query, args)]
|
|
if ids:
|
|
_UPDATE_METADATA_IDS['grandparent_rating_key_ids'].update(ids)
|
|
else:
|
|
continue
|
|
|
|
monitor_db.action(
|
|
"UPDATE session_history SET grandparent_rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
monitor_db.action(
|
|
"UPDATE session_history_metadata SET grandparent_rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
|
|
elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album':
|
|
# check parent_rating_key (2 tables)
|
|
query = (
|
|
"SELECT id FROM session_history "
|
|
"WHERE parent_rating_key = ? "
|
|
)
|
|
args = [old_key]
|
|
|
|
if _UPDATE_METADATA_IDS['parent_rating_key_ids']:
|
|
query += "AND id NOT IN (%s)" % ",".join(_UPDATE_METADATA_IDS['parent_rating_key_ids'])
|
|
|
|
ids = [str(row['id']) for row in monitor_db.select(query, args)]
|
|
if ids:
|
|
_UPDATE_METADATA_IDS['parent_rating_key_ids'].update(ids)
|
|
else:
|
|
continue
|
|
|
|
monitor_db.action(
|
|
"UPDATE session_history SET parent_rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
monitor_db.action(
|
|
"UPDATE session_history_metadata SET parent_rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
|
|
else:
|
|
# check rating_key (2 tables)
|
|
query = (
|
|
"SELECT id FROM session_history "
|
|
"WHERE rating_key = ? "
|
|
)
|
|
args = [old_key]
|
|
|
|
if _UPDATE_METADATA_IDS['rating_key_ids']:
|
|
query += "AND id NOT IN (%s)" % ",".join(_UPDATE_METADATA_IDS['rating_key_ids'])
|
|
|
|
ids = [str(row['id']) for row in monitor_db.select(query, args)]
|
|
if ids:
|
|
_UPDATE_METADATA_IDS['rating_key_ids'].update(ids)
|
|
else:
|
|
continue
|
|
|
|
monitor_db.action(
|
|
"UPDATE session_history SET rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
monitor_db.action(
|
|
"UPDATE session_history_media_info SET rating_key = ? "
|
|
"WHERE id IN (%s)" % ",".join(ids),
|
|
[new_key]
|
|
)
|
|
|
|
# update session_history_metadata table
|
|
self.update_metadata_details(old_key, new_key, metadata, ids)
|
|
|
|
return 'Updated metadata in database.'
|
|
else:
|
|
return 'Unable to update metadata in database. No changes were made.'
|
|
|
|
def update_metadata_details(self, old_rating_key='', new_rating_key='', metadata=None, ids=None):
|
|
|
|
if metadata:
|
|
# Create full_title
|
|
if metadata['media_type'] == 'episode':
|
|
full_title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
|
|
elif metadata['media_type'] == 'track':
|
|
full_title = '%s - %s' % (metadata['title'],
|
|
metadata['original_title'] or metadata['grandparent_title'])
|
|
else:
|
|
full_title = metadata['title']
|
|
|
|
directors = ";".join(metadata['directors'])
|
|
writers = ";".join(metadata['writers'])
|
|
actors = ";".join(metadata['actors'])
|
|
genres = ";".join(metadata['genres'])
|
|
labels = ";".join(metadata['labels'])
|
|
|
|
logger.debug("Tautulli DataFactory :: Updating metadata in the database for rating_key %s -> %s.",
|
|
old_rating_key, new_rating_key)
|
|
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
query = "UPDATE session_history SET section_id = ? " \
|
|
"WHERE id IN (%s)" % ",".join(ids)
|
|
args = [metadata['section_id']]
|
|
monitor_db.action(query=query, args=args)
|
|
|
|
# Update the session_history_metadata table
|
|
query = "UPDATE session_history_metadata SET rating_key = ?, parent_rating_key = ?, " \
|
|
"grandparent_rating_key = ?, title = ?, parent_title = ?, grandparent_title = ?, " \
|
|
"original_title = ?, full_title = ?, " \
|
|
"media_index = ?, parent_media_index = ?, thumb = ?, parent_thumb = ?, " \
|
|
"grandparent_thumb = ?, art = ?, media_type = ?, year = ?, originally_available_at = ?, " \
|
|
"added_at = ?, updated_at = ?, last_viewed_at = ?, content_rating = ?, summary = ?, " \
|
|
"tagline = ?, rating = ?, duration = ?, guid = ?, directors = ?, writers = ?, actors = ?, " \
|
|
"genres = ?, studio = ?, labels = ? " \
|
|
"WHERE id IN (%s)" % ",".join(ids)
|
|
|
|
args = [metadata['rating_key'], metadata['parent_rating_key'], metadata['grandparent_rating_key'],
|
|
metadata['title'], metadata['parent_title'], metadata['grandparent_title'],
|
|
metadata['original_title'], full_title,
|
|
metadata['media_index'], metadata['parent_media_index'], metadata['thumb'],
|
|
metadata['parent_thumb'], metadata['grandparent_thumb'], metadata['art'], metadata['media_type'],
|
|
metadata['year'], metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'],
|
|
metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'],
|
|
metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres,
|
|
metadata['studio'], labels]
|
|
|
|
monitor_db.action(query=query, args=args)
|
|
|
|
def get_notification_log(self, kwargs=None):
|
|
data_tables = datatables.DataTables()
|
|
|
|
columns = ["notify_log.id",
|
|
"notify_log.timestamp",
|
|
"notify_log.session_key",
|
|
"notify_log.rating_key",
|
|
"notify_log.user_id",
|
|
"notify_log.user",
|
|
"notify_log.notifier_id",
|
|
"notify_log.agent_id",
|
|
"notify_log.agent_name",
|
|
"notify_log.notify_action",
|
|
"notify_log.subject_text",
|
|
"notify_log.body_text",
|
|
"notify_log.success"
|
|
]
|
|
try:
|
|
query = data_tables.ssp_query(table_name='notify_log',
|
|
columns=columns,
|
|
custom_where=[],
|
|
group_by=[],
|
|
join_types=[],
|
|
join_tables=[],
|
|
join_evals=[],
|
|
kwargs=kwargs)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_notification_log: %s." % e)
|
|
return {'recordsFiltered': 0,
|
|
'recordsTotal': 0,
|
|
'draw': 0,
|
|
'data': []}
|
|
|
|
notifications = query['result']
|
|
|
|
rows = []
|
|
for item in notifications:
|
|
if item['body_text']:
|
|
body_text = item['body_text'].replace('\r\n', '<br />').replace('\n', '<br />')
|
|
else:
|
|
body_text = ''
|
|
|
|
row = {'id': item['id'],
|
|
'timestamp': item['timestamp'],
|
|
'session_key': item['session_key'],
|
|
'rating_key': item['rating_key'],
|
|
'user_id': item['user_id'],
|
|
'user': item['user'],
|
|
'notifier_id': item['notifier_id'],
|
|
'agent_id': item['agent_id'],
|
|
'agent_name': item['agent_name'],
|
|
'notify_action': item['notify_action'],
|
|
'subject_text': item['subject_text'],
|
|
'body_text': body_text,
|
|
'success': item['success']
|
|
}
|
|
|
|
rows.append(row)
|
|
|
|
dict = {'recordsFiltered': query['filteredCount'],
|
|
'recordsTotal': query['totalCount'],
|
|
'data': rows,
|
|
'draw': query['draw']
|
|
}
|
|
|
|
return dict
|
|
|
|
def delete_notification_log(self):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
try:
|
|
logger.info("Tautulli DataFactory :: Clearing notification logs from database.")
|
|
monitor_db.action("DELETE FROM notify_log")
|
|
monitor_db.action("VACUUM")
|
|
return True
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for delete_notification_log: %s." % e)
|
|
return False
|
|
|
|
def get_newsletter_log(self, kwargs=None):
|
|
data_tables = datatables.DataTables()
|
|
|
|
columns = ["newsletter_log.id",
|
|
"newsletter_log.timestamp",
|
|
"newsletter_log.newsletter_id",
|
|
"newsletter_log.agent_id",
|
|
"newsletter_log.agent_name",
|
|
"newsletter_log.notify_action",
|
|
"newsletter_log.subject_text",
|
|
"newsletter_log.body_text",
|
|
"newsletter_log.start_date",
|
|
"newsletter_log.end_date",
|
|
"newsletter_log.uuid",
|
|
"newsletter_log.success"
|
|
]
|
|
try:
|
|
query = data_tables.ssp_query(table_name='newsletter_log',
|
|
columns=columns,
|
|
custom_where=[],
|
|
group_by=[],
|
|
join_types=[],
|
|
join_tables=[],
|
|
join_evals=[],
|
|
kwargs=kwargs)
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_newsletter_log: %s." % e)
|
|
return {'recordsFiltered': 0,
|
|
'recordsTotal': 0,
|
|
'draw': 0,
|
|
'data': []}
|
|
|
|
newsletters = query['result']
|
|
|
|
rows = []
|
|
for item in newsletters:
|
|
row = {'id': item['id'],
|
|
'timestamp': item['timestamp'],
|
|
'newsletter_id': item['newsletter_id'],
|
|
'agent_id': item['agent_id'],
|
|
'agent_name': item['agent_name'],
|
|
'notify_action': item['notify_action'],
|
|
'subject_text': item['subject_text'],
|
|
'body_text': item['body_text'],
|
|
'start_date': item['start_date'],
|
|
'end_date': item['end_date'],
|
|
'uuid': item['uuid'],
|
|
'success': item['success']
|
|
}
|
|
|
|
rows.append(row)
|
|
|
|
dict = {'recordsFiltered': query['filteredCount'],
|
|
'recordsTotal': query['totalCount'],
|
|
'data': rows,
|
|
'draw': query['draw']
|
|
}
|
|
|
|
return dict
|
|
|
|
def delete_newsletter_log(self):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
try:
|
|
logger.info("Tautulli DataFactory :: Clearing newsletter logs from database.")
|
|
monitor_db.action("DELETE FROM newsletter_log")
|
|
monitor_db.action("VACUUM")
|
|
return True
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for delete_newsletter_log: %s." % e)
|
|
return False
|
|
|
|
def get_user_devices(self, user_id='', history_only=True):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if user_id:
|
|
if history_only:
|
|
query = "SELECT machine_id FROM session_history " \
|
|
"WHERE user_id = ? " \
|
|
"GROUP BY machine_id"
|
|
else:
|
|
query = "SELECT * FROM (" \
|
|
"SELECT user_id, machine_id FROM session_history " \
|
|
"UNION SELECT user_id, machine_id from sessions_continued) " \
|
|
"WHERE user_id = ? " \
|
|
"GROUP BY machine_id"
|
|
|
|
try:
|
|
result = monitor_db.select(query=query, args=[user_id])
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_user_devices: %s." % e)
|
|
return []
|
|
else:
|
|
return []
|
|
|
|
return [d['machine_id'] for d in result]
|
|
|
|
def get_recently_added_item(self, rating_key=''):
|
|
monitor_db = database.MonitorDatabase()
|
|
|
|
if rating_key:
|
|
try:
|
|
query = "SELECT * FROM recently_added WHERE rating_key = ?"
|
|
result = monitor_db.select(query=query, args=[rating_key])
|
|
except Exception as e:
|
|
logger.warn("Tautulli DataFactory :: Unable to execute database query for get_recently_added_item: %s." % e)
|
|
return []
|
|
else:
|
|
return []
|
|
|
|
return result
|
|
|
|
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
|
|
|
|
return True
|