Add media info table to library page

This commit is contained in:
Jonathan Wong 2016-01-10 13:35:20 -08:00
parent 10e4d562ab
commit 381c3da31c
28 changed files with 1415 additions and 462 deletions

View file

@ -71,9 +71,20 @@ img {
-moz-box-shadow: 0 0 0 3px rgba(0,0,0,.2);
box-shadow: 0 0 0 3px rgba(0,0,0,.2);
}
.navbar-header {
position: absolute;
top: 0;
left: 15px;
width: 100%;
}
.navbar-brand {
padding: 5px 5px;
}
.navbar-right {
position: absolute;
top: 0;
right: 15px;
}
.nav > li > a {
color: #999;
-webkit-transition: all 0.3s ease;
@ -2542,12 +2553,22 @@ a .home-platforms-instance-list-oval:hover,
width: 100%;
}
}
table.display,
table.display tr.shown + tr table[id^='history_child'],
table.display tr.shown + tr table[id^='media_info_child'],
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] {
table-layout: fixed;
}
table.display.no-fixed {
table-layout: auto;
}
table.display tr.shown + tr div.slider {
display: none;
}
table.display tr.shown + tr > td {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
table.display tr.shown + tr:hover {
background-color: rgba(255,255,255,0);
@ -2558,7 +2579,9 @@ table.display tr.shown + tr .pagination > .active > a,
table.display tr.shown + tr .pagination > .active > a:hover {
color: #fff;
}
table.display tr.shown + tr table[id^='history_child'] td:hover a {
table.display tr.shown + tr table[id^='history_child'] td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] > tr > td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] td:hover a {
color: #F9AA03;
}
table.display tr.shown + tr .pagination > .disabled > a {
@ -2569,14 +2592,22 @@ table.display tr.shown + tr .pagination > li > a:hover {
}
table[id^='history_child'] {
margin-top: 0;
margin-left: -4px;
opacity: .6;
}
table[id^='history_child'] thead th {
table[id^='media_info_child'] {
margin-top: 0;
}
table[id^='history_child'] thead th,
table[id^='media_info_child'] thead th {
line-height: 0;
height: 0 !important;
overflow: hidden;
}
table[id^='media_info_child'] table[id^='media_info_child'] thead th {
line-height: 25px;
height: 35px !important;
overflow: hidden;
}
#search_form {
width: 300px;
padding: 8px 15px;

View file

@ -25,18 +25,18 @@
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="delete_row">Delete</th>
<th align='left' id="time">Time</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="ip_address">IP Address</th>
<th align='left' id="platform">Platform</th>
<th align='left' id="device">Player</th>
<th align='left' id="title">Title</th>
<th align='left' id="started">Started</th>
<th align='left' id="paused_counter">Paused</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="duration">Duration</th>
<th align='left' id="percent_complete"></th>
<th align="left" id="delete_row">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="device">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody>
@ -83,8 +83,8 @@
type: 'post',
data: function (d) {
return {
'json_data': JSON.stringify(d),
'media_type': media_type
json_data: JSON.stringify(d),
media_type: media_type
};
}
}

View file

@ -10,14 +10,14 @@
</h4>
</div>
<div class="modal-body" id="modal-text">
<table class="display" id="history_table" width="100%">
<table class="display no-fixed" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="started">Started</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="player">Player</th>
<th align='left' id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
</tr>
</thead>
<tbody>
@ -34,13 +34,14 @@
$(document).ready(function() {
$('#date-header').html(moment('${data}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
history_table_modal_options.ajax = {
"url": "get_history",
url: 'get_history',
type: "post",
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'grouping': false,
'start_date': '${data}'
};
return {
json_data: JSON.stringify(d),
grouping: false,
start_date: '${data}'
};
}
}

View file

@ -54,29 +54,29 @@ DOCUMENTATION :: END
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if data['media_type'] == 'movie':
<li><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'show':
<li><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'season':
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Season ${data['media_index']}</li>
% elif data['media_type'] == 'episode':
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">Season ${data['parent_media_index']}</a></li>
<li class="active">Episode ${data['media_index']} - ${data['title']}</li>
% elif data['media_type'] == 'artist':
<li><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'album':
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'track':
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Track ${data['media_index']} - ${data['title']}</li>
@ -288,14 +288,6 @@ DOCUMENTATION :: END
</div>
% endif
<div class='col-md-12'>
<!-- Need to find a place to put this -->
% if data['media_type'] == 'library' and config['update_library_ids'] == 1:
<div id="update_library_ids_mssage" style="text-align: center; margin-top: 20px;">
<i class="fa fa-refresh fa-spin"></i> Updating library ids in the database. This could take a few minutes depending on the size of your database.
<br />
The history table will refresh automatically when the update is complete. Please wait...
</div>
% endif
<div class='table-card-header'>
<div class="header-bar">
<span>Watch History for <strong>${data['title']}</strong></span>
@ -370,41 +362,17 @@ DOCUMENTATION :: END
% if data:
<script src="interfaces/default/js/tables/history_table.js"></script>
<!-- Need to find a place to put this -->
% if data['media_type'] == 'library':
% if data['media_type'] == 'show' or data['media_type'] == 'artist':
<script>
function get_history() {
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'library_id': "${data['library_id']}" };
}
}
}
if ("${config['update_library_ids']}" == "1") {
$.ajax({
url: 'update_library_ids',
type: 'post',
cache: false,
async: true,
data: { },
complete: function (xhr, status) {
$('#update_library_ids_mssage').remove();
history_table.draw();
}
});
}
</script>
% elif data['media_type'] == 'show' or data['media_type'] == 'artist':
<script>
function get_history() {
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'grandparent_rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
grandparent_rating_key: "${data['rating_key']}"
};
}
}
}
@ -416,8 +384,10 @@ DOCUMENTATION :: END
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'parent_rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
parent_rating_key: "${data['rating_key']}"
};
}
}
}
@ -429,8 +399,10 @@ DOCUMENTATION :: END
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
rating_key: "${data['rating_key']}"
};
}
}
}

View file

@ -56,7 +56,7 @@ history_table_options = {
}
},
"searchable": false,
"width": "8%",
"width": "7%",
"className": "no-wrap expand-history"
},
{
@ -73,7 +73,7 @@ history_table_options = {
$(td).html(cellData);
}
},
"width": "8%",
"width": "7%",
"className": "no-wrap hidden-xs"
},
{
@ -95,7 +95,7 @@ history_table_options = {
$(td).html('n/a');
}
},
"width": "8%",
"width": "7%",
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control-ip"
},
{
@ -106,7 +106,7 @@ history_table_options = {
$(td).html(cellData);
}
},
"width": "8%",
"width": "7%",
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
},
{
@ -153,7 +153,7 @@ history_table_options = {
}
}
},
"width": "35%"
"width": "33%"
},
{
"targets": [7],
@ -226,7 +226,7 @@ history_table_options = {
"searchable": false,
"orderable": false,
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"width": "1%"
"width": "2%"
},
],
"drawCallback": function (settings) {
@ -260,7 +260,7 @@ history_table_options = {
var rowData = this.data();
if (rowData['group_count'] != 1 && rowData['reference_id'] in history_child_table) {
// if grouped row and a child table was already created
$(this.node()).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormat(rowData)).show();
createChildTable(this, rowData)
}
@ -426,13 +426,13 @@ function childTableOptions(rowData) {
history_child_options.pageLength = 10;
history_child_options.bStateSave = false;
history_child_options.ajax = {
"url": "get_history",
type: "post",
url: 'get_history',
type: 'post',
data: function (d) {
return {
'json_data': JSON.stringify(d),
'grouping': false,
'reference_id': rowData['reference_id']
json_data: JSON.stringify(d),
grouping: false,
reference_id: rowData['reference_id']
};
}
}
@ -469,7 +469,7 @@ function childTableOptions(rowData) {
// Format the detailed history child table
function childTableFormat(rowData) {
return '<div class="slider">' +
'<table id="history_child-' + rowData['reference_id'] + '">' +
'<table id="history_child-' + rowData['reference_id'] + '" width="100%">' +
'<thead>' +
'<tr>' +
'<th align="left" id="delete_row">Delete</th>' +

View file

@ -39,7 +39,7 @@ history_table_modal_options = {
},
"searchable": false,
"className": "no-wrap",
"width": "5%"
"width": "10%"
},
{
"targets": [1],
@ -53,7 +53,7 @@ history_table_modal_options = {
},
"searchable": false,
"className": "no-wrap",
"width": "5%"
"width": "10%"
},
{
"targets": [2],
@ -69,7 +69,8 @@ history_table_modal_options = {
$(td).html(cellData);
}
},
"className": "no-wrap hidden-xs"
"className": "no-wrap hidden-xs",
"width": "15%"
},
{
"targets": [3],
@ -87,7 +88,8 @@ history_table_modal_options = {
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + '&nbsp' + cellData + '</div></a></div>');
}
},
"className": "no-wrap hidden-sm hidden-xs modal-control"
"className": "no-wrap hidden-sm hidden-xs modal-control",
"width": "20%"
},
{
"targets": [4],
@ -113,7 +115,8 @@ history_table_modal_options = {
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
}
}
},
"width": "30%"
}
],
"drawCallback": function (settings) {

View file

@ -14,7 +14,7 @@ libraries_list_table_options = {
"processing": false,
"serverSide": true,
"pageLength": 10,
"order": [ 1, 'asc'],
"order": [ 2, 'asc'],
"autoWidth": true,
"stateSave": true,
"pagingType": "bootstrap",
@ -40,14 +40,14 @@ libraries_list_table_options = {
"targets": [1],
"data": "library_thumb",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData === '') {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(interfaces/default/images/cover.png);"></div></a>');
} else {
if (cellData !== null && cellData !== '') {
if (rowData['library_thumb'].substring(0, 4) == "http") {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(' + rowData['library_thumb'] + ');"></div></a>');
} else {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(pms_image_proxy?img=' + rowData['library_thumb'] + '&width=80&height=80&fallback=poster);"></div></a>');
}
} else {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(interfaces/default/images/cover.png);"></div></a>');
}
},
"orderable": false,
@ -59,12 +59,12 @@ libraries_list_table_options = {
"targets": [2],
"data": "section_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
$(td).html('<div data-id="' + rowData['section_id'] + '">' +
'<a href="library?section_id=' + rowData['section_id'] + '">' + cellData + '</a>' +
'</div>');
} else {
$(td).html(cellData);
$(td).html('n/a');
}
},
"width": "10%",
@ -74,7 +74,7 @@ libraries_list_table_options = {
"targets": [3],
"data": "section_type",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
@ -85,10 +85,8 @@ libraries_list_table_options = {
"targets": [4],
"data": "count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
}
},
@ -99,10 +97,8 @@ libraries_list_table_options = {
"targets": [5],
"data": "parent_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
}
},
@ -113,10 +109,8 @@ libraries_list_table_options = {
"targets": [6],
"data": "child_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
}
},
@ -126,11 +120,11 @@ libraries_list_table_options = {
{
"targets": [7],
"data": "last_accessed",
"render": function (data, type, full) {
if (data) {
return moment(data, "X").fromNow();
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(moment(cellData, "X").fromNow());
} else {
return "never";
$(td).html("never");
}
},
"searchable": false,
@ -141,7 +135,7 @@ libraries_list_table_options = {
"targets": [8],
"data":"last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = ''
if (rowData['media_type'] === 'movie') {
@ -158,9 +152,9 @@ libraries_list_table_options = {
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type']) {
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
} else {
$(td).html('n/a');
}
} else {
$(td).html('n/a');
}
},
"width": "25%",

View file

@ -0,0 +1,420 @@
var date_format = 'YYYY-MM-DD';
var time_format = 'hh:mm a';
$.ajax({
url: 'get_date_formats',
type: 'GET',
success: function(data) {
date_format = data.date_format;
time_format = data.time_format;
}
});
media_info_table_options = {
"destroy": true,
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ library items",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"<span class='hidden-md hidden-sm hidden-xs'>(filtered from _MAX_ total entries)</span>",
"emptyTable": "No data in table"
},
"pagingType": "bootstrap",
"stateSave": true,
"processing": false,
"serverSide": true,
"pageLength": 25,
"order": [ 1, 'asc'],
"autoWidth": false,
"columnDefs": [
{
"targets": [0],
"data": "added_at",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
var expand_details = '';
var date = moment(cellData, "X").format(date_format);
if (rowData['media_type'] === 'show') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Seasons"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'season') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Episodes"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'artist') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Albumns"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'album') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Tracks"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else {
$(td).html('<div style="float: left;"><i class="fa fa-fw"></i>&nbsp;' + date + '</div>');
}
}
},
"width": "7%",
"className": "no-wrap expand-media-info",
"searchable": false
},
{
"targets": [1],
"data": "title",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = '';
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'show') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="TV Show"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'season') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Season"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">E' + rowData['media_index'] + ' - ' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'artist') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Artist"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'album') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Album"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">T' + rowData['media_index'] + ' - ' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else {
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
}
},
"width": "26%"
},
{
"targets": [2],
"data": "container",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "5%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [3],
"data": "bitrate",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData + ' kbps');
}
},
"width": "5%",
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"searchable": false
},
{
"targets": [4],
"data": "video_codec",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [5],
"data": "video_resolution",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [6],
"data": "video_framerate",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [7],
"data": "audio_codec",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [8],
"data": "audio_channels",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData + ' ch');
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [9],
"data": "file_size",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(Math.round(cellData / 1024 / 1000).toString() + ' MB');
}
},
"width": "5%",
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"searchable": false
},
{
"targets": [10],
"data": "last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
date = moment(cellData, "X").format(date_format);
$(td).html(date);
}
},
"width": "7%",
"className": "no-wrap hidden-xs",
"searchable": false
},
{
"targets": [11],
"data": "play_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "5%",
"className": "no-wrap hidden-xs",
"searchable": false
}
],
"drawCallback": function (settings) {
// Jump to top of page
// $('html,body').scrollTop(0);
$('#ajaxMsg').fadeOut();
// Create the tooltips.
$('.expand-media-info-tooltip').tooltip({ container: 'body' });
$('.media-type-tooltip').tooltip({ container: 'body' });
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
}
});
media_info_table.rows().every(function () {
var rowData = this.data();
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(this, rowData)
}
});
},
"preDrawCallback": function(settings) {
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows...";
showMsg(msg, false, false, 0)
},
"rowCallback": function (row, rowData, rowIndex) {
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(row).addClass('shown')
media_info_table.row(row).child(childTableFormatMedia(rowData)).show();
}
}
}
// Parent table expand detailed media info
$('#media_info_table').on('click', '> tbody > tr > td.expand-media-info a', function () {
var tr = $(this).closest('tr');
var row = media_info_table.row(tr);
var rowData = row.data();
$(this).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
if (row.child.isShown()) {
$('div.slider', row.child()).slideUp(function () {
row.child.hide();
tr.removeClass('shown');
delete media_info_child_table[rowData['rating_key']];
});
} else {
tr.addClass('shown');
row.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(row, rowData);
}
});
// Initialize the detailed media info child table options using the parent table options
function childTableOptionsMedia(rowData) {
switch (rowData['section_type']) {
case 'show':
section_type = 'season';
break;
case 'season':
section_type = 'episode';
break;
case 'artist':
section_type = 'album';
break;
case 'album':
section_type = 'track';
break;
}
media_info_table_options = media_info_table_options;
// Remove settings that are not necessary
media_info_table_options.searching = false;
media_info_table_options.lengthChange = false;
media_info_table_options.info = false;
media_info_table_options.pageLength = 10;
media_info_table_options.bStateSave = false;
media_info_table_options.ajax = {
url: 'get_library_media_info2',
type: 'post',
data: function (d) {
return {
json_data: JSON.stringify(d),
section_id: rowData['section_id'],
section_type: section_type,
rating_key: rowData['rating_key']
};
}
}
media_info_table_options.fnDrawCallback = function (settings) {
$('#ajaxMsg').fadeOut();
// Create the tooltips.
$('.expand-media-info-tooltip').tooltip({ container: 'body' });
$('.media-type-tooltip').tooltip();
$('.thumb-tooltip').popover({
html: true,
trigger: 'hover',
placement: 'right',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
}
});
if (rowData['rating_key'] in media_info_child_table) {
media_info_child_table[rowData['rating_key']].rows().every(function () {
var childrowData = this.data();
if (childrowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormatMedia(childrowData)).show();
createChildTableMedia(this, childrowData)
}
});
}
$(this).closest('div.slider').slideDown();
}
media_info_table_options.fnRowCallback = function (row, rowData, rowIndex) {
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(row).addClass('shown')
media_info_table.row(row).child(childTableFormatMedia(rowData)).show();
}
}
return media_info_table_options;
}
// Format the detailed media info child table
function childTableFormatMedia(rowData) {
return '<div class="slider">' +
'<table id="media_info_child-' + rowData['rating_key'] + '" data-id="' + rowData['rating_key'] + '" width="100%">' +
'<thead>' +
'<tr>' +
'<th align="left" id="added_at">Added At</th>' +
'<th align="left" id="title">Title</th>' +
'<th align="left" id="container">Container</th>' +
'<th align="left" id="bitrate">Bitrate</th>' +
'<th align="left" id="video_codec">Video Codec</th>' +
'<th align="left" id="video_resolution">Video Resolution</th>' +
'<th align="left" id="video_resolution">Video Framerate</th>' +
'<th align="left" id="audio_codec">Audio Codec</th>' +
'<th align="left" id="audio_channels">Audio Channels</th>' +
'<th align="left" id="file_size">File Size</th>' +
'<th align="left" id="last_watched">Last Watched</th>' +
'<th align="left" id="total_plays">Total Plays</th>' +
'</tr>' +
'</thead>' +
'<tbody>' +
'</tbody>' +
'</table>' +
'</div>';
}
// Create the detailed media info child table
media_info_child_table = {};
function createChildTableMedia(row, rowData) {
media_info_table_options = childTableOptionsMedia(rowData);
// initialize the child table
media_info_child_table[rowData['rating_key']] = $('#media_info_child-' + rowData['rating_key']).DataTable(media_info_table_options);
// Set child table column visibility to match parent table
var visibility = media_info_table.columns().visible();
for (var i = 0; i < visibility.length; i++) {
if (!(visibility[i])) { media_info_child_table[rowData['rating_key']].column(i).visible(visibility[i]); }
}
media_info_table.on('column-visibility', function (e, settings, colIdx, visibility) {
if (row.child.isShown()) {
media_info_child_table[rowData['rating_key']].column(colIdx).visible(visibility);
}
});
// Child table expand detailed media info
$('table[id^=media_info_child]').on('click', '> tbody > tr > td.expand-media-info a', function () {
var table_id = $(this).closest('table').data('id');
var tr = $(this).closest('tr');
var row = media_info_child_table[table_id].row(tr);
var rowData = row.data();
$(this).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
if (row.child.isShown()) {
$('div.slider', row.child()).slideUp(function () {
row.child.hide();
tr.removeClass('shown');
delete media_info_child_table[rowData['rating_key']];
});
} else {
tr.addClass('shown');
row.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(row, rowData);
}
});
}

View file

@ -4,7 +4,7 @@ var plex_log_table_options = {
"serverSide": false,
"pagingType": "bootstrap",
"order": [ 0, 'desc'],
"pageLength": 10,
"pageLength": 50,
"stateSave": true,
"language": {
"search":"Search: ",

View file

@ -54,13 +54,13 @@ users_list_table_options = {
"targets": [2],
"data": "friendly_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '">' +
'<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>' +
'<input type="text" class="hidden" value="' + cellData + '">' +
'</div>');
} else {
$(td).html(cellData);
$(td).html('n/a');
}
},
"width": "10%",
@ -69,11 +69,11 @@ users_list_table_options = {
{
"targets": [3],
"data": "last_seen",
"render": function ( data, type, full ) {
if (data) {
return moment(data, "X").fromNow();
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(moment(cellData, "X").fromNow());
} else {
return "never";
$(td).html("never");
}
},
"searchable": false,
@ -105,7 +105,7 @@ users_list_table_options = {
"targets": [5],
"data": "platform",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
@ -118,7 +118,7 @@ users_list_table_options = {
"targets": [6],
"data":"player",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData) {
if (cellData !== null && cellData !== '') {
var transcode_dec = '';
if (rowData['video_decision'] === 'transcode') {
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
@ -139,7 +139,7 @@ users_list_table_options = {
"targets": [7],
"data":"last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = ''
if (rowData['media_type'] === 'movie') {
@ -156,9 +156,9 @@ users_list_table_options = {
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type']) {
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
} else {
$(td).html('n/a');
}
} else {
$(td).html('n/a');
}
},
"width": "30%",

View file

@ -7,6 +7,17 @@
<%def name="body()">
<div class='container-fluid'>
% if config['update_section_ids'] == 1:
<div id="update_section_ids_mssage" style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> PlexPy needs to update the Library IDs in your databse. Click the "<strong>Refresh libraries</strong>" button below to begin the update.
</div>
% elif config['update_section_ids'] == -1:
<div id="update_section_ids_mssage" style="text-align: center; margin-top: 20px;">
<i class="fa fa-refresh fa-spin"></i> PlexPy is updating library IDs in the database. This could take a few minutes depending on the size of your database.
<br />
You may leave this page and come back later.
</div>
% endif
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-book"></i> All Libraries</span>
@ -20,7 +31,7 @@
</div>
</div>
<div class='table-card-back'>
<table id="libraries_list_table" class="display" width="100%">
<table id="libraries_list_table" class="display no-fixed" width="100%">
<thead>
<tr>
<th align="left" id="edit_row">Edit</th>
@ -74,7 +85,9 @@
url: 'get_library_list',
type: 'POST',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ) };
return {
json_data: JSON.stringify(d)
};
}
}
@ -157,7 +170,15 @@
});
});
$("#refresh-libraries-list").click(function() {
$("#refresh-libraries-list").click(function () {
if ("${config['update_section_ids']}" == "1") {
$('#update_section_ids_mssage').html(
'<i class="fa fa-refresh fa-spin"></i> PlexPy is updating library IDs in the database. This could take a few minutes depending on the size of your database.' +
'<br />' +
'You may leave this page and come back later.');
$(this).prop('disabled', true);
}
$.ajax({
url: 'refresh_libraries_list',
cache: false,
@ -173,5 +194,9 @@
}
});
});
if ("${config['update_section_ids']}" == "-1") {
$("#refresh-libraries-list").prop('disabled', true);
}
</script>
</%def>

View file

@ -67,6 +67,7 @@ DOCUMENTATION :: END
<ul class="user-info-nav">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="history-tab-btn" href="#libraryHistory" data-toggle="tab">History</a></li>
<li><a id="media-info-tab-btn" href="#libraryMediaInfo" data-toggle="tab">Media Info</a></li>
</ul>
</div>
</div>
@ -170,18 +171,62 @@ DOCUMENTATION :: END
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="delete">Delete</th>
<th align='left' id="time">Time</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="ip_address">IP Address</th>
<th align='left' id="platform">Platform</th>
<th align='left' id="player">Player</th>
<th align='left' id="title">Title</th>
<th align='left' id="started">Started</th>
<th align='left' id="paused_counter">Paused</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="duration">Duration</th>
<th align='left' id="percent_complete"></th>
<th align="left" id="delete">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="libraryMediaInfo">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class='table-card-header'>
<div class="header-bar">
<span>
<i class="fa fa-history"></i> All Media Info for <strong>
<span class="set-username">${data['section_name']}</span>
</strong>
</span>
</div>
<div class="button-bar">
<button class="btn btn-dark refresh-media-info-table-button" id="refresh-media-info-table" style="margin-right: 5px;">
<i class="fa fa-refresh"></i> Refresh media info
</button>
<div class="colvis-button-bar hidden-xs" id="button-bar-media-info"></div>
</div>
</div>
<div class="table-card-back">
<table class="display" id="media_info_table" width="100%">
<thead>
<tr>
<th align="left" id="added_at">Added At</th>
<th align="left" id="title">Title</th>
<th align="left" id="container">Container</th>
<th align="left" id="bitrate">Bitrate</th>
<th align="left" id="video_codec">Video Codec</th>
<th align="left" id="video_resolution">Video Resolution</th>
<th align="left" id="video_resolution">Video Framerate</th>
<th align="left" id="audio_codec">Audio Codec</th>
<th align="left" id="audio_channels">Audio Channels</th>
<th align="left" id="file_size">File Size</th>
<th align="left" id="last_watched">Last Watched</th>
<th align="left" id="total_plays">Total Plays</th>
</tr>
</thead>
<tbody></tbody>
@ -250,6 +295,7 @@ DOCUMENTATION :: END
% if data:
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/history_table.js"></script>
<script src="interfaces/default/js/tables/media_info_table.js"></script>
<script>
$(document).ready(function () {
@ -267,7 +313,7 @@ DOCUMENTATION :: END
$.ajax({
url: 'get_library_watch_time_stats',
async: true,
data: { library_id: section_id },
data: { section_id: section_id },
complete: function(xhr, status) {
$("#library-time-stats").html(xhr.responseText);
}
@ -277,7 +323,7 @@ DOCUMENTATION :: END
$.ajax({
url: 'get_library_user_stats',
async: true,
data: { library_id: section_id },
data: { section_id: section_id },
complete: function(xhr, status) {
$("#library-user-stats").html(xhr.responseText);
}
@ -290,8 +336,8 @@ DOCUMENTATION :: END
type: 'post',
data: function ( d ) {
return {
'json_data': JSON.stringify( d ),
'section_id': section_id
json_data: JSON.stringify( d ),
section_id: section_id
};
}
}
@ -304,9 +350,44 @@ DOCUMENTATION :: END
clearSearchButton('history_table', history_table);
}
function loadMediaInfoTable() {
// Build media info table
media_info_table_options.ajax = {
url: 'get_library_media_info2',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
section_id: section_id
};
}
}
media_info_table = $('#media_info_table').DataTable(media_info_table_options);
var colvis = new $.fn.dataTable.ColVis(media_info_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' });
$(colvis.button()).appendTo('#button-bar-media-info');
clearSearchButton('media_info_table', media_info_table);
}
$( "#history-tab-btn" ).one( "click", function() {
loadHistoryTable();
});
$( "#media-info-tab-btn" ).one( "click", function() {
loadMediaInfoTable();
});
$("#refresh-media-info-table").click(function () {
$.ajax({
url: 'delete_datatable_media_info_cache',
cache: false,
async: true,
data: { section_id: section_id },
complete: function(xhr, status) {
media_info_table.draw()
}
});
});
// Load edit library modal
$("#toggle-edit-library-modal").click(function() {
@ -374,7 +455,7 @@ DOCUMENTATION :: END
url: 'get_library_recently_watched',
async: true,
data: {
library_id: section_id,
section_id: section_id,
limit: containerSize
},
complete: function(xhr, status) {
@ -398,7 +479,7 @@ DOCUMENTATION :: END
url: 'get_library_recently_added',
async: true,
data: {
library_id: section_id,
section_id: section_id,
limit: containerSize
},
complete: function(xhr, status) {
@ -413,8 +494,9 @@ DOCUMENTATION :: END
recentlyWatched();
recentlyAdded();
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
</script>
% endif
</%def>

View file

@ -19,7 +19,7 @@ parent_title Returns the name of the artist.
grandparent_title Returns the name of the show.
media_index Returns the index number of the episode.
parent_media_index Returns the index number of the season.
library_id Returns the library section number of the media item.
section_id Returns the library section number of the media item.
library_name Returns the library section name of the media item.
year Returns the release year of the movie, episode, or album.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
@ -39,7 +39,11 @@ DOCUMENTATION :: END
<a href="info?rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-poster">
% if item['media_type'] == 'episode':
% if item['parent_thumb']:
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['parent_thumb']}&width=300&height=450&fallback=poster);">
% else:
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['grandparent_thumb']}&width=300&height=450&fallback=poster);">
% endif
% elif item['media_type'] == 'movie':
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
% endif

View file

@ -19,7 +19,7 @@ parent_title Returns the name of the artist.
grandparent_title Returns the name of the show.
media_index Returns the index number of the episode.
parent_media_index Returns the index number of the season.
library_id Returns the library section number of the media item.
section_id Returns the library section number of the media item.
library_name Returns the library section name of the media item.
year Returns the release year of the movie, episode, or album.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.

View file

@ -181,18 +181,18 @@ from plexpy import helpers
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="delete">Delete</th>
<th align='left' id="time">Time</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="ip_address">IP Address</th>
<th align='left' id="platform">Platform</th>
<th align='left' id="player">Player</th>
<th align='left' id="title">Title</th>
<th align='left' id="started">Started</th>
<th align='left' id="paused_counter">Paused</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="duration">Duration</th>
<th align='left' id="percent_complete"></th>
<th align="left" id="delete">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody></tbody>
@ -221,17 +221,17 @@ from plexpy import helpers
<table class="display" id="sync_table" width="100%">
<thead>
<tr>
<th align='left' id="state">State</th>
<th align='left' id="username">Username</th>
<th align='left' id="sync_title">Title</th>
<th align='left' id="type">Type</th>
<th align='left' id="sync_platform">Platform</th>
<th align='left' id="device">Device</th>
<th align='left' id="size">Total Size</th>
<th align='left' id="items">Total Items</th>
<th align='left' id="converted">Converted</th>
<th align='left' id="downloaded">Downloaded</th>
<th align='left' id="sync_percent_complete">Complete</th>
<th align="left" id="state">State</th>
<th align="left" id="username">Username</th>
<th align="left" id="sync_title">Title</th>
<th align="left" id="type">Type</th>
<th align="left" id="sync_platform">Platform</th>
<th align="left" id="device">Device</th>
<th align="left" id="size">Total Size</th>
<th align="left" id="items">Total Items</th>
<th align="left" id="converted">Converted</th>
<th align="left" id="downloaded">Downloaded</th>
<th align="left" id="sync_percent_complete">Complete</th>
</tr>
</thead>
<tbody></tbody>
@ -342,9 +342,9 @@ from plexpy import helpers
type: 'post',
data: function ( d ) {
return {
'json_data': JSON.stringify( d ),
'user_id': user_id,
'media_type': media_type
json_data: JSON.stringify( d ),
user_id: user_id,
media_type: media_type
};
}
}
@ -391,8 +391,9 @@ from plexpy import helpers
url: 'get_user_ips',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'user_id': user_id
return {
json_data: JSON.stringify( d ),
user_id: user_id
};
}
}

View file

@ -20,7 +20,7 @@
</div>
</div>
<div class='table-card-back'>
<table id="users_list_table" class="display" width="100%">
<table id="users_list_table" class="display no-fixed" width="100%">
<thead>
<tr>
<th align="left" id="edit_row">Edit</th>
@ -77,7 +77,9 @@
url: 'get_user_list',
type: 'POST',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ) };
return {
json_data: JSON.stringify(d)
};
}
}

View file

@ -377,7 +377,7 @@ def dbcheck():
# sessions table :: This is a temp table that logs currently active sessions
c_db.execute(
'CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, library_id INTEGER, media_type TEXT, started INTEGER, '
'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, '
'paused_counter INTEGER DEFAULT 0, state TEXT, user_id INTEGER, user TEXT, friendly_name TEXT, '
'ip_address TEXT, machine_id TEXT, player TEXT, platform TEXT, title TEXT, parent_title TEXT, '
'grandparent_title TEXT, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
@ -413,7 +413,7 @@ def dbcheck():
'CREATE TABLE IF NOT EXISTS session_history_metadata (id INTEGER PRIMARY KEY, '
'rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
'title TEXT, parent_title TEXT, grandparent_title TEXT, full_title TEXT, media_index INTEGER, '
'parent_media_index INTEGER, library_id INTEGER, thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, '
'parent_media_index INTEGER, section_id INTEGER, thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, '
'art TEXT, media_type TEXT, year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, '
'last_viewed_at INTEGER, content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, '
'duration INTEGER DEFAULT 0, guid TEXT, directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT)'
@ -594,11 +594,11 @@ def dbcheck():
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT library_id from sessions')
c_db.execute('SELECT section_id from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN library_id INTEGER'
'ALTER TABLE sessions ADD COLUMN section_id INTEGER'
)
# Upgrade session_history table from earlier versions
@ -644,11 +644,11 @@ def dbcheck():
# Upgrade session_history_metadata table from earlier versions
try:
c_db.execute('SELECT library_id from session_history_metadata')
c_db.execute('SELECT section_id from session_history_metadata')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table session_history_metadata.")
c_db.execute(
'ALTER TABLE session_history_metadata ADD COLUMN library_id INTEGER'
'ALTER TABLE session_history_metadata ADD COLUMN section_id INTEGER'
)
# Upgrade users table from earlier versions

View file

@ -192,7 +192,7 @@ def check_recently_added():
recently_added = recently_added_list['recently_added']
for item in recently_added:
library_details = library_data.get_details(section_id=item['library_id'])
library_details = library_data.get_details(section_id=item['section_id'])
if not library_details['do_notify_created']:
continue
@ -221,7 +221,7 @@ def check_recently_added():
if not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
for item in metadata:
library_details = library_data.get_details(section_id=item['library_id'])
library_details = library_data.get_details(section_id=item['section_id'])
if 0 < time_threshold - int(item['added_at']) <= time_interval:
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))

View file

@ -29,7 +29,7 @@ class ActivityProcessor(object):
def write_session(self, session=None, notify=True):
if session:
values = {'session_key': session['session_key'],
'library_id': session['library_id'],
'section_id': session['section_id'],
'rating_key': session['rating_key'],
'media_type': session['media_type'],
'state': session['state'],
@ -104,7 +104,7 @@ class ActivityProcessor(object):
user_details = user_data.get_details(user_id=session['user_id'])
library_data = libraries.Libraries()
library_details = library_data.get_details(section_id=session['library_id'])
library_details = library_data.get_details(section_id=session['section_id'])
if session:
logging_enabled = False
@ -257,7 +257,7 @@ class ActivityProcessor(object):
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history_metadata table...")
query = 'INSERT INTO session_history_metadata (id, rating_key, parent_rating_key, ' \
'grandparent_rating_key, title, parent_title, grandparent_title, full_title, media_index, ' \
'parent_media_index, library_id, thumb, parent_thumb, grandparent_thumb, art, media_type, ' \
'parent_media_index, section_id, 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) VALUES ' \
'(last_insert_rowid(), ' \
@ -265,7 +265,7 @@ class ActivityProcessor(object):
args = [session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'],
session['title'], session['parent_title'], session['grandparent_title'], full_title,
metadata['media_index'], metadata['parent_media_index'], metadata['library_id'], metadata['thumb'],
metadata['media_index'], metadata['parent_media_index'], metadata['section_id'], metadata['thumb'],
metadata['parent_thumb'], metadata['grandparent_thumb'], metadata['art'], session['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'],

View file

@ -372,7 +372,7 @@ _CONFIG_DEFINITIONS = {
'TWITTER_ON_EXTUP': (int, 'Twitter', 0),
'TWITTER_ON_INTUP': (int, 'Twitter', 0),
'UPDATE_DB_INTERVAL': (int, 'General', 24),
'UPDATE_LIBRARY_IDS': (int, 'General', 1),
'UPDATE_SECTION_IDS': (int, 'General', 1),
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1),
'XBMC_ENABLED': (int, 'XBMC', 0),

View file

@ -98,12 +98,12 @@ class DataFactory(object):
for item in history:
filter_duration += int(item['duration'])
if item["media_type"] == 'episode' and item["parent_thumb"]:
thumb = item["parent_thumb"]
elif item["media_type"] == 'episode':
thumb = item["grandparent_thumb"]
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"]
thumb = item['thumb']
if item['percent_complete'] >= watched_percent:
watched_status = 1
@ -113,37 +113,37 @@ class DataFactory(object):
watched_status = 0
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
row = {"reference_id": item["reference_id"],
"id": item["id"],
"date": item["date"],
"started": item["started"],
"stopped": item["stopped"],
"duration": item["duration"],
"paused_counter": item["paused_counter"],
"user_id": item["user_id"],
"user": item["user"],
"friendly_name": item["friendly_name"],
"platform": platform,
"player": item['player'],
"ip_address": item["ip_address"],
"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"],
"parent_title": item["parent_title"],
"year": item["year"],
"media_index": item["media_index"],
"parent_media_index": item["parent_media_index"],
"thumb": thumb,
"video_decision": item["video_decision"],
"audio_decision": item["audio_decision"],
"percent_complete": int(round(item['percent_complete'])),
"watched_status": watched_status,
"group_count": item["group_count"],
"group_ids": item["group_ids"]
row = {'reference_id': item['reference_id'],
'id': item['id'],
'date': item['date'],
'started': item['started'],
'stopped': item['stopped'],
'duration': item['duration'],
'paused_counter': item['paused_counter'],
'user_id': item['user_id'],
'user': item['user'],
'friendly_name': item['friendly_name'],
'platform': platform,
'player': item['player'],
'ip_address': item['ip_address'],
'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'],
'parent_title': item['parent_title'],
'year': item['year'],
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
'thumb': thumb,
'video_decision': item['video_decision'],
'audio_decision': item['audio_decision'],
'percent_complete': int(round(item['percent_complete'])),
'watched_status': watched_status,
'group_count': item['group_count'],
'group_ids': item['group_ids']
}
rows.append(row)
@ -154,7 +154,7 @@ class DataFactory(object):
'draw': query['draw'],
'filter_duration': helpers.human_duration(filter_duration, sig='dhm'),
'total_duration': helpers.human_duration(total_duration, sig='dhm')
}
}
return dict
@ -736,7 +736,7 @@ class DataFactory(object):
'session_history_metadata.parent_title, session_history_metadata.grandparent_title, ' \
'session_history_metadata.full_title, library_sections.section_name, ' \
'session_history_metadata.media_index, session_history_metadata.parent_media_index, ' \
'session_history_metadata.library_id, session_history_metadata.thumb, ' \
'session_history_metadata.section_id, 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, ' \
@ -746,7 +746,7 @@ class DataFactory(object):
'session_history_metadata.guid, session_history_metadata.directors, session_history_metadata.writers, ' \
'session_history_metadata.actors, session_history_metadata.genres, session_history_metadata.studio ' \
'FROM session_history_metadata ' \
'JOIN library_sections ON session_history_metadata.library_id = library_sections.section_id ' \
'JOIN library_sections ON session_history_metadata.section_id = library_sections.section_id ' \
'WHERE session_history_metadata.rating_key = ?'
result = monitor_db.select(query=query, args=[rating_key])
else:
@ -789,7 +789,7 @@ class DataFactory(object):
'genres': genres,
'actors': actors,
'library_name': item['section_name'],
'library_id': item['library_id']
'section_id': item['section_id']
}
return metadata
@ -1056,7 +1056,7 @@ class DataFactory(object):
# 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 = ?, full_title = ?, ' \
'media_index = ?, parent_media_index = ?, library_id = ?, thumb = ?, parent_thumb = ?, ' \
'media_index = ?, parent_media_index = ?, section_id = ?, 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 = ?, ' \
@ -1065,7 +1065,7 @@ class DataFactory(object):
args = [metadata['rating_key'], metadata['parent_rating_key'], metadata['grandparent_rating_key'],
metadata['title'], metadata['parent_title'], metadata['grandparent_title'], full_title,
metadata['media_index'], metadata['parent_media_index'], metadata['library_id'], metadata['thumb'],
metadata['media_index'], metadata['parent_media_index'], metadata['section_id'], 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'],
@ -1073,35 +1073,4 @@ class DataFactory(object):
metadata['studio'],
old_rating_key]
monitor_db.action(query=query, args=args)
def update_library_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 library_id IS NULL'
result = monitor_db.select(query=query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for update_library_id: %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 = {'library_id': metadata['library_id']}
monitor_db.upsert('session_history_metadata', key_dict=section_keys, value_dict=section_values)
else:
continue
return True
monitor_db.action(query=query, args=args)

View file

@ -124,8 +124,9 @@ class DataTables(object):
order += ', '
order = order.rstrip(', ')
if order:
order = 'ORDER BY ' + order.rstrip(', ')
order = 'ORDER BY ' + order
# Build where parameters
if parameters['search']['value']:

View file

@ -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)

View file

@ -31,7 +31,7 @@ def notify(stream_data=None, notify_action=None):
user_details = user_data.get_details(user_id=stream_data['user_id'])
library_data = libraries.Libraries()
library_details = library_data.get_details(section_id=stream_data['library_id'])
library_details = library_data.get_details(section_id=stream_data['section_id'])
if not user_details['do_notify']:
# logger.debug(u"PlexPy NotificationHandler :: Notifications for user '%s' is disabled." % user_details['username'])

View file

@ -41,7 +41,7 @@ def extract_plexwatch_xml(xml=None):
grandparent_thumb = helpers.get_xml_attr(a, 'grandparentThumb')
grandparent_title = helpers.get_xml_attr(a, 'grandparentTitle')
guid = helpers.get_xml_attr(a, 'guid')
library_id = helpers.get_xml_attr(a, 'librarySectionID')
section_id = helpers.get_xml_attr(a, 'librarySectionID')
media_index = helpers.get_xml_attr(a, 'index')
originally_available_at = helpers.get_xml_attr(a, 'originallyAvailableAt')
last_viewed_at = helpers.get_xml_attr(a, 'lastViewedAt')
@ -157,7 +157,7 @@ def extract_plexwatch_xml(xml=None):
'title': title,
'tagline': tagline,
'guid': guid,
'library_id': library_id,
'section_id': section_id,
'media_index': media_index,
'originally_available_at': originally_available_at,
'last_viewed_at': last_viewed_at,
@ -251,6 +251,8 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
hours=0, minutes=0, seconds=0)
ap = activity_processor.ActivityProcessor()
user_data = users.Users()
@ -372,7 +374,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
'rating': extracted_xml['rating'],
'duration': extracted_xml['duration'],
'guid': extracted_xml['guid'],
'library_id': extracted_xml['library_id'],
'section_id': extracted_xml['section_id'],
'directors': extracted_xml['directors'],
'writers': extracted_xml['writers'],
'actors': extracted_xml['actors'],

View file

@ -44,11 +44,6 @@ def refresh_libraries():
server_id = plexpy.CONFIG.PMS_IDENTIFIER
if plexpy.CONFIG.HOME_LIBRARY_CARDS == ['first_run']:
populate_cards = True
else:
populate_cards = False
library_keys = []
if library_sections:
@ -56,11 +51,11 @@ def refresh_libraries():
for section in library_sections:
section_keys = {'server_id': server_id,
'section_id': section['key']}
'section_id': section['section_id']}
section_values = {'server_id': server_id,
'section_id': section['key'],
'section_name': section['title'],
'section_type': section['type'],
'section_id': section['section_id'],
'section_name': section['section_name'],
'section_type': section['section_type'],
'thumb': section['thumb'],
'art': section['art'],
'count': section['count'],
@ -70,13 +65,42 @@ def refresh_libraries():
monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
library_keys.append(section['key'])
library_keys.append(section['section_id'])
if populate_cards:
if plexpy.CONFIG.HOME_LIBRARY_CARDS == ['first_run']:
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', library_keys)
plexpy.CONFIG.write()
if plexpy.CONFIG.UPDATE_SECTION_IDS == 1:
from plexpy import libraries
plexpy.CONFIG.UPDATE_SECTION_IDS = -1
logger.info(u"PlexPy Pmsconnect :: Updating section_id's in database.")
logger.debug(u"PlexPy Pmsconnect :: Disabling monitoring while update in progress.")
plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
hours=0, minutes=0, seconds=0)
result = libraries.Libraries().update_section_ids()
if result:
logger.debug(u"PlexPy Pmsconnect :: Updated all section_id's in database.")
plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 0)
plexpy.CONFIG.write()
else:
logger.debug(u"PlexPy Pmsconnect :: Unable to update section_id's in database.")
plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 1)
plexpy.CONFIG.write()
logger.debug(u"PlexPy Pmsconnect :: Re-enabling monitoring.")
plexpy.initialize_scheduler()
logger.info(u"PlexPy Pmsconnect :: Libraries list refreshed.")
else:
logger.warn(u"PlexPy Pmsconnect :: Unable to refresh libraries list.")
@ -169,7 +193,7 @@ class PmsConnect(object):
return request
def get_library_recently_added(self, section_key='', count='0', output_format=''):
def get_library_recently_added(self, section_id='', count='0', output_format=''):
"""
Return list of recently added items.
@ -178,7 +202,7 @@ class PmsConnect(object):
Output: array
"""
uri = '/library/sections/' + section_key + '/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count
uri = '/library/sections/' + section_id + '/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
@ -203,6 +227,23 @@ class PmsConnect(object):
return request
def get_childrens_list(self, rating_key='', output_format=''):
"""
Return list of children in requested library item.
Parameters required: rating_key { ratingKey of parent }
Optional parameters: output_format { dict, json }
Output: array
"""
uri = '/library/metadata/' + rating_key + '/allLeaves'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
def get_server_list(self, output_format=''):
"""
Return list of local servers.
@ -267,7 +308,7 @@ class PmsConnect(object):
return request
def get_library_list(self, section_key='', list_type='all', count='0', sort_type='', output_format=''):
def get_library_list(self, section_id='', list_type='all', count='0', sort_type='', output_format=''):
"""
Return list of items in library on server.
@ -275,7 +316,9 @@ class PmsConnect(object):
Output: array
"""
uri = '/library/sections/' + section_key + '/' + list_type +'?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count + sort_type
count = '&X-Plex-Container-Size=' + count if count else ''
uri = '/library/sections/' + section_id + '/' + list_type +'?X-Plex-Container-Start=0' + count + sort_type
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
@ -363,7 +406,7 @@ class PmsConnect(object):
return request
def get_recently_added_details(self, library_id='', count='0'):
def get_recently_added_details(self, section_id='', count='0'):
"""
Return processed and validated list of recently added items.
@ -371,8 +414,8 @@ class PmsConnect(object):
Output: array
"""
if library_id:
recent = self.get_library_recently_added(library_id, count, output_format='xml')
if section_id:
recent = self.get_library_recently_added(section_id, count, output_format='xml')
else:
recent = self.get_recently_added(count, output_format='xml')
@ -402,7 +445,7 @@ class PmsConnect(object):
'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'),
'media_index': helpers.get_xml_attr(item, 'index'),
'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'),
'library_id': helpers.get_xml_attr(item, 'librarySectionID'),
'section_id': helpers.get_xml_attr(item, 'librarySectionID'),
'library_name': helpers.get_xml_attr(item, 'librarySectionTitle'),
'year': helpers.get_xml_attr(item, 'year'),
'thumb': helpers.get_xml_attr(item, 'thumb'),
@ -424,7 +467,7 @@ class PmsConnect(object):
'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'),
'media_index': helpers.get_xml_attr(item, 'index'),
'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'),
'library_id': helpers.get_xml_attr(item, 'librarySectionID'),
'section_id': helpers.get_xml_attr(item, 'librarySectionID'),
'library_name': helpers.get_xml_attr(item, 'librarySectionTitle'),
'year': helpers.get_xml_attr(item, 'year'),
'thumb': helpers.get_xml_attr(item, 'thumb'),
@ -450,7 +493,7 @@ class PmsConnect(object):
try:
xml_head = metadata.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_metadata: %s: %s." % e)
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_metadata: %s." % e)
return []
metadata_list = []
@ -474,7 +517,7 @@ class PmsConnect(object):
logger.debug(u"PlexPy Pmsconnect :: Metadata failed")
return None
library_id = helpers.get_xml_attr(a, 'librarySectionID')
section_id = helpers.get_xml_attr(a, 'librarySectionID')
library_name = helpers.get_xml_attr(a, 'librarySectionTitle')
genres = []
@ -500,7 +543,7 @@ class PmsConnect(object):
if metadata_type == 'movie':
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -535,7 +578,7 @@ class PmsConnect(object):
elif metadata_type == 'show':
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -572,7 +615,7 @@ class PmsConnect(object):
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
show_details = self.get_metadata_details(parent_rating_key)
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -609,7 +652,7 @@ class PmsConnect(object):
grandparent_rating_key = helpers.get_xml_attr(metadata_main, 'grandparentRatingKey')
show_details = self.get_metadata_details(grandparent_rating_key)
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -644,7 +687,7 @@ class PmsConnect(object):
elif metadata_type == 'artist':
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -681,7 +724,7 @@ class PmsConnect(object):
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
artist_details = self.get_metadata_details(parent_rating_key)
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -718,7 +761,7 @@ class PmsConnect(object):
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
album_details = self.get_metadata_details(parent_rating_key)
metadata = {'media_type': metadata_type,
'library_id': library_id,
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
@ -798,11 +841,11 @@ class PmsConnect(object):
output = {'metadata': metadata_list}
return output
def get_library_metadata_details(self, library_id=''):
def get_library_metadata_details(self, section_id=''):
"""
Return processed and validated metadata list for requested library.
Parameters required: library_id { Plex library key }
Parameters required: section_id { Plex library key }
Output: array
"""
@ -826,9 +869,9 @@ class PmsConnect(object):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
key = helpers.get_xml_attr(result, 'key')
if key == library_id:
if key == section_id:
metadata = {'media_type': 'library',
'library_id': helpers.get_xml_attr(result, 'key'),
'section_id': helpers.get_xml_attr(result, 'key'),
'library': helpers.get_xml_attr(result, 'type'),
'title': helpers.get_xml_attr(result, 'title'),
'art': helpers.get_xml_attr(result, 'art'),
@ -945,7 +988,7 @@ class PmsConnect(object):
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
'library_id': helpers.get_xml_attr(session, 'librarySectionID'),
'section_id': helpers.get_xml_attr(session, 'librarySectionID'),
'media_index': helpers.get_xml_attr(session, 'index'),
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
'art': helpers.get_xml_attr(session, 'art'),
@ -1067,7 +1110,7 @@ class PmsConnect(object):
if helpers.get_xml_attr(session, 'type') == 'episode':
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
'library_id': helpers.get_xml_attr(session, 'librarySectionID'),
'section_id': helpers.get_xml_attr(session, 'librarySectionID'),
'media_index': helpers.get_xml_attr(session, 'index'),
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
'art': helpers.get_xml_attr(session, 'art'),
@ -1125,7 +1168,7 @@ class PmsConnect(object):
elif helpers.get_xml_attr(session, 'type') == 'movie':
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
'library_id': helpers.get_xml_attr(session, 'librarySectionID'),
'section_id': helpers.get_xml_attr(session, 'librarySectionID'),
'media_index': helpers.get_xml_attr(session, 'index'),
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
'art': helpers.get_xml_attr(session, 'art'),
@ -1183,7 +1226,7 @@ class PmsConnect(object):
elif helpers.get_xml_attr(session, 'type') == 'clip':
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
'library_id': helpers.get_xml_attr(session, 'librarySectionID'),
'section_id': helpers.get_xml_attr(session, 'librarySectionID'),
'media_index': helpers.get_xml_attr(session, 'index'),
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
'art': helpers.get_xml_attr(session, 'art'),
@ -1274,7 +1317,7 @@ class PmsConnect(object):
machine_id = helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'machineIdentifier')
session_output = {'session_key': helpers.get_xml_attr(session, 'sessionKey'),
'library_id': helpers.get_xml_attr(session, 'librarySectionID'),
'section_id': helpers.get_xml_attr(session, 'librarySectionID'),
'media_index': helpers.get_xml_attr(session, 'index'),
'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'),
'art': helpers.get_xml_attr(session, 'art'),
@ -1494,9 +1537,9 @@ class PmsConnect(object):
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
libraries_output = {'key': helpers.get_xml_attr(result, 'key'),
'type': helpers.get_xml_attr(result, 'type'),
'title': helpers.get_xml_attr(result, 'title'),
libraries_output = {'section_id': helpers.get_xml_attr(result, 'key'),
'section_type': helpers.get_xml_attr(result, 'type'),
'section_name': helpers.get_xml_attr(result, 'title'),
'thumb': helpers.get_xml_attr(result, 'thumb'),
'art': helpers.get_xml_attr(result, 'art')
}
@ -1509,71 +1552,123 @@ class PmsConnect(object):
return output
def get_library_children(self, library_type='', section_key='', list_type='all', count='1', sort_type = ''):
def get_library_children(self, section_id='', section_type='', list_type='all', count='', rating_key='', get_media_info=False):
"""
Return processed and validated server library items list.
Parameters required: library_type { movie, show, episode, artist }
section_key { unique library key }
Parameters required: section_type { movie, show, episode, artist }
section_id { unique library key }
Output: array
"""
if library_type == 'movie':
if section_type == 'movie':
sort_type = '&type=1'
elif library_type == 'show':
elif section_type == 'show':
sort_type = '&type=2'
elif library_type == 'season':
elif section_type == 'season':
sort_type = '&type=3'
elif library_type == 'episode':
elif section_type == 'episode':
sort_type = '&type=4'
elif library_type == 'artist':
elif section_type == 'artist':
sort_type = '&type=8'
elif library_type == 'album':
elif section_type == 'album':
sort_type = '&type=9'
elif library_type == 'track':
elif section_type == 'track':
sort_type = '&type=10'
elif library_type == 'photo':
elif section_type == 'photo':
sort_type = ''
elif library_type == 'photoAlbum':
elif section_type == 'photoAlbum':
sort_type = '&type=14'
elif library_type == 'picture':
elif section_type == 'picture':
sort_type = '&type=13'
else:
sort_type = ''
if str(section_id).isdigit():
library_data = self.get_library_list(section_id, list_type, count, sort_type, output_format='xml')
elif str(rating_key).isdigit():
library_data = self.get_children_list(rating_key, output_format='xml')
else:
logger.warn(u"PlexPy Pmsconnect :: get_library_children called by invalid section_id or rating_key provided.")
return []
library_data = self.get_library_list(section_key, list_type, count, sort_type, output_format='xml')
try:
xml_head = library_data.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_library_children: %s." % e)
return []
library_list = []
childern_list = []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
logger.debug(u"PlexPy Pmsconnect :: No library data.")
library_list = {'library_count': '0',
'library_list': []
childern_list = {'library_count': '0',
'childern_list': []
}
return library_list
return childern_list
if rating_key:
library_count = helpers.get_xml_attr(xml_head[0], 'size')
else:
library_count = helpers.get_xml_attr(xml_head[0], 'totalSize')
# Get show/season info from xml_head
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
library_output = {'key': helpers.get_xml_attr(result, 'key'),
'type': helpers.get_xml_attr(result, 'type'),
'title': helpers.get_xml_attr(result, 'title'),
'thumb': helpers.get_xml_attr(result, 'thumb')
item_main = a.getElementsByTagName('Directory')
item_main = [d for d in item_main if helpers.get_xml_attr(d, 'ratingKey')]
elif a.getElementsByTagName('Video'):
item_main = a.getElementsByTagName('Video')
elif a.getElementsByTagName('Track'):
item_main = a.getElementsByTagName('Track')
elif a.getElementsByTagName('Photo'):
item_main = a.getElementsByTagName('Photo')
else:
item_main = []
for item in item_main:
item_info = {'section_id': helpers.get_xml_attr(item, 'key'),
'section_type': helpers.get_xml_attr(item, 'type'),
'media_type': helpers.get_xml_attr(item, 'type'),
'rating_key': helpers.get_xml_attr(item, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(item, 'parentRatingKey'),
'grandparent_rating_key': helpers.get_xml_attr(item, 'grandparentRatingKey'),
'title': helpers.get_xml_attr(item, 'title'),
'parent_title': helpers.get_xml_attr(item, 'parentTitle'),
'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'),
'media_index': helpers.get_xml_attr(item, 'index'),
'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'),
'year': helpers.get_xml_attr(item, 'year'),
'thumb': helpers.get_xml_attr(item, 'thumb'),
'parent_thumb': helpers.get_xml_attr(item, 'parentThumb'),
'grandparent_thumb': helpers.get_xml_attr(item, 'grandparentThumb'),
'added_at': helpers.get_xml_attr(item, 'addedAt')
}
if get_media_info:
item_media = item.getElementsByTagName('Media')
for media in item_media:
media_info = {'container': helpers.get_xml_attr(media, 'container'),
'bitrate': helpers.get_xml_attr(media, 'bitrate'),
'video_codec': helpers.get_xml_attr(media, 'videoCodec'),
'video_resolution': helpers.get_xml_attr(media, 'videoResolution'),
'video_framerate': helpers.get_xml_attr(media, 'videoFrameRate'),
'audio_codec': helpers.get_xml_attr(media, 'audioCodec'),
'audio_channels': helpers.get_xml_attr(media, 'audioChannels'),
'file': helpers.get_xml_attr(media.getElementsByTagName('Part')[0], 'file'),
'file_size': helpers.get_xml_attr(media.getElementsByTagName('Part')[0], 'size'),
}
library_list.append(library_output)
item_info.update(media_info)
output = {'library_count': helpers.get_xml_attr(xml_head[0], 'totalSize'),
'count_type': helpers.get_xml_attr(xml_head[0], 'title2'),
'library_list': library_list
childern_list.append(item_info)
output = {'library_count': library_count,
'childern_list': childern_list
}
return output
def get_library_details(self):
@ -1590,57 +1685,44 @@ class PmsConnect(object):
libraries_list = server_libraries['libraries_list']
for library in libraries_list:
library_type = library['type']
section_key = library['key']
library_list = self.get_library_children(library_type, section_key)
section_type = library['section_type']
section_id = library['section_id']
children_list = self.get_library_children(section_id=section_id, section_type=section_type, count='1')
if library_list['library_count'] != '0':
library_stats = {'key': library['key'],
'title': library['title'],
'type': library_type,
if children_list['library_count'] != '0':
library_stats = {'section_id': library['section_id'],
'section_name': library['section_name'],
'section_type': section_type,
'thumb': library['thumb'],
'art': library['art'],
'count': library_list['library_count'],
'count_type': library_list['count_type']
'count': children_list['library_count']
}
if library_type == 'show':
parent_list = self.get_library_children(library_type='season', section_key=section_key)
parent_stats = {'parent_count': parent_list['library_count'],
'parent_count_type': 'All Seasons'
}
if section_type == 'show':
parent_list = self.get_library_children(section_id=section_id, section_type='season', count='1')
parent_stats = {'parent_count': parent_list['library_count']}
library_stats.update(parent_stats)
child_list = self.get_library_children(library_type='episode', section_key=section_key)
child_stats = {'child_count': child_list['library_count'],
'child_count_type': 'All Episodes'
}
child_list = self.get_library_children(section_id=section_id, section_type='episode', count='1')
child_stats = {'child_count': child_list['library_count']}
library_stats.update(child_stats)
if library_type == 'artist':
parent_list = self.get_library_children(library_type='album', section_key=section_key)
parent_stats = {'parent_count': parent_list['library_count'],
'parent_count_type': 'All Seasons'
}
if section_type == 'artist':
parent_list = self.get_library_children(section_id=section_id, section_type='album', count='1')
parent_stats = {'parent_count': parent_list['library_count']}
library_stats.update(parent_stats)
child_list = self.get_library_children(library_type='track', section_key=section_key)
child_stats = {'child_count': child_list['library_count'],
'child_count_type': 'All Albums'
}
child_list = self.get_library_children(section_id=section_id, section_type='track', count='1')
child_stats = {'child_count': child_list['library_count']}
library_stats.update(child_stats)
if library_type == 'photo':
parent_list = self.get_library_children(library_type='photoAlbum', section_key=section_key)
parent_stats = {'parent_count': parent_list['library_count'],
'parent_count_type': 'All Photo Albums'
}
if section_type == 'photo':
parent_list = self.get_library_children(section_id=section_id, section_type='photoAlbum', count='1')
parent_stats = {'parent_count': parent_list['library_count']}
library_stats.update(parent_stats)
child_list = self.get_library_children(library_type='picture', section_key=section_key)
child_stats = {'child_count': child_list['library_count'],
'child_count_type': 'All Photos'
}
child_list = self.get_library_children(section_id=section_id, section_type='picture', count='1')
child_stats = {'child_count': child_list['library_count']}
library_stats.update(child_stats)
server_library_stats.append(library_stats)
@ -1782,7 +1864,7 @@ class PmsConnect(object):
else:
match_type = 'index'
library_id = None
section_id = None
library_name = None
# get grandparent rating key
@ -1790,7 +1872,7 @@ class PmsConnect(object):
try:
metadata = self.get_metadata_details(rating_key=rating_key)
rating_key = metadata['metadata']['parent_rating_key']
library_id = metadata['metadata']['library_id']
section_id = metadata['metadata']['section_id']
library_name = metadata['metadata']['library_name']
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to get parent_rating_key for get_rating_keys_list: %s." % e)
@ -1800,7 +1882,7 @@ class PmsConnect(object):
try:
metadata = self.get_metadata_details(rating_key=rating_key)
rating_key = metadata['metadata']['grandparent_rating_key']
library_id = metadata['metadata']['library_id']
section_id = metadata['metadata']['section_id']
library_name = metadata['metadata']['library_name']
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to get grandparent_rating_key for get_rating_keys_list: %s." % e)
@ -1875,10 +1957,10 @@ class PmsConnect(object):
key_list = {key:
{'rating_key': int(rating_key),
'children': parents },
'library_id': library_id,
'library_name': library_name
}
'section_id': section_id,
'library_name': library_name
}
return key_list
def get_server_response(self):

View file

@ -73,6 +73,11 @@ class Users(object):
rows = []
for item in users:
if item['friendly_name']:
friendly_name = item['friendly_name']
else:
friendly_name = item['username']
if item['media_type'] == 'episode' and item['parent_thumb']:
thumb = item['parent_thumb']
elif item['media_type'] == 'episode':
@ -114,7 +119,7 @@ class Users(object):
'recordsTotal': query['totalCount'],
'data': rows,
'draw': query['draw']
}
}
return dict
@ -199,7 +204,7 @@ class Users(object):
'recordsTotal': query['totalCount'],
'data': rows,
'draw': query['draw']
}
}
return dict

View file

@ -234,7 +234,11 @@ class WebInterface(object):
@cherrypy.expose
def libraries(self):
return serve_template(templatename="libraries.html", title="Libraries")
config = {
"update_section_ids": plexpy.CONFIG.UPDATE_SECTION_IDS
}
return serve_template(templatename="libraries.html", title="Libraries", config=config)
@cherrypy.expose
def get_library_list(self, **kwargs):
@ -300,10 +304,10 @@ class WebInterface(object):
return status_message
@cherrypy.expose
def get_library_watch_time_stats(self, library_id=None, **kwargs):
def get_library_watch_time_stats(self, section_id=None, **kwargs):
library_data = libraries.Libraries()
result = library_data.get_watch_time_stats(library_id=library_id)
result = library_data.get_watch_time_stats(section_id=section_id)
if result:
return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats")
@ -312,10 +316,10 @@ class WebInterface(object):
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
@cherrypy.expose
def get_library_user_stats(self, library_id=None, **kwargs):
def get_library_user_stats(self, section_id=None, **kwargs):
library_data = libraries.Libraries()
result = library_data.get_user_stats(library_id=library_id)
result = library_data.get_user_stats(section_id=section_id)
if result:
return serve_template(templatename="library_user_stats.html", data=result, title="Player Stats")
@ -324,10 +328,10 @@ class WebInterface(object):
return serve_template(templatename="library_user_stats.html", data=None, title="Player Stats")
@cherrypy.expose
def get_library_recently_watched(self, library_id=None, limit='10', **kwargs):
def get_library_recently_watched(self, section_id=None, limit='10', **kwargs):
library_data = libraries.Libraries()
result = library_data.get_recently_watched(library_id=library_id, limit=limit)
result = library_data.get_recently_watched(section_id=section_id, limit=limit)
if result:
return serve_template(templatename="user_recently_watched.html", data=result, title="Recently Watched")
@ -336,10 +340,10 @@ class WebInterface(object):
return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched")
@cherrypy.expose
def get_library_recently_added(self, library_id=None, limit='10', **kwargs):
def get_library_recently_added(self, section_id=None, limit='10', **kwargs):
library_data = pmsconnect.PmsConnect()
result = library_data.get_recently_added_details(library_id=library_id, count=limit)
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_recently_added_details(section_id=section_id, count=limit)
if result:
return serve_template(templatename="library_recently_added.html", data=result['recently_added'], title="Recently Added")
@ -347,6 +351,39 @@ class WebInterface(object):
logger.warn(u"Unable to retrieve data for get_library_recently_added.")
return serve_template(templatename="library_recently_added.html", data=None, title="Recently Added")
@cherrypy.expose
def get_library_media_info(self, section_id=None, **kwargs):
library_data = libraries.Libraries()
result = library_data.get_datatables_media_info(section_id=section_id, kwargs=kwargs)
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
@cherrypy.expose
def get_library_media_info2(self, section_id=None, section_type=None, rating_key=None, **kwargs):
library_data = libraries.Libraries()
result = library_data.get_datatables_media_info2(section_id=section_id,
section_type=section_type,
rating_key=rating_key,
kwargs=kwargs)
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
@cherrypy.expose
def get_library_unwatched(self, section_id=None, section_type=None, **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_library_children(section_id=section_id,
section_type=section_type,
get_media_info=True,
kwargs=kwargs)
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(result)
@cherrypy.expose
def delete_all_library_history(self, section_id, **kwargs):
library_data = libraries.Libraries()
@ -395,6 +432,35 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
@cherrypy.expose
def update_section_ids(self, **kwargs):
logger.debug(u"Updating section_id's in database.")
library_data = libraries.Libraries()
result = library_data.update_section_ids()
if result:
logger.debug(u"Updated all section_id's in database.")
plexpy.CONFIG.UPDATE_SECTION_IDS = 0
plexpy.CONFIG.write()
return "Section ids updated."
else:
logger.debug(u"Unable to update section_id's in database.")
return "Unable to update section ids in database."
@cherrypy.expose
def delete_datatable_media_info_cache(self, section_id, **kwargs):
library_data = libraries.Libraries()
if section_id:
delete_row = library_data.delete_datatable_media_info_cache(section_id=section_id)
if delete_row:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': delete_row})
else:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
##### Users #####
@ -595,7 +661,7 @@ class WebInterface(object):
custom_where.append(['session_history.reference_id', reference_id])
if 'section_id' in kwargs:
section_id = kwargs.get('section_id', "")
custom_where.append(['session_history_metadata.library_id', section_id])
custom_where.append(['session_history_metadata.section_id', section_id])
if 'media_type' in kwargs:
media_type = kwargs.get('media_type', "")
if media_type != 'all':
@ -1348,14 +1414,10 @@ class WebInterface(object):
@cherrypy.expose
def info(self, rating_key=None, source=None, **kwargs):
# temporary until I find a beter place to put this
#data_factory.update_library_ids()
metadata = None
config = {
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
"update_library_ids": plexpy.CONFIG.UPDATE_LIBRARY_IDS
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
}
if source == 'history':
@ -1371,7 +1433,6 @@ class WebInterface(object):
return serve_template(templatename="info.html", data=metadata, title="Info", config=config)
else:
return self.update_metadata(rating_key)
#raise cherrypy.InternalRedirect("/update_metadata?rating_key=" + rating_key)
@cherrypy.expose
def get_item_children(self, rating_key='', **kwargs):
@ -1493,24 +1554,7 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
@cherrypy.expose
def update_history_rating_key(self, old_rating_key, new_rating_key, media_type, **kwargs):
data_factory = datafactory.DataFactory()
pms_connect = pmsconnect.PmsConnect()
old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type)
new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type)
update_db = data_factory.update_rating_key(old_key_list=old_key_list,
new_key_list=new_key_list,
media_type=media_type)
if update_db:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': update_db})
else:
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps({'message': 'no data received'})
# test code
@cherrypy.expose
@ -1537,22 +1581,6 @@ class WebInterface(object):
else:
logger.warn(u"Unable to retrieve data for get_old_rating_keys.")
@cherrypy.expose
def update_library_ids(self, **kwargs):
logger.debug(u"Updating library_id's in database.")
data_factory = datafactory.DataFactory()
result = data_factory.update_library_ids()
if result:
logger.debug(u"Updated all library_id's in database.")
plexpy.CONFIG.UPDATE_LIBRARY_IDS = 0
plexpy.CONFIG.write()
return "Library ids updated."
else:
logger.debug(u"Unable to update library_id's in database.")
return "Unable to update library ids in database."
##### API #####