diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css
index ff44dbea..2842814e 100644
--- a/data/interfaces/default/css/plexpy.css
+++ b/data/interfaces/default/css/plexpy.css
@@ -2350,4 +2350,40 @@ a .home-platforms-instance-list-oval:hover,
.dashboard-instance {
width: 100%;
}
+}
+
+table.display tr.shown + tr div.slider {
+ display: none;
+}
+table.display tr.shown + tr > td {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+table.display tr.shown + tr:hover {
+ background-color: rgba(255,255,255,0);
+}
+table.display tr.shown + tr:hover a,
+table.display tr.shown + tr td:hover a,
+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 {
+ color: #F9AA03;
+}
+table.display tr.shown + tr .pagination > .disabled > a {
+ color: #444444;
+}
+table.display tr.shown + tr .pagination > li > a:hover {
+ color: #23527c;
+}
+table[id^='history_child'] {
+ margin-top: 0;
+ margin-left: -4px;
+ opacity: .6;
+}
+table[id^='history_child'] thead th {
+ line-height: 0;
+ height: 0 !important;
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js
index 1cccbf9b..dcd94933 100644
--- a/data/interfaces/default/js/tables/history_table.js
+++ b/data/interfaces/default/js/tables/history_table.js
@@ -46,13 +46,17 @@ history_table_options = {
"createdCell": function (td, cellData, rowData, row, col) {
if (rowData['stopped'] === null) {
$(td).html('Currently watching...');
+ } else if (rowData['group_count'] > 1) {
+ date = moment(cellData, "X").format(date_format);
+ expand_history = '';
+ $(td).html('
');
} else {
- $(td).html(moment(cellData,"X").format(date_format));
+ $(td).html(moment(cellData, "X").format(date_format));
}
},
"searchable": false,
"width": "8%",
- "className": "no-wrap"
+ "className": "no-wrap expand-history"
},
{
"targets": [2],
@@ -83,7 +87,8 @@ history_table_options = {
$(td).html('n/a');
}
} else {
- $(td).html(' ' + cellData + '');
+ external_ip = '';
+ $(td).html(''+ external_ip + cellData + '');
}
} else {
$(td).html('n/a');
@@ -105,7 +110,7 @@ history_table_options = {
} else if (rowData['video_decision'] === 'direct play' || rowData['video_decision'] === '') {
transcode_dec = '';
}
- $(td).html('');
+ $(td).html('');
}
},
"width": "15%",
@@ -121,16 +126,16 @@ history_table_options = {
if (rowData['media_type'] === 'movie') {
media_type = '';
thumb_popover = '' + cellData + ' (' + rowData['year'] + ')'
- $(td).html('');
+ $(td).html('');
} else if (rowData['media_type'] === 'episode') {
media_type = '';
thumb_popover = '' + cellData + ' \
(S' + rowData['parent_media_index'] + '· E' + rowData['media_index'] + ')'
- $(td).html('');
+ $(td).html('');
} else if (rowData['media_type'] === 'track') {
media_type = '';
thumb_popover = '' + cellData + ' (' + rowData['parent_title'] + ')'
- $(td).html('');
+ $(td).html('');
} else {
$(td).html('' + cellData + '');
}
@@ -155,7 +160,7 @@ history_table_options = {
{
"targets": [7],
"data":"paused_counter",
- "render": function ( data, type, full ) {
+ "render": function (data, type, full) {
if (data !== null) {
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
} else {
@@ -183,7 +188,7 @@ history_table_options = {
{
"targets": [9],
"data":"duration",
- "render": function ( data, type, full ) {
+ "render": function (data, type, full) {
if (data !== null) {
return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins';
} else {
@@ -197,7 +202,7 @@ history_table_options = {
{
"targets": [10],
"data":"percent_complete",
- "render": function ( data, type, full ) {
+ "render": function (data, type, full) {
if (data > 80) {
return ''
} else if (data > 40) {
@@ -218,6 +223,8 @@ history_table_options = {
$('#ajaxMsg').fadeOut();
// Create the tooltips.
+ $('.expand-history-tooltip').tooltip({ container: 'body' });
+ $('.external-ip-tooltip').tooltip();
$('.transcode-tooltip').tooltip();
$('.media-type-tooltip').tooltip();
$('.watched-tooltip').tooltip();
@@ -231,24 +238,57 @@ history_table_options = {
});
if ($('#row-edit-mode').hasClass('active')) {
- $('.delete-control').each(function() {
+ $('.delete-control').each(function () {
$(this).removeClass('hidden');
});
}
+
+ history_table.rows().every(function () {
+ var rowData = this.data();
+ if (rowData['group_count'] != 1 && rowData['group_start_id'] in history_child_table) {
+ // if grouped row and a child table was already created
+ this.child(childTableFormat(rowData)).show();
+ createChildTable(this, rowData)
+ }
+ });
},
- "preDrawCallback": function(settings) {
+ "preDrawCallback": function (settings) {
var msg = " Fetching rows...
";
showMsg(msg, false, false, 0)
},
- "rowCallback": function (row, rowData) {
- if ($.inArray(rowData['id'], history_to_delete) !== -1) {
+ "rowCallback": function (row, rowData, rowIndex) {
+ if (rowData['group_count'] == 1) {
+ // if no grouped rows simply toggle the delete button
+ if ($.inArray(rowData['id'], history_to_delete) !== -1) {
+ $(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
+ }
+ } else {
+ // if grouped rows
+ // toggle the parent button to danger
$(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
+ // check if any child rows are not selected
+ for (var i = rowData['group_start_id']; i <= rowData['id']; i++) {
+ var index = $.inArray(i, history_to_delete);
+ if (index == -1) {
+ // if any child row is not selected, toggle parent button to warning
+ $(row).find('button[data-id="' + rowData['id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
+ break;
+ }
+ }
}
+
+ if (rowData['group_count'] != 1 && rowData['group_start_id'] in history_child_table) {
+ // if grouped row and a child table was already created
+ $(row).addClass('shown')
+ history_table.row(row).child(childTableFormat(rowData)).show();
+ }
+
}
}
-$('#history_table').on('click', 'td.modal-control', function () {
- var tr = $(this).parents('tr');
+// Parent table platform modal
+$('#history_table').on('click', '> tbody > tr > td.modal-control', function () {
+ var tr = $(this).closest('tr');
var row = history_table.row( tr );
var rowData = row.data();
@@ -266,8 +306,9 @@ $('#history_table').on('click', 'td.modal-control', function () {
showStreamDetails();
});
-$('#history_table').on('click', 'td.modal-control-ip', function () {
- var tr = $(this).parents('tr');
+// Parent table ip address modal
+$('#history_table').on('click', '> tbody > tr > td.modal-control-ip', function () {
+ var tr = $(this).closest('tr');
var row = history_table.row( tr );
var rowData = row.data();
@@ -288,16 +329,234 @@ $('#history_table').on('click', 'td.modal-control-ip', function () {
getUserLocation(rowData['ip_address']);
});
-$('#history_table').on('click', 'td.delete-control > button', function () {
- var tr = $(this).parents('tr');
+// Parent table delete mode
+$('#history_table').on('click', '> tbody > tr > td.delete-control > button', function () {
+ var tr = $(this).closest('tr');
var row = history_table.row( tr );
var rowData = row.data();
- var index = $.inArray(rowData['id'], history_to_delete);
- if (index === -1) {
- history_to_delete.push(rowData['id']);
+ if (rowData['group_count'] == 1) {
+ // if no grouped rows simply add or remove row from history_to_delete
+ var index = $.inArray(rowData['id'], history_to_delete);
+ if (index === -1) {
+ history_to_delete.push(rowData['id']);
+ } else {
+ history_to_delete.splice(index, 1);
+ }
+ $(this).toggleClass('btn-warning').toggleClass('btn-danger');
} else {
- history_to_delete.splice(index, 1);
+ // if grouped rows
+ if ($(this).hasClass('btn-warning')) {
+ // add all grouped rows to history_to_delete
+ for (var i = rowData['group_start_id']; i <= rowData['id']; i++) {
+ var index = $.inArray(i, history_to_delete);
+ if (index == -1) {
+ history_to_delete.push(i);
+ }
+ }
+ $(this).toggleClass('btn-warning').toggleClass('btn-danger');
+ if (row.child.isShown()) {
+ // if child table is visible, toggle all child buttons to danger
+ tr.next().find('td.delete-control > button.btn-warning').toggleClass('btn-warning').toggleClass('btn-danger');
+ }
+ } else {
+ // remove all grouped rows to history_to_delete
+ for (var i = rowData['group_start_id']; i <= rowData['id']; i++) {
+ var index = $.inArray(i, history_to_delete);
+ if (index != -1) {
+ history_to_delete.splice(index, 1);
+ }
+ }
+ $(this).toggleClass('btn-warning').toggleClass('btn-danger');
+ if (row.child.isShown()) {
+ // if child table is visible, toggle all child buttons to warning
+ tr.next().find('td.delete-control > button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
+ }
+ }
}
- $(this).toggleClass('btn-warning').toggleClass('btn-danger');
-});
\ No newline at end of file
+});
+
+// Parent table expand detailed history
+$('#history_table').on('click', '> tbody > tr > td.expand-history a', function () {
+ var tr = $(this).closest('tr');
+ var row = history_table.row(tr);
+ var rowData = row.data();
+
+ if (row.child.isShown()) {
+ $('div.slider', row.child()).slideUp(function () {
+ row.child.hide();
+ tr.removeClass('shown');
+ delete history_child_table[rowData['group_start_id']];
+ });
+ } else {
+ tr.addClass('shown');
+ row.child(childTableFormat(rowData)).show();
+ createChildTable(row, rowData);
+ }
+});
+
+
+// Initialize the detailed history child table options using the parent table options
+function childTableOptions(rowData) {
+ history_child_options = history_table_options;
+ // Remove settings that are not necessary
+ history_child_options.searching = false;
+ history_child_options.lengthChange = false;
+ history_child_options.info = false;
+ history_child_options.pageLength = 10;
+ history_child_options.bStateSave = false;
+ history_child_options.ajax = {
+ "url": "get_history",
+ type: "post",
+ data: function (d) {
+ return {
+ 'json_data': JSON.stringify(d),
+ 'grouping': false,
+ 'group_start_id': rowData['group_start_id']
+ };
+ }
+ }
+ history_child_options.fnDrawCallback = function (settings) {
+ $('#ajaxMsg').fadeOut();
+
+ // Create the tooltips.
+ $('.expand-history-tooltip').tooltip({ container: 'body' });
+ $('.external-ip-tooltip').tooltip();
+ $('.transcode-tooltip').tooltip();
+ $('.media-type-tooltip').tooltip();
+ $('.watched-tooltip').tooltip();
+ $('.thumb-tooltip').popover({
+ html: true,
+ trigger: 'hover',
+ placement: 'right',
+ content: function () {
+ return '';
+ }
+ });
+
+ if ($('#row-edit-mode').hasClass('active')) {
+ $('.delete-control').each(function () {
+ $(this).removeClass('hidden');
+ });
+ }
+
+ $(this).closest('div.slider').slideDown();
+ }
+
+ return history_child_options;
+}
+
+// Format the detailed history child table
+function childTableFormat(rowData) {
+ return '' +
+ '
' +
+ '' +
+ '' +
+ 'Delete | ' +
+ 'Time | ' +
+ 'User | ' +
+ 'IP Address | ' +
+ 'Platform | ' +
+ 'Title | ' +
+ 'Started | ' +
+ 'Paused | ' +
+ 'Stopped | ' +
+ 'Duration | ' +
+ ' | ' +
+ '
' +
+ '' +
+ '' +
+ '' +
+ '
' +
+ '
';
+}
+
+// Create the detailed history child table
+history_child_table = {};
+function createChildTable(row, rowData) {
+ history_child_options = childTableOptions(rowData);
+ // initialize the child table
+ history_child_table[rowData['group_start_id']] = $('#history_child-' + rowData['group_start_id']).DataTable(history_child_options);
+
+ // Set child table column visibility to match parent table
+ var visibility = history_table.columns().visible();
+ for (var i = 0; i < visibility.length; i++) {
+ if (!(visibility[i])) { history_child_table[rowData['group_start_id']].column(i).visible(visibility[i]); }
+ }
+ history_table.on('column-visibility', function (e, settings, colIdx, visibility) {
+ if (row.child.isShown()) {
+ history_child_table[rowData['group_start_id']].column(colIdx).visible(visibility);
+ }
+ });
+
+ // Child table platform modal
+ $('#history_child-' + rowData['group_start_id']).on('click', 'td.modal-control', function () {
+ var tr = $(this).closest('tr');
+ var childRow = history_child_table[rowData['group_start_id']].row(tr);
+ var childRowData = childRow.data();
+
+ function showStreamDetails() {
+ $.ajax({
+ url: 'get_stream_data',
+ data: { row_id: childRowData['id'], user: childRowData['friendly_name'] },
+ cache: false,
+ async: true,
+ complete: function (xhr, status) {
+ $("#info-modal").html(xhr.responseText);
+ }
+ });
+ }
+ showStreamDetails();
+ });
+
+ // Child table ip address modal
+ $('#history_child-' + rowData['group_start_id']).on('click', 'td.modal-control-ip', function () {
+ var tr = $(this).closest('tr');
+ var childRow = history_child_table[rowData['group_start_id']].row(tr);
+ var childRowData = childRow.data();
+
+ function getUserLocation(ip_address) {
+ if (isPrivateIP(ip_address)) {
+ return "n/a"
+ } else {
+ $.ajax({
+ url: 'get_ip_address_details',
+ data: { ip_address: ip_address },
+ async: true,
+ complete: function (xhr, status) {
+ $("#ip-info-modal").html(xhr.responseText);
+ }
+ });
+ }
+ }
+ getUserLocation(childRowData['ip_address']);
+ });
+
+ // Child table delete mode
+ $('#history_child-' + rowData['group_start_id']).on('click', 'td.delete-control > button', function () {
+ var tr = $(this).closest('tr');
+ var childRow = history_child_table[rowData['group_start_id']].row(tr);
+ var childRowData = childRow.data();
+
+ // add or remove row from history_to_delete
+ var index = $.inArray(childRowData['id'], history_to_delete);
+ if (index === -1) {
+ history_to_delete.push(childRowData['id']);
+ } else {
+ history_to_delete.splice(index, 1);
+ }
+ $(this).toggleClass('btn-warning').toggleClass('btn-danger');
+
+ tr.parents('tr').prev().find('td.delete-control > button.btn-warning').toggleClass('btn-warning').toggleClass('btn-danger');
+ // check if any child rows are not selected
+ for (var i = rowData['group_start_id']; i <= rowData['id']; i++) {
+ var index = $.inArray(i, history_to_delete);
+ if (index == -1) {
+ // if any child row is not selected, toggle parent button to warning
+ tr.parents('tr').prev().find('td.delete-control > button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
+ break;
+ }
+ }
+ });
+}
+