mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 15:56:07 -07:00
Add media info table to library page
This commit is contained in:
parent
10e4d562ab
commit
381c3da31c
28 changed files with 1415 additions and 462 deletions
|
@ -60,7 +60,7 @@ class Libraries(object):
|
|||
join_tables=['session_history_metadata',
|
||||
'session_history',
|
||||
'session_history_media_info'],
|
||||
join_evals=[['session_history_metadata.library_id', 'library_sections.section_id'],
|
||||
join_evals=[['session_history_metadata.section_id', 'library_sections.section_id'],
|
||||
['session_history_metadata.id', 'session_history.id'],
|
||||
['session_history_metadata.id', 'session_history_media_info.id']],
|
||||
kwargs=kwargs)
|
||||
|
@ -116,10 +116,295 @@ class Libraries(object):
|
|||
'recordsTotal': query['totalCount'],
|
||||
'data': rows,
|
||||
'draw': query['draw']
|
||||
}
|
||||
}
|
||||
|
||||
return dict
|
||||
|
||||
def get_datatables_media_info(self, section_id=None, kwargs=None):
|
||||
data_tables = datatables.DataTables()
|
||||
|
||||
custom_where = ['library_sections.section_id', section_id]
|
||||
|
||||
columns = ['session_history.id',
|
||||
'session_history.started AS last_watched',
|
||||
'COUNT(DISTINCT session_history.reference_id) AS play_count',
|
||||
'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.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.media_type',
|
||||
'session_history_metadata.added_at',
|
||||
'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_framerate',
|
||||
'session_history_media_info.audio_codec',
|
||||
'session_history_media_info.audio_channels',
|
||||
'session_history_media_info.duration AS file_size'
|
||||
]
|
||||
|
||||
try:
|
||||
query = data_tables.ssp_query(table_name='session_history',
|
||||
columns=columns,
|
||||
custom_where=[custom_where],
|
||||
group_by=['session_history_metadata.rating_key'],
|
||||
join_types=['JOIN',
|
||||
'JOIN',
|
||||
'JOIN'],
|
||||
join_tables=['library_sections',
|
||||
'session_history_metadata',
|
||||
'session_history_media_info'],
|
||||
join_evals=[['session_history_metadata.section_id', 'library_sections.section_id'],
|
||||
['session_history.id', 'session_history_metadata.id'],
|
||||
['session_history.id', 'session_history_media_info.id']],
|
||||
kwargs=kwargs)
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_datatables_media_info: %s." % e)
|
||||
return {'recordsFiltered': 0,
|
||||
'recordsTotal': 0,
|
||||
'draw': 0,
|
||||
'data': 'null',
|
||||
'error': 'Unable to execute database query.'}
|
||||
|
||||
results = query['result']
|
||||
|
||||
rows = []
|
||||
for item in results:
|
||||
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']
|
||||
|
||||
row = {'id': item['id'],
|
||||
'last_watched': item['last_watched'],
|
||||
'added_at': item['added_at'],
|
||||
'play_count': item['play_count'],
|
||||
'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'],
|
||||
'year': item['year'],
|
||||
'media_index': item['media_index'],
|
||||
'parent_media_index': item['parent_media_index'],
|
||||
'thumb': thumb,
|
||||
'container': item['container'],
|
||||
'bitrate': item['bitrate'],
|
||||
'video_codec': item['video_codec'],
|
||||
'video_resolution': item['video_resolution'],
|
||||
'video_framerate': item['video_framerate'],
|
||||
'audio_codec': item['audio_codec'],
|
||||
'audio_channels': item['audio_channels'],
|
||||
'file_size': item['file_size']
|
||||
}
|
||||
|
||||
rows.append(row)
|
||||
|
||||
dict = {'recordsFiltered': query['filteredCount'],
|
||||
'recordsTotal': query['totalCount'],
|
||||
'data': rows,
|
||||
'draw': query['draw']
|
||||
}
|
||||
|
||||
return dict
|
||||
|
||||
def get_datatables_media_info2(self, section_id=None, section_type=None, rating_key=None, kwargs=None):
|
||||
from plexpy import pmsconnect
|
||||
import json, os
|
||||
|
||||
default_return = {'recordsFiltered': 0,
|
||||
'recordsTotal': 0,
|
||||
'draw': 0,
|
||||
'data': None,
|
||||
'error': 'Unable to execute database query.'}
|
||||
|
||||
if section_id and not str(section_id).isdigit():
|
||||
logger.warn(u"PlexPy Libraries :: Datatable media info called by invalid section_id provided.")
|
||||
return default_return
|
||||
elif rating_key and not str(rating_key).isdigit():
|
||||
logger.warn(u"PlexPy Libraries :: Datatable media info called by invalid rating_key provided.")
|
||||
return default_return
|
||||
|
||||
rows = []
|
||||
if rating_key:
|
||||
try:
|
||||
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info-%s_%s.json' % (section_id, rating_key))
|
||||
with open(inFilePath, 'r') as inFile:
|
||||
rows = json.load(inFile)
|
||||
library_count = len(rows)
|
||||
except IOError as e:
|
||||
logger.debug(u"PlexPy Libraries :: No JSON file for rating_key %s." % rating_key)
|
||||
logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for rating_key %s." % rating_key)
|
||||
elif section_id:
|
||||
try:
|
||||
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info-%s.json' % section_id)
|
||||
with open(inFilePath, 'r') as inFile:
|
||||
rows = json.load(inFile)
|
||||
library_count = len(rows)
|
||||
except IOError as e:
|
||||
logger.debug(u"PlexPy Libraries :: No JSON file for library section_id %s." % section_id)
|
||||
logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for section_id %s." % section_id)
|
||||
|
||||
if not rows:
|
||||
# Get the library details
|
||||
library_details = self.get_details(section_id=section_id)
|
||||
if library_details['section_id'] == None:
|
||||
logger.warn(u"PlexPy Libraries :: Library section_id %s not found." % section_id)
|
||||
return default_return
|
||||
|
||||
if not section_type:
|
||||
section_type = library_details['section_type']
|
||||
|
||||
# Get play counts from the database
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if section_type == 'show' or section_type == 'artist':
|
||||
group_by = 'grandparent_rating_key'
|
||||
elif section_type == 'season' or section_type == 'album':
|
||||
group_by = 'parent_rating_key'
|
||||
else:
|
||||
group_by = 'rating_key'
|
||||
|
||||
try:
|
||||
query = 'SELECT MAX(session_history.started) AS last_watched, COUNT(session_history.id) AS play_count, ' \
|
||||
'session_history.rating_key, session_history.parent_rating_key, session_history.grandparent_rating_key ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_metadata ON session_history.id = session_history_metadata.id ' \
|
||||
'WHERE session_history_metadata.section_id = ? ' \
|
||||
'GROUP BY session_history.%s ' % group_by
|
||||
result = monitor_db.select(query, args=[section_id])
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_datatables_media_info2: %s." % e)
|
||||
return default_return
|
||||
|
||||
watched_list = {}
|
||||
for item in result:
|
||||
watched_list[str(item[group_by])] = {'last_watched': item['last_watched'],
|
||||
'play_count': item['play_count']}
|
||||
|
||||
# Get all library children items
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
|
||||
|
||||
if rating_key:
|
||||
library_children = pms_connect.get_library_children(rating_key=rating_key,
|
||||
get_media_info=True)
|
||||
elif section_id:
|
||||
library_children = pms_connect.get_library_children(section_id=section_id,
|
||||
section_type=section_type,
|
||||
get_media_info=True)
|
||||
|
||||
if library_children:
|
||||
library_count = library_children['library_count']
|
||||
children_list = library_children['childern_list']
|
||||
else:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to get a list of library items.")
|
||||
return default_return
|
||||
|
||||
rows = []
|
||||
for item in children_list:
|
||||
thumb = item['thumb']
|
||||
|
||||
if item['media_type'] == 'episode' or item['media_type'] == 'track':
|
||||
full_title = 'E%s - %s' % (item['media_index'], item['title'])
|
||||
else:
|
||||
full_title = item['title']
|
||||
|
||||
watched_item = watched_list.get(item['rating_key'], None)
|
||||
if watched_item:
|
||||
last_watched = watched_item['last_watched']
|
||||
play_count = watched_item['play_count']
|
||||
else:
|
||||
last_watched = None
|
||||
play_count = None
|
||||
|
||||
row = {'section_id': library_details['section_id'],
|
||||
'section_type': library_details['section_type'],
|
||||
'last_watched': last_watched,
|
||||
'added_at': item['added_at'],
|
||||
'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': full_title,
|
||||
'title': item['title'],
|
||||
'year': item['year'],
|
||||
'media_index': item['media_index'],
|
||||
'parent_media_index': item['parent_media_index'],
|
||||
'thumb': thumb,
|
||||
'container': item.get('container', ''),
|
||||
'bitrate': item.get('bitrate', ''),
|
||||
'video_codec': item.get('video_codec', ''),
|
||||
'video_resolution': item.get('video_resolution', ''),
|
||||
'video_framerate': item.get('video_framerate', ''),
|
||||
'audio_codec': item.get('audio_codec', ''),
|
||||
'audio_channels': item.get('audio_channels', ''),
|
||||
'file_size': item.get('file_size', ''),
|
||||
'play_count': play_count
|
||||
}
|
||||
rows.append(row)
|
||||
|
||||
if rating_key:
|
||||
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info-%s_%s.json' % (section_id, rating_key))
|
||||
with open(outFilePath, 'w') as outFile:
|
||||
json.dump(rows, outFile)
|
||||
elif section_id:
|
||||
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info-%s.json' % section_id)
|
||||
with open(outFilePath, 'w') as outFile:
|
||||
json.dump(rows, outFile)
|
||||
|
||||
results = []
|
||||
|
||||
# Get datatables JSON data
|
||||
if kwargs.get('json_data'):
|
||||
json_data = helpers.process_json_kwargs(json_kwargs=kwargs.get('json_data'))
|
||||
#print json_data
|
||||
|
||||
# Search results
|
||||
search_value = json_data['search']['value'].lower()
|
||||
if search_value:
|
||||
searchable_columns = [d['data'] for d in json_data['columns'] if d['searchable']]
|
||||
for row in rows:
|
||||
for k,v in row.iteritems():
|
||||
if k in searchable_columns and search_value in v.lower():
|
||||
results.append(row)
|
||||
break
|
||||
else:
|
||||
results = rows
|
||||
|
||||
filtered_count = len(results)
|
||||
|
||||
# Sort results
|
||||
sort_order = json_data['order']
|
||||
for order in reversed(sort_order):
|
||||
sort_key = json_data['columns'][int(order['column'])]['data']
|
||||
reverse = True if order['dir'] == 'desc' else False
|
||||
if rating_key and sort_key == 'title':
|
||||
results = sorted(results, key=lambda k: int(k['media_index']), reverse=reverse)
|
||||
else:
|
||||
results = sorted(results, key=lambda k: k[sort_key], reverse=reverse)
|
||||
|
||||
# Paginate results
|
||||
results = results[json_data['start']:(json_data['start'] + json_data['length'])]
|
||||
|
||||
dict = {'recordsFiltered': filtered_count,
|
||||
'recordsTotal': library_count,
|
||||
'data': results,
|
||||
'draw': int(json_data['draw'])
|
||||
}
|
||||
|
||||
## Add total disk space used
|
||||
return dict
|
||||
|
||||
def set_config(self, section_id=None, custom_thumb='', do_notify=1, keep_history=1, do_notify_created=1):
|
||||
if section_id:
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
@ -234,7 +519,7 @@ class Libraries(object):
|
|||
'keep_history': 0
|
||||
}
|
||||
|
||||
def get_watch_time_stats(self, library_id=None):
|
||||
def get_watch_time_stats(self, section_id=None):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
time_queries = [1, 7, 30, 0]
|
||||
|
@ -243,26 +528,26 @@ class Libraries(object):
|
|||
for days in time_queries:
|
||||
try:
|
||||
if days > 0:
|
||||
if str(library_id).isdigit():
|
||||
if str(section_id).isdigit():
|
||||
query = 'SELECT (SUM(stopped - started) - ' \
|
||||
'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
|
||||
'COUNT(session_history.id) AS total_plays ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND library_id = ?' % days
|
||||
result = monitor_db.select(query, args=[library_id])
|
||||
'AND section_id = ?' % days
|
||||
result = monitor_db.select(query, args=[section_id])
|
||||
else:
|
||||
result = []
|
||||
else:
|
||||
if str(library_id).isdigit():
|
||||
if str(section_id).isdigit():
|
||||
query = 'SELECT (SUM(stopped - started) - ' \
|
||||
'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
|
||||
'COUNT(session_history.id) AS total_plays ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE library_id = ?'
|
||||
result = monitor_db.select(query, args=[library_id])
|
||||
'WHERE section_id = ?'
|
||||
result = monitor_db.select(query, args=[section_id])
|
||||
else:
|
||||
result = []
|
||||
except Exception as e:
|
||||
|
@ -286,22 +571,22 @@ class Libraries(object):
|
|||
|
||||
return library_watch_time_stats
|
||||
|
||||
def get_user_stats(self, library_id=None):
|
||||
def get_user_stats(self, section_id=None):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
user_stats = []
|
||||
|
||||
try:
|
||||
if str(library_id).isdigit():
|
||||
if str(section_id).isdigit():
|
||||
query = 'SELECT (CASE WHEN users.friendly_name IS NULL THEN users.username ' \
|
||||
'ELSE users.friendly_name END) AS user, users.user_id, users.thumb, COUNT(user) AS user_count ' \
|
||||
'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 library_id = ? ' \
|
||||
'WHERE section_id = ? ' \
|
||||
'GROUP BY user ' \
|
||||
'ORDER BY user_count DESC'
|
||||
result = monitor_db.select(query, args=[library_id])
|
||||
result = monitor_db.select(query, args=[section_id])
|
||||
else:
|
||||
result = []
|
||||
except Exception as e:
|
||||
|
@ -318,7 +603,7 @@ class Libraries(object):
|
|||
|
||||
return user_stats
|
||||
|
||||
def get_recently_watched(self, library_id=None, limit='10'):
|
||||
def get_recently_watched(self, section_id=None, limit='10'):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
recently_watched = []
|
||||
|
||||
|
@ -326,17 +611,17 @@ class Libraries(object):
|
|||
limit = '10'
|
||||
|
||||
try:
|
||||
if str(library_id).isdigit():
|
||||
if str(section_id).isdigit():
|
||||
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
|
||||
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
|
||||
'year, started, user ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE library_id = ? ' \
|
||||
'WHERE section_id = ? ' \
|
||||
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
|
||||
' ELSE session_history.rating_key END) ' \
|
||||
'ORDER BY started DESC LIMIT ?'
|
||||
result = monitor_db.select(query, args=[library_id, limit])
|
||||
result = monitor_db.select(query, args=[section_id, limit])
|
||||
else:
|
||||
result = []
|
||||
except Exception as e:
|
||||
|
@ -399,18 +684,18 @@ class Libraries(object):
|
|||
'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id '
|
||||
'FROM session_history_media_info '
|
||||
'JOIN session_history_metadata ON session_history_media_info.id = session_history_metadata.id '
|
||||
'WHERE session_history_metadata.library_id = ?)', [section_id])
|
||||
'WHERE session_history_metadata.section_id = ?)', [section_id])
|
||||
session_history_del = \
|
||||
monitor_db.action('DELETE FROM '
|
||||
'session_history '
|
||||
'WHERE session_history.id IN (SELECT session_history.id '
|
||||
'FROM session_history '
|
||||
'JOIN session_history_metadata ON session_history.id = session_history_metadata.id '
|
||||
'WHERE session_history_metadata.library_id = ?)', [section_id])
|
||||
'WHERE session_history_metadata.section_id = ?)', [section_id])
|
||||
session_history_metadata_del = \
|
||||
monitor_db.action('DELETE FROM '
|
||||
'session_history_metadata '
|
||||
'WHERE session_history_metadata.library_id = ?', [section_id])
|
||||
'WHERE session_history_metadata.section_id = ?', [section_id])
|
||||
|
||||
return 'Deleted all items for section_id %s.' % section_id
|
||||
else:
|
||||
|
@ -465,4 +750,50 @@ class Libraries(object):
|
|||
else:
|
||||
return 'Unable to re-add library, section_id or section_name not valid.'
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to execute database query for undelete: %s." % e)
|
||||
logger.warn(u"PlexPy Libraries :: Unable to execute database query for undelete: %s." % e)
|
||||
|
||||
def update_section_ids(self):
|
||||
from plexpy import pmsconnect
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
try:
|
||||
query = 'SELECT id, rating_key FROM session_history_metadata WHERE section_id IS NULL'
|
||||
result = monitor_db.select(query=query)
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to execute database query for update_section_ids: %s." % e)
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
id = item['id']
|
||||
rating_key = item['rating_key']
|
||||
|
||||
result = pms_connect.get_metadata_details(rating_key=rating_key)
|
||||
|
||||
if result:
|
||||
metadata = result['metadata']
|
||||
|
||||
section_keys = {'id': id}
|
||||
section_values = {'section_id': metadata['section_id']}
|
||||
|
||||
monitor_db.upsert('session_history_metadata', key_dict=section_keys, value_dict=section_values)
|
||||
else:
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
def delete_datatable_media_info_cache(self, section_id=None):
|
||||
import os
|
||||
|
||||
try:
|
||||
if section_id.isdigit():
|
||||
[os.remove(os.path.join(plexpy.CONFIG.CACHE_DIR, f)) for f in os.listdir(plexpy.CONFIG.CACHE_DIR)
|
||||
if f.startswith('media_info-%s' % section_id) and f.endswith('.json')]
|
||||
|
||||
logger.debug(u"PlexPy Libraries :: Deleted media info table cache for section_id %s." % section_id)
|
||||
return 'Deleted media info table cache for library with id %s.' % section_id
|
||||
else:
|
||||
return 'Unable to delete media info table cache, section_id not valid.'
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to delete media info table cache: %s." % e)
|
Loading…
Add table
Add a link
Reference in a new issue