Add season list to show page, and watch history to season page

This commit is contained in:
Jonathan Wong 2015-08-27 00:48:10 -07:00
parent 53830a7711
commit 60380c6a99
7 changed files with 329 additions and 72 deletions

View file

@ -1008,17 +1008,102 @@ a .summary-poster-face-episode:hover {
background-size: contain; background-size: contain;
height: 16px; height: 16px;
} }
.show-seasons-wrapper {
}
.show-seasons-instance {
list-style: none;
margin: 0;
}
.show-seasons-instance li {
float: left;
position: relative;
left: 0px;
margin-right: 25px;
margin-bottom: 25px;
}
a .show-seasons-card-overlay:hover {
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;
}
.show-seasons-poster {
float: left;
position: relative;
left: 0px;
}
.show-seasons-card-overlay {
position: absolute;
left: 0;
right: 0;
bottom: 0;
text-align: left;
background: -moz-linear-gradient(top, rgba(0,0,0,0) 30%, rgba(0,0,0,1) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(30%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1)));
background: -webkit-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%);
background: -o-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%);
background: -ms-linear-gradient(top, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%);
background: linear-gradient(to bottom, rgba(0,0,0,0) 30%,rgba(0,0,0,1) 100%);
height: 225px;
}
.show-seasons-overlay-text {
color: #aaa;
font-size: 12px;
float: left;
position: absolute;
left: 8px;
bottom: 5px;
}
.show-seasons-instance-text-wrapper {
width: 150px;
font-size: 13px;
margin-bottom: 20px;
clear: both;
}
.show-seasons-instance-text-wrapper h3 {
padding: 5px 3px 0 3px;
color: #fff;
text-overflow: ellipsis;
overflow: hidden;
position: relative;
font-size: 13px;
margin: 0;
line-height: 15px;
font-weight: normal;
width: 250px;
white-space: nowrap;
text-align: left;
clear: both;
}
.show-seasons-title a {
text-decoration: none;
font-size: 14px;
font-weight: normal;
color: #fff;
float: left;
text-overflow: ellipsis;
overflow: hidden;
position: relative;
white-space: nowrap;
width: 205px;
margin-top: 2px;
margin-left: 0px;
margin-bottom: 20px;
}
.show-seasons a:hover {
color: #F9AA03;
}
.season-episodes-wrapper { .season-episodes-wrapper {
} }
.season-episodes-instance { .season-episodes-instance {
list-style: none; list-style: none;
margin: 0 0 0px 0px; margin: 0;
} }
.season-episodes-instance li { .season-episodes-instance li {
float: left; float: left;
position: relative; position: relative;
left: 0px; left: 0px;
margin-right: 25px; margin-right: 25px;
margin-bottom: 25px;
} }
a .season-episodes-card-overlay:hover { a .season-episodes-card-overlay:hover {
webkit-box-shadow: inset 0 0 0 2px #e9a049; webkit-box-shadow: inset 0 0 0 2px #e9a049;
@ -1059,11 +1144,13 @@ a .season-episodes-card-overlay:hover {
font-size: 11px; font-size: 11px;
text-shadow: 0 1px 5px rgba(0,0,0,0.2); text-shadow: 0 1px 5px rgba(0,0,0,0.2);
} }
.season-episodes-instance-text-wrapper { .season-episodes-overlay-text {
width: 250px; color: #aaa;
font-size: 13px; font-size: 12px;
margin-bottom: 20px; float: left;
clear: both; position: absolute;
left: 8px;
bottom: 5px;
} }
.season-episodes-instance-text-wrapper h3 { .season-episodes-instance-text-wrapper h3 {
padding: 5px 3px 0 3px; padding: 5px 3px 0 3px;
@ -1098,14 +1185,6 @@ a .season-episodes-card-overlay:hover {
.season-episodes a:hover { .season-episodes a:hover {
color: #F9AA03; color: #F9AA03;
} }
.season-episodes-season {
color: #aaa;
font-size: 12px;
float: left;
position: absolute;
left: 8px;
bottom: 5px;
}
.user-info-wrapper { .user-info-wrapper {
height: 113px; height: 113px;
} }

View file

@ -197,7 +197,30 @@ DOCUMENTATION :: END
% endif % endif
</div> </div>
</div> </div>
% if data['type'] == 'movie' or data['type'] == 'episode' or data['type'] == 'show': % if data['type'] == 'show':
<div class='col-md-12'>
<div class='table-card-header'>
<div class="header-bar">
<span>Season List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="season-list"></div>
</div>
</div>
% elif data['type'] == 'season':
<div class='col-md-12'>
<div class='table-card-header'>
<div class="header-bar">
<span>Episode List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="episode-list"></div>
</div>
</div>
% endif
% if data['type'] == 'movie' or data['type'] == 'episode' or data['type'] == 'show' or data['type'] == 'season':
<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">
@ -233,18 +256,7 @@ DOCUMENTATION :: END
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal"> <div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
</div> </div>
</div> </div>
% elif data['type'] == 'season': % endif
<div class='col-md-12'>
<div class='table-card-header'>
<div class="header-bar">
<span>Episode List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="episode-list"></div>
</div>
% endif
</div>
</div> </div>
</div> </div>
</div> </div>
@ -288,28 +300,27 @@ DOCUMENTATION :: END
type: 'post', type: 'post',
data: function ( d ) { data: function ( d ) {
return { 'json_data': JSON.stringify( d ), return { 'json_data': JSON.stringify( d ),
'rating_key': ${data['rating_key']} 'rating_key': ${data['rating_key']} };
}; }
} }
}
history_table = $('#history_table').DataTable(history_table_options); history_table = $('#history_table').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] }); var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
$(colvis.button()).appendTo('div.colvis-button-bar'); $(colvis.button()).appendTo('div.colvis-button-bar');
clearSearchButton('history_table', history_table); clearSearchButton('history_table', history_table);
$('#row-edit-mode').click(function() { $('#row-edit-mode').click(function() {
if ($(this).hasClass('active')) { if ($(this).hasClass('active')) {
$('.delete-control').each(function() { $('.delete-control').each(function() {
$(this).addClass('hidden'); $(this).addClass('hidden');
}); });
} else { } else {
$('.delete-control').each(function() { $('.delete-control').each(function() {
$(this).removeClass('hidden'); $(this).removeClass('hidden');
}); });
} }
}); });
}); });
</script> </script>
% elif data['type'] == 'show': % elif data['type'] == 'show':
@ -321,40 +332,75 @@ DOCUMENTATION :: END
type: 'post', type: 'post',
data: function ( d ) { data: function ( d ) {
return { 'json_data': JSON.stringify( d ), return { 'json_data': JSON.stringify( d ),
'grandparent_rating_key': ${data['rating_key']} 'grandparent_rating_key': ${data['rating_key']} };
}; }
}
}
history_table = $('#history_table').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
$(colvis.button()).appendTo('div.colvis-button-bar');
clearSearchButton('history_table', history_table);
$('#row-edit-mode').click(function() {
if ($(this).hasClass('active')) {
$('.delete-control').each(function() {
$(this).addClass('hidden');
});
} else {
$('.delete-control').each(function() {
$(this).removeClass('hidden');
});
} }
history_table = $('#history_table').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
$(colvis.button()).appendTo('div.colvis-button-bar');
clearSearchButton('history_table', history_table);
$('#row-edit-mode').click(function() {
if ($(this).hasClass('active')) {
$('.delete-control').each(function() {
$(this).addClass('hidden');
});
} else {
$('.delete-control').each(function() {
$(this).removeClass('hidden');
});
}
});
}); });
$.ajax({
url: 'get_show_children',
type: "GET",
async: true,
data: { rating_key : ${data['rating_key']} },
complete: function(xhr, status) {
$("#season-list").html(xhr.responseText); }
}); });
</script> </script>
% endif % endif
% if data['type'] == 'season': % if data['type'] == 'season':
<script src="interfaces/default/js/tables/history_table.js"></script>
<script> <script>
$(document).ready(function () {
history_table_options.ajax = {
"url": "get_history",
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'parent_rating_key': ${data['rating_key']} };
}
}
history_table = $('#history_table').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 10] });
$(colvis.button()).appendTo('div.colvis-button-bar');
clearSearchButton('history_table', history_table);
$('#row-edit-mode').click(function() {
if ($(this).hasClass('active')) {
$('.delete-control').each(function() {
$(this).addClass('hidden');
});
} else {
$('.delete-control').each(function() {
$(this).removeClass('hidden');
});
}
});
});
$.ajax({ $.ajax({
url: 'get_children', url: 'get_season_children',
type: "GET", type: "GET",
async: true, async: true,
data: { rating_key : ${data['rating_key']} }, data: { rating_key : ${data['rating_key']} },
complete: function(xhr, status) { complete: function(xhr, status) {
$("#episode-list").html(xhr.responseText); $("#episode-list").html(xhr.responseText); }
}
}); });
</script> </script>
% endif % endif

View file

@ -34,7 +34,7 @@ DOCUMENTATION :: END
<div class="season-episodes-poster"> <div class="season-episodes-poster">
<div class="season-episodes-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280);"> <div class="season-episodes-poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=500&height=280);">
<div class="season-episodes-card-overlay"> <div class="season-episodes-card-overlay">
<div class="season-episodes-season"> <div class="season-episodes-overlay-text">
Episode ${a['index']} Episode ${a['index']}
</div> </div>
</div> </div>

View file

@ -0,0 +1,55 @@
<%doc>
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
Filename: info_season_list.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
== Global keys ==
season_count Returns the number of seasons in the array.
season_list Returns an array of seasons.
data['season_list'] :: Usable paramaters
== Global keys ==
rating_key Returns the unique identifier for the media item.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
title Returns the name of the season.
index Returns the season number.
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data['season_count'] > 0:
<div class="show-seasons-wrapper">
<ul class="show-seasons-instance list-unstyled">
% for a in data['season_list']:
% if a['rating_key']:
<li>
<a href="info?item_id=${a['rating_key']}">
<div class="show-seasons-poster">
% if a['thumb']:
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['thumb']}&width=300&height=450);">
% else:
<div class="poster-face" style="background-image: url(pms_image_proxy?img=${a['parent_thumb']}&width=300&height=450);">
% endif
<div class="show-seasons-card-overlay">
<div class="show-seasons-overlay-text">
Season ${a['index']}
</div>
</div>
</div>
</div>
</a>
</li>
% endif
% endfor
</ul>
</div>
% endif
% endif

View file

@ -55,6 +55,7 @@ class DataFactory(object):
(CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \ (CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \
session_history_metadata.duration * 1.0 END) * 100) as percent_complete', session_history_metadata.duration * 1.0 END) * 100) as percent_complete',
'session_history.grandparent_rating_key as grandparent_rating_key', 'session_history.grandparent_rating_key as grandparent_rating_key',
'session_history.parent_rating_key as parent_rating_key',
'session_history.rating_key as rating_key', 'session_history.rating_key as rating_key',
'session_history.user', 'session_history.user',
'session_history_metadata.media_type', 'session_history_metadata.media_type',
@ -112,6 +113,7 @@ class DataFactory(object):
"duration": item["duration"], "duration": item["duration"],
"percent_complete": item["percent_complete"], "percent_complete": item["percent_complete"],
"grandparent_rating_key": item["grandparent_rating_key"], "grandparent_rating_key": item["grandparent_rating_key"],
"parent_rating_key": item["parent_rating_key"],
"rating_key": item["rating_key"], "rating_key": item["rating_key"],
"user": item["user"], "user": item["user"],
"media_type": item["media_type"], "media_type": item["media_type"],

View file

@ -89,6 +89,23 @@ class PmsConnect(object):
return request return request
"""
Return list of seasons in requested show.
Parameters required: rating_key { ratingKey of parent }
Optional parameters: output_format { dict, json }
Output: array
"""
def get_season_list(self, rating_key='', output_format=''):
uri = '/library/metadata/' + rating_key + '/children'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
""" """
Return list of episodes in requested season. Return list of episodes in requested season.
@ -798,6 +815,49 @@ class PmsConnect(object):
return session_output return session_output
"""
Return processed and validated season list.
Output: array
"""
def get_show_children(self, rating_key=''):
season_data = self.get_season_list(rating_key, output_format='xml')
try:
xml_head = season_data.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_season_list.")
return []
season_list = []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
logger.debug(u"No season data.")
episode_list = {'season_count': '0',
'season_list': []
}
return season_list
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
season_output = {'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
'index': helpers.get_xml_attr(result, 'index'),
'title': helpers.get_xml_attr(result, 'title'),
'thumb': helpers.get_xml_attr(result, 'thumb'),
'parent_thumb': helpers.get_xml_attr(a, 'thumb')
}
season_list.append(season_output)
output = {'season_count': helpers.get_xml_attr(xml_head[0], 'size'),
'title': helpers.get_xml_attr(xml_head[0], 'title2'),
'season_list': season_list
}
return output
""" """
Return processed and validated episode list. Return processed and validated episode list.

View file

@ -1,4 +1,4 @@
# This file is part of PlexPy. # This file is part of PlexPy.
# #
# PlexPy is free software: you can redistribute it and/or modify # PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -567,6 +567,9 @@ class WebInterface(object):
if 'rating_key' in kwargs: if 'rating_key' in kwargs:
rating_key = kwargs.get('rating_key', "") rating_key = kwargs.get('rating_key', "")
custom_where = [['rating_key', rating_key]] custom_where = [['rating_key', rating_key]]
if 'parent_rating_key' in kwargs:
rating_key = kwargs.get('parent_rating_key', "")
custom_where = [['parent_rating_key', rating_key]]
if 'grandparent_rating_key' in kwargs: if 'grandparent_rating_key' in kwargs:
rating_key = kwargs.get('grandparent_rating_key', "") rating_key = kwargs.get('grandparent_rating_key', "")
custom_where = [['grandparent_rating_key', rating_key]] custom_where = [['grandparent_rating_key', rating_key]]
@ -804,7 +807,19 @@ class WebInterface(object):
return serve_template(templatename="user_platform_stats.html", data=None, title="Platform Stats") return serve_template(templatename="user_platform_stats.html", data=None, title="Platform Stats")
@cherrypy.expose @cherrypy.expose
def get_children(self, rating_key='', **kwargs): def get_show_children(self, rating_key='', **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_show_children(rating_key)
if result:
return serve_template(templatename="info_season_list.html", data=result, title="Season List")
else:
logger.warn('Unable to retrieve data.')
return serve_template(templatename="info_season_list.html", data=None, title="Season List")
@cherrypy.expose
def get_season_children(self, rating_key='', **kwargs):
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_season_children(rating_key) result = pms_connect.get_season_children(rating_key)