mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 05:31:15 -07:00
watch time & user stats for collections (#1982)
* user_stats for collection * watch_time_stats for collection * check for media_type to be compatible with API * update API and datafactory optimizations * beautify webserve class * fix sql query build * filter on suitable collections * stats for collections of sub media type * optimize array creation
This commit is contained in:
parent
993909fa08
commit
ae3d75bbe3
3 changed files with 69 additions and 29 deletions
|
@ -12,6 +12,7 @@ data :: Usable parameters (if not applicable for media type, blank value will be
|
||||||
== Global keys ==
|
== Global keys ==
|
||||||
rating_key Returns the unique identifier for the media item.
|
rating_key Returns the unique identifier for the media item.
|
||||||
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
|
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
|
||||||
|
sub_media_type Returns the subtype of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
|
||||||
art Returns the location of the item's artwork
|
art Returns the location of the item's artwork
|
||||||
title Returns the name of the movie, show, episode, artist, album, or track.
|
title Returns the name of the movie, show, episode, artist, album, or track.
|
||||||
edition_title Returns the edition title of a movie.
|
edition_title Returns the edition title of a movie.
|
||||||
|
@ -553,7 +554,7 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track'):
|
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection'):
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="table-card-header">
|
<div class="table-card-header">
|
||||||
<div class="header-bar">
|
<div class="header-bar">
|
||||||
|
@ -936,13 +937,16 @@ DOCUMENTATION :: END
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
% endif
|
% endif
|
||||||
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track'):
|
% if data['media_type'] in ('movie', 'show', 'season', 'episode', 'artist', 'album', 'track', 'collection'):
|
||||||
<script>
|
<script>
|
||||||
// Populate watch time stats
|
// Populate watch time stats
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'item_watch_time_stats',
|
url: 'item_watch_time_stats',
|
||||||
async: true,
|
async: true,
|
||||||
data: { rating_key: "${data['rating_key']}" },
|
data: {
|
||||||
|
rating_key: "${data['rating_key']}",
|
||||||
|
media_type: "${data['media_type']}"
|
||||||
|
},
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
$("#watch-time-stats").html(xhr.responseText);
|
$("#watch-time-stats").html(xhr.responseText);
|
||||||
}
|
}
|
||||||
|
@ -951,7 +955,10 @@ DOCUMENTATION :: END
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'item_user_stats',
|
url: 'item_user_stats',
|
||||||
async: true,
|
async: true,
|
||||||
data: { rating_key: "${data['rating_key']}" },
|
data: {
|
||||||
|
rating_key: "${data['rating_key']}",
|
||||||
|
media_type: "${data['media_type']}"
|
||||||
|
},
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
$("#user-stats").html(xhr.responseText);
|
$("#user-stats").html(xhr.responseText);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# This file is part of Tautulli.
|
# This file is part of Tautulli.
|
||||||
#
|
#
|
||||||
|
@ -1188,7 +1188,7 @@ class DataFactory(object):
|
||||||
|
|
||||||
return library_stats
|
return library_stats
|
||||||
|
|
||||||
def get_watch_time_stats(self, rating_key=None, grouping=None, query_days=None):
|
def get_watch_time_stats(self, rating_key=None, media_type=None, grouping=None, query_days=None):
|
||||||
if rating_key is None:
|
if rating_key is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -1210,6 +1210,15 @@ class DataFactory(object):
|
||||||
|
|
||||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||||
|
|
||||||
|
if media_type == 'collection':
|
||||||
|
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:
|
for days in query_days:
|
||||||
timestamp_query = timestamp - days * 24 * 60 * 60
|
timestamp_query = timestamp - days * 24 * 60 * 60
|
||||||
|
|
||||||
|
@ -1221,11 +1230,14 @@ class DataFactory(object):
|
||||||
'COUNT(DISTINCT %s) AS total_plays, section_id ' \
|
'COUNT(DISTINCT %s) AS total_plays, section_id ' \
|
||||||
'FROM session_history ' \
|
'FROM session_history ' \
|
||||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||||
'WHERE stopped >= %s ' \
|
'WHERE stopped >= ? ' \
|
||||||
'AND (session_history.grandparent_rating_key = ? ' \
|
'AND (session_history.grandparent_rating_key IN (%s) ' \
|
||||||
'OR session_history.parent_rating_key = ? ' \
|
'OR session_history.parent_rating_key IN (%s) ' \
|
||||||
'OR session_history.rating_key = ?)' % (group_by, timestamp_query)
|
'OR session_history.rating_key IN (%s))' % (
|
||||||
result = monitor_db.select(query, args=[rating_key, rating_key, rating_key])
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
result = monitor_db.select(query, args=[timestamp_query] + rating_keys * 3)
|
||||||
else:
|
else:
|
||||||
result = []
|
result = []
|
||||||
else:
|
else:
|
||||||
|
@ -1235,10 +1247,13 @@ class DataFactory(object):
|
||||||
'COUNT(DISTINCT %s) AS total_plays, section_id ' \
|
'COUNT(DISTINCT %s) AS total_plays, section_id ' \
|
||||||
'FROM session_history ' \
|
'FROM session_history ' \
|
||||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||||
'WHERE (session_history.grandparent_rating_key = ? ' \
|
'WHERE (session_history.grandparent_rating_key IN (%s) ' \
|
||||||
'OR session_history.parent_rating_key = ? ' \
|
'OR session_history.parent_rating_key IN (%s) ' \
|
||||||
'OR session_history.rating_key = ?)' % group_by
|
'OR session_history.rating_key IN (%s))' % (
|
||||||
result = monitor_db.select(query, args=[rating_key, rating_key, rating_key])
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
result = monitor_db.select(query, args=rating_keys * 3)
|
||||||
else:
|
else:
|
||||||
result = []
|
result = []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1267,7 +1282,7 @@ class DataFactory(object):
|
||||||
|
|
||||||
return item_watch_time_stats
|
return item_watch_time_stats
|
||||||
|
|
||||||
def get_user_stats(self, rating_key=None, grouping=None):
|
def get_user_stats(self, rating_key=None, media_type=None, grouping=None):
|
||||||
if grouping is None:
|
if grouping is None:
|
||||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||||
|
|
||||||
|
@ -1279,6 +1294,15 @@ class DataFactory(object):
|
||||||
|
|
||||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||||
|
|
||||||
|
if media_type == 'collection':
|
||||||
|
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:
|
try:
|
||||||
if str(rating_key).isdigit():
|
if str(rating_key).isdigit():
|
||||||
query = 'SELECT (CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = "" ' \
|
query = 'SELECT (CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = "" ' \
|
||||||
|
@ -1290,12 +1314,15 @@ class DataFactory(object):
|
||||||
'FROM session_history ' \
|
'FROM session_history ' \
|
||||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||||
'JOIN users ON users.user_id = session_history.user_id ' \
|
'JOIN users ON users.user_id = session_history.user_id ' \
|
||||||
'WHERE (session_history.grandparent_rating_key = ? ' \
|
'WHERE (session_history.grandparent_rating_key IN (%s) ' \
|
||||||
'OR session_history.parent_rating_key = ? ' \
|
'OR session_history.parent_rating_key IN (%s) ' \
|
||||||
'OR session_history.rating_key = ?) ' \
|
'OR session_history.rating_key IN (%s)) ' \
|
||||||
'GROUP BY users.user_id ' \
|
'GROUP BY users.user_id ' \
|
||||||
'ORDER BY total_plays DESC, total_time DESC' % group_by
|
'ORDER BY total_plays DESC, total_time DESC' % (
|
||||||
result = monitor_db.select(query, args=[rating_key, rating_key, rating_key])
|
group_by, rating_keys_arg, rating_keys_arg, rating_keys_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
result = monitor_db.select(query, args=rating_keys * 3)
|
||||||
else:
|
else:
|
||||||
result = []
|
result = []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -4431,10 +4431,10 @@ class WebInterface(object):
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth()
|
@requireAuth()
|
||||||
def item_watch_time_stats(self, rating_key=None, **kwargs):
|
def item_watch_time_stats(self, rating_key=None, media_type=None, **kwargs):
|
||||||
if rating_key:
|
if rating_key:
|
||||||
item_data = datafactory.DataFactory()
|
item_data = datafactory.DataFactory()
|
||||||
result = item_data.get_watch_time_stats(rating_key=rating_key)
|
result = item_data.get_watch_time_stats(rating_key=rating_key, media_type=media_type)
|
||||||
else:
|
else:
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
|
@ -4446,10 +4446,10 @@ class WebInterface(object):
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth()
|
@requireAuth()
|
||||||
def item_user_stats(self, rating_key=None, **kwargs):
|
def item_user_stats(self, rating_key=None, media_type=None, **kwargs):
|
||||||
if rating_key:
|
if rating_key:
|
||||||
item_data = datafactory.DataFactory()
|
item_data = datafactory.DataFactory()
|
||||||
result = item_data.get_user_stats(rating_key=rating_key)
|
result = item_data.get_user_stats(rating_key=rating_key, media_type=media_type)
|
||||||
else:
|
else:
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
|
@ -4463,7 +4463,7 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi()
|
@addtoapi()
|
||||||
def get_item_watch_time_stats(self, rating_key=None, grouping=None, query_days=None, **kwargs):
|
def get_item_watch_time_stats(self, rating_key=None, media_type=None, grouping=None, query_days=None, **kwargs):
|
||||||
""" Get the watch time stats for the media item.
|
""" Get the watch time stats for the media item.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -4471,6 +4471,7 @@ class WebInterface(object):
|
||||||
rating_key (str): Rating key of the item
|
rating_key (str): Rating key of the item
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
media_type (str): Media type of the item (only required for a collection)
|
||||||
grouping (int): 0 or 1
|
grouping (int): 0 or 1
|
||||||
query_days (str): Comma separated days, e.g. "1,7,30,0"
|
query_days (str): Comma separated days, e.g. "1,7,30,0"
|
||||||
|
|
||||||
|
@ -4504,7 +4505,9 @@ class WebInterface(object):
|
||||||
|
|
||||||
if rating_key:
|
if rating_key:
|
||||||
item_data = datafactory.DataFactory()
|
item_data = datafactory.DataFactory()
|
||||||
result = item_data.get_watch_time_stats(rating_key=rating_key, grouping=grouping,
|
result = item_data.get_watch_time_stats(rating_key=rating_key,
|
||||||
|
media_type=media_type,
|
||||||
|
grouping=grouping,
|
||||||
query_days=query_days)
|
query_days=query_days)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
|
@ -4518,7 +4521,7 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi()
|
@addtoapi()
|
||||||
def get_item_user_stats(self, rating_key=None, grouping=None, **kwargs):
|
def get_item_user_stats(self, rating_key=None, media_type=None, grouping=None, **kwargs):
|
||||||
""" Get the user stats for the media item.
|
""" Get the user stats for the media item.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -4526,6 +4529,7 @@ class WebInterface(object):
|
||||||
rating_key (str): Rating key of the item
|
rating_key (str): Rating key of the item
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
media_type (str): Media type of the item (only required for a collection)
|
||||||
grouping (int): 0 or 1
|
grouping (int): 0 or 1
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -4554,7 +4558,9 @@ class WebInterface(object):
|
||||||
|
|
||||||
if rating_key:
|
if rating_key:
|
||||||
item_data = datafactory.DataFactory()
|
item_data = datafactory.DataFactory()
|
||||||
result = item_data.get_user_stats(rating_key=rating_key, grouping=grouping)
|
result = item_data.get_user_stats(rating_key=rating_key,
|
||||||
|
media_type=media_type,
|
||||||
|
grouping=grouping)
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue