diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index 60f42f8a..c58ae816 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -9,7 +9,7 @@ a { } a:hover, a:focus { - color: #e9a049; + color: #f9be03; text-decoration: none; outline: none; } @@ -974,6 +974,264 @@ a .dashboard-activity-metadata-user-thumb:hover { .dashboard-activity-metadata-title a:hover { color: #e9a049; } +.dashboard-stats-instance { + float: left; + position: relative; + height: 160px; + min-width: 350px; + max-width: 500px; + margin-right: 20px; + margin-bottom: 20px; +} +.dashboard-stats-container { + height: 160px; + width: 100%; + position: relative; + padding: 0px; + -webkit-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; + overflow: hidden; +} +.dashboard-stats-background-overlay { + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: nowrap; + flex-wrap: nowrap; + height: 160px; + width: 100%; + padding: 5px; + overflow: hidden; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); +} +.dashboard-stats-background { + background-color: #282828; + background-position: center; + background-size: cover; + height: 160px; + width: 100%; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 0.40; + -webkit-filter: blur(3px); + -moz-filter: blur(3px); + filter: blur(3px); + -webkit-transition: background .2s linear; + transition: background .2s linear; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + z-index: -1; +} +.dashboard-stats-background.flat { + opacity: 1; +} +.dashboard-stats-poster { + background-position: center; + background-size: cover; + height: 150px; + width: 100px; + margin-right: 5px; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -webkit-flex-shrink: 0; + flex-shrink: 0; + -webkit-transition: background .2s linear; + transition: background .2s linear; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + z-index: 1; +} +.dashboard-stats-cover { + background-position: center; + background-size: cover; + height: 100px; + width: 100px; + margin-top: 25px; + margin-right: 5px; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -webkit-flex-shrink: 0; + flex-shrink: 0; + -webkit-transition: background .2s linear; + transition: background .2s linear; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + z-index: 1; +} +.dashboard-stats-circle { + background-position: center; + background-size: cover; + height: 100px; + width: 100px; + margin-top: 25px; + margin-right: 5px; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -webkit-flex-shrink: 0; + flex-shrink: 0; + -webkit-transition: background .2s linear; + transition: background .2s linear; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + z-index: 1; + -webkit-border-radius: 50%; + -moz-border-radius: 50%; + border-radius: 350%; + overflow: hidden; +} +.dashboard-stats-square { + background-position: center; + background-size: cover; + height: 100px; + width: 100px; + margin-top: 25px; + margin-right: 5px; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + box-shadow: 0 0 4px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.1); + -webkit-flex-shrink: 0; + flex-shrink: 0; + -webkit-transition: background .2s linear; + transition: background .2s linear; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + z-index: 1; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + overflow: hidden; +} +.dashboard-stats-info-container { + display: -webkit-flex; + display: flex; + flex-direction: column; + height: 150px; + width: 385px; + overflow: hidden; +} +.dashboard-stats-info-title { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + height: 30px; + width: 385px; + padding: 5px 5px 5px 15px; + line-height: 20px; + border-bottom: 1px solid rgba(255,255,255,.1); + -webkit-flex-grow: 1; + flex-grow: 1; + z-index: 1; +} +.dashboard-stats-info-title h4 { + margin: 0; + -webkit-flex-grow: 1; + flex-grow: 1; +} +.dashboard-stats-info-title .dashboard-stats-stats-units { + color: #aaa; + font-size: 12px; + text-align: right; + text-transform: uppercase; +} +.dashboard-stats-info-scroller { + height: 120px; + width: 385px; + -webkit-flex-grow: 1; + flex-grow: 1; + z-index: 1; +} +.dashboard-stats-info-scroller.scrollbar-macosx > .scroll-element.scroll-y { + left: 10px; +} +.dashboard-stats-info-scroller.scrollbar-macosx > .scroll-element .scroll-bar { + background-color: #999; +} +.dashboard-stats-info { + width: 100%; + font-size: 12px; + padding: 5px 0 5px 15px; + position: relative; +} +.dashboard-stats-info-list { + margin-bottom: 20px; + padding-right: 0; +} +.dashboard-stats-info-list:last-of-type { + margin-bottom: 0; +} +.dashboard-stats-info-item { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + width: 100%; + padding: 2.5px 5px; + line-height: 14px; + border-bottom: 1px solid rgba(255,255,255,0.05); +} +.dashboard-stats-info-item:last-of-type { + margin-bottom: 0; +} +.dashboard-stats-info-item .sub-heading { + height: 100%; + width: 6px; + color: #aaa; + font-size: 10px; + text-align: right; + text-transform: uppercase; +} +.dashboard-stats-info-item .sub-value { + height: 100%; + margin-left: 10px; + font-size: 12px; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + -webkit-flex-grow: 1; + flex-grow: 1; +} +.dashboard-stats-info-item .sub-count { + height: 100%; + margin-left: 10px; + color: #f9be03; + font-size: 12px; + text-align: right; + white-space: nowrap; + overflow: hidden; +} +.dashboard-stats-info-item:first-of-type { + padding: 5px 5px; + line-height: 20px; +} +.dashboard-stats-info-item:first-of-type .sub-heading { + font-size: 13px; +} +.dashboard-stats-info-item:first-of-type .sub-value { + font-size: 16px; +} +.dashboard-stats-info-item:first-of-type .sub-count { + font-size: 16px; +} +.dashboard-stats-info-item:hover { + background-color: rgba(255,255,255,0.05); +} +a:hover .dashboard-stats-poster, +a:hover .dashboard-stats-cover, +a:hover .dashboard-stats-circle, +a:hover .dashboard-stats-square { + -webkit-box-shadow: inset 0 0 0 2px #e9a049; + -moz-box-shadow: inset 0 0 0 2px #e9a049; + box-shadow: inset 0 0 0 2px #e9a049; +} #dashboard-no-recently-added { margin-bottom: 20px; } diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 88f75a53..bd85a9eb 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -57,1035 +57,142 @@ DOCUMENTATION :: END # Human readable duration def hd(seconds): - minutes = helpers.cast_to_float(seconds) / 60 - if minutes > 60: - hours = int(minutes / 60) - minutes = int(minutes % 60) - if minutes > 0: - return "

" + str(hours) + "

hrs

" + str(minutes) + "

mins

" - else: - return "

" + str(hours) + "

hrs

" - else: - return "

" + str(int(minutes)) + "

mins

" + m, s = divmod(helpers.cast_to_int(seconds), 60) + h, m = divmod(m, 60) + return str(h).zfill(1) + ':' + str(m).zfill(2) %> -% if data: - + +% endfor % else: diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 5642210c..86f9618f 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -281,104 +281,7 @@ class DataFactory(object): home_stats = [] for stat in stats_cards: - if stat == 'top_tv': - top_tv = [] - try: - query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \ - 't.art, t.media_type, t.content_rating, t.labels, t.started, ' \ - 'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.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 ' \ - ' JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ - ' WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ - ' >= datetime("now", "-%s days", "localtime") ' \ - ' AND session_history.media_type = "episode" ' \ - ' GROUP BY %s) AS t ' \ - 'GROUP BY t.grandparent_title ' \ - 'ORDER BY %s DESC, started DESC ' \ - 'LIMIT %s ' % (time_range, group_by, sort_type, stats_count) - result = monitor_db.select(query) - except Exception as e: - logger.warn(u"PlexPy 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'], - 'total_plays': item['total_plays'], - 'total_duration': item['total_duration'], - 'users_watched': '', - 'rating_key': item['grandparent_rating_key'], - 'last_play': item['last_watch'], - 'grandparent_thumb': item['grandparent_thumb'], - '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_type': '', - 'platform': '', - 'row_id': item['id'] - } - top_tv.append(row) - - home_stats.append({'stat_id': stat, - 'stat_type': sort_type, - 'rows': session.mask_session_info(top_tv)}) - - elif stat == 'popular_tv': - popular_tv = [] - try: - query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \ - 't.art, t.media_type, t.content_rating, t.labels, t.started, ' \ - 'COUNT(DISTINCT t.user_id) AS users_watched, ' \ - 'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.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 ' \ - ' JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ - ' WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ - ' >= datetime("now", "-%s days", "localtime") ' \ - ' AND session_history.media_type = "episode" ' \ - ' GROUP BY %s) AS t ' \ - 'GROUP BY t.grandparent_title ' \ - 'ORDER BY users_watched DESC, %s DESC, started DESC ' \ - 'LIMIT %s ' % (time_range, group_by, sort_type, stats_count) - result = monitor_db.select(query) - except Exception as e: - logger.warn(u"PlexPy 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'], - 'users_watched': item['users_watched'], - 'rating_key': item['grandparent_rating_key'], - 'last_play': item['last_watch'], - 'total_plays': item['total_plays'], - 'grandparent_thumb': item['grandparent_thumb'], - '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_type': '', - 'platform': '', - 'row_id': item['id'] - } - popular_tv.append(row) - - home_stats.append({'stat_id': stat, - 'rows': session.mask_session_info(popular_tv)}) - - elif stat == 'top_movies': + if stat == 'top_movies': top_movies = [] try: query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \ @@ -425,6 +328,7 @@ class DataFactory(object): 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': @@ -473,8 +377,108 @@ class DataFactory(object): 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 t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \ + 't.art, t.media_type, t.content_rating, t.labels, t.started, ' \ + 'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.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 ' \ + ' JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ + ' WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ + ' >= datetime("now", "-%s days", "localtime") ' \ + ' AND session_history.media_type = "episode" ' \ + ' GROUP BY %s) AS t ' \ + 'GROUP BY t.grandparent_title ' \ + 'ORDER BY %s DESC, started DESC ' \ + 'LIMIT %s ' % (time_range, group_by, sort_type, stats_count) + result = monitor_db.select(query) + except Exception as e: + logger.warn(u"PlexPy 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'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], + 'users_watched': '', + '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_type': '', + 'platform': '', + '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 t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \ + 't.art, t.media_type, t.content_rating, t.labels, t.started, ' \ + 'COUNT(DISTINCT t.user_id) AS users_watched, ' \ + 'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.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 ' \ + ' JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ + ' WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ + ' >= datetime("now", "-%s days", "localtime") ' \ + ' AND session_history.media_type = "episode" ' \ + ' GROUP BY %s) AS t ' \ + 'GROUP BY t.grandparent_title ' \ + 'ORDER BY users_watched DESC, %s DESC, started DESC ' \ + 'LIMIT %s ' % (time_range, group_by, sort_type, stats_count) + result = monitor_db.select(query) + except Exception as e: + logger.warn(u"PlexPy 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'], + 'users_watched': item['users_watched'], + '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_type': '', + 'platform': '', + '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: @@ -506,7 +510,7 @@ class DataFactory(object): 'rating_key': item['grandparent_rating_key'], 'last_play': item['last_watch'], 'grandparent_thumb': item['grandparent_thumb'], - 'thumb': '', + 'thumb': item['grandparent_thumb'], 'art': item['art'], 'section_id': item['section_id'], 'media_type': item['media_type'], @@ -522,6 +526,7 @@ class DataFactory(object): 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': @@ -555,7 +560,7 @@ class DataFactory(object): 'last_play': item['last_watch'], 'total_plays': item['total_plays'], 'grandparent_thumb': item['grandparent_thumb'], - 'thumb': '', + 'thumb': item['grandparent_thumb'], 'art': item['art'], 'section_id': item['section_id'], 'media_type': item['media_type'], @@ -570,6 +575,7 @@ class DataFactory(object): 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_users': @@ -610,8 +616,10 @@ class DataFactory(object): 'total_plays': item['total_plays'], 'total_duration': item['total_duration'], 'last_play': item['last_watch'], + 'thumb': user_thumb, 'user_thumb': user_thumb, 'grandparent_thumb': '', + 'art': '', 'users_watched': '', 'rating_key': '', 'title': '', @@ -623,6 +631,7 @@ class DataFactory(object): home_stats.append({'stat_id': stat, 'stat_type': sort_type, + 'stat_title': 'Most Active Users', 'rows': session.mask_session_info(top_users, mask_metadata=False)}) elif stat == 'top_platforms': @@ -659,6 +668,7 @@ class DataFactory(object): 'title': '', 'thumb': '', 'grandparent_thumb': '', + 'art': '', 'users_watched': '', 'rating_key': '', 'user': '', @@ -669,6 +679,7 @@ class DataFactory(object): 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': @@ -727,6 +738,7 @@ class DataFactory(object): last_watched.append(row) home_stats.append({'stat_id': stat, + 'stat_title': 'Last Watched Items', 'rows': session.mask_session_info(last_watched)}) elif stat == 'most_concurrent': @@ -807,6 +819,7 @@ class DataFactory(object): return None home_stats.append({'stat_id': stat, + 'stat_title': 'Most Concurrent Streams', 'rows': most_concurrent}) return home_stats