Add Show Contact Sheet to server WebUI
This commit is contained in:
parent
4ad22bb46a
commit
41a0553c45
|
@ -80,6 +80,46 @@
|
|||
|
||||
<div id="alert-container"></div>
|
||||
|
||||
<!-- Contact sheet modal -->
|
||||
<div class="modal fade" id="contactsheetModal" tabindex="-1" aria-labelledby="contactsheetModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="max-width: 80vw;">
|
||||
<div class="modal-content modal-content-contactsheet">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title title-truncate" id="contactsheetModalLabel">Image Title</h6>
|
||||
<button type="button" class="btn btn-danger fa fa-times-circle" data-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<img id="imageModalImage" src="" alt="Contact Sheet" class="img-fluid" style="max-width: 100%; height: auto;" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="deleteRecordingBtn" type="button" class="btn btn-danger"><i class="fa fa-trash"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="modal fade" id="contactsheetModal" tabindex="-1" aria-labelledby="contactsheetModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="max-width: 80vw;">
|
||||
<div class="modal-content modal-content-contactsheet">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title title-truncate" id="contactsheetModalLabel">Image Title</h6>
|
||||
<button type="button" class="btn btn-danger fas fa-times-circle" data-dismiss="modal" aria-label="Close">
|
||||
<span class="sr-only">Close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<img id="imageModalImage" src="placeholder.jpg" alt="Contact Sheet" class="img-fluid" style="max-width: 100%; height: auto;" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger fas fa-trash"
|
||||
data-bind="enable: (ko_status() === 'FINISHED' && ko_contactSheet() !== null), click: show, attr: { title: (ko_status() === 'FINISHED' && ko_contactSheet() !== null) ? 'Show contact sheet' : 'No contact sheet' }">
|
||||
<span class="sr-only">Show contact sheet</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<section id="models" class="tab-pane fade active show" role="tabpanel" aria-labelledby="models-tab">
|
||||
<div class="container">
|
||||
|
@ -182,12 +222,12 @@
|
|||
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'model.name'}">Model</a></th>
|
||||
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'startDate'}">Date</a></th>
|
||||
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'ko_status'}">Status</a></th>
|
||||
<!-- <th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'progress'}">Progress</a></th> -->
|
||||
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'sizeInByte'}">Size</a></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: recordings">
|
||||
|
@ -195,8 +235,10 @@
|
|||
<td><a data-bind="attr: { href: model.url, title: model.name }, text: model.name"></a></td>
|
||||
<td data-bind="text: ko_date"></td>
|
||||
<td data-bind="text: ko_status"></td>
|
||||
<!-- <td data-bind="text: ko_progressString"></td> -->
|
||||
<td data-bind="text: ko_size"></td>
|
||||
<td>
|
||||
<button class="btn btn-secondary fa fa-image" data-bind="enable: (ko_status() == 'FINISHED' && ko_contactSheet() !== null), click: show, attr: { title: (ko_status() == 'FINISHED' && ko_contactSheet() !== null) ? 'Show contact sheet' : 'No contact sheet' }"></button>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-secondary fa fa-play" title="Play recording" data-bind="click: play"></button>
|
||||
</td>
|
||||
|
@ -207,12 +249,12 @@
|
|||
<td>
|
||||
<button class="btn btn-secondary fa fa-recycle" title="Rerun processing"
|
||||
data-bind="enable: (ko_status() == 'WAITING' || ko_status() == 'FAILED' || ko_status() == 'FINISHED'),
|
||||
click: ctbrec.rerunProcessing"></button>
|
||||
click: rerunProcessing"></button>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-secondary fa fa-trash" title="Delete recording"
|
||||
data-bind="enable: (ko_status() == 'FINISHED' || ko_status() == 'WAITING' || ko_status() == 'FAILED'),
|
||||
click: ctbrec.deleteRecording"></button>
|
||||
click: deleteRecording"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -297,9 +339,10 @@
|
|||
<script src="vendor/CryptoJS/hmac-sha256.js"></script>
|
||||
|
||||
<!-- ctbrec stuff -->
|
||||
<script src="config.js"></script>
|
||||
<script src="recordings.js"></script>
|
||||
<script src="models.js"></script>
|
||||
<script src="config.js"></script>
|
||||
|
||||
<script>
|
||||
let cookieJar = cookie.cookie;
|
||||
cookieJar.defaults.expires = 365 * 100;
|
||||
|
@ -450,17 +493,17 @@
|
|||
});
|
||||
}
|
||||
|
||||
function addModelKeyPressed(e) {
|
||||
let charCode = (typeof e.which === "number") ? e.which : e.keyCode;
|
||||
|
||||
if (charCode === 13) {
|
||||
addModel();
|
||||
} else {
|
||||
$('#addModelByUrl').autocomplete({
|
||||
source: ["AmateurTv:", "BongaCams:", "Cam4:", "Camsoda:", "Chaturbate:", "CherryTv:", "Dreamcam:", "Fc2Live:", "Flirt4Free:", "LiveJasmin:", "MVLive:", "MyFreeCams:", "SecretFriends:", "Showup:", "Streamate:", "Streamray:", "Stripchat:", "XloveCam:"]
|
||||
});
|
||||
}
|
||||
}
|
||||
function addModelKeyPressed(e) {
|
||||
let charCode = (typeof e.which === "number") ? e.which : e.keyCode;
|
||||
|
||||
if (charCode === 13) {
|
||||
addModel();
|
||||
} else {
|
||||
$('#addModelByUrl').autocomplete({
|
||||
source: ["AmateurTv:", "BongaCams:", "Cam4:", "Camsoda:", "Chaturbate:", "CherryTv:", "Dreamcam:", "Fc2Live:", "Flirt4Free:", "LiveJasmin:", "MVLive:", "MyFreeCams:", "SecretFriends:", "Showup:", "Streamate:", "Streamray:", "Stripchat:", "XloveCam:"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let ctbrec = {
|
||||
add: function(input, duration, onsuccess) {
|
||||
|
@ -588,7 +631,7 @@
|
|||
}
|
||||
},
|
||||
|
||||
rerunProcessing: function(recording) {
|
||||
/* rerunProcessing: function(recording) {
|
||||
let name = recording.model.name + ' ' + recording.ko_date();
|
||||
try {
|
||||
let action = '{"action": "rerunPostProcessing", "recording": ' + JSON.stringify(recording) + '}';
|
||||
|
@ -634,7 +677,7 @@
|
|||
} catch (e) {
|
||||
if (console) console.log('Unexpected error', e);
|
||||
}
|
||||
}
|
||||
} */
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
|
@ -682,6 +725,11 @@
|
|||
throughput
|
||||
});
|
||||
|
||||
// Required for contact sheets
|
||||
window.onload = function() {
|
||||
loadConfig();
|
||||
};
|
||||
|
||||
updateOnlineModels();
|
||||
updateRecordings();
|
||||
|
||||
|
|
|
@ -58,6 +58,54 @@ function download(recording) {
|
|||
location.href = src;
|
||||
}
|
||||
|
||||
function rerunProcessing(recording) {
|
||||
let name = recording.model.name + ' ' + recording.ko_date();
|
||||
try {
|
||||
let action = '{"action": "rerunPostProcessing", "recording": ' + JSON.stringify(recording) + '}';
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '../rec',
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 60000,
|
||||
headers: {'CTBREC-HMAC': CryptoJS.HmacSHA256(action, hmac)},
|
||||
data: action
|
||||
});
|
||||
} catch (e) {
|
||||
if (console) console.log('Unexpected error', e);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteRecording(recording) {
|
||||
let name = recording.model.name + ' ' + recording.ko_date();
|
||||
try {
|
||||
let action = '{"action": "delete", "recording": ' + JSON.stringify(recording) + '}';
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '../rec',
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 60000,
|
||||
headers: {'CTBREC-HMAC': CryptoJS.HmacSHA256(action, hmac)},
|
||||
data: action
|
||||
})
|
||||
.done(function(data) {
|
||||
if (data.status === 'success') {
|
||||
$.notify('Removed recording ' + name, 'info');
|
||||
observableRecordingsArray.remove(recording);
|
||||
} else {
|
||||
$.notify('Removing recording ' + name + ' failed', 'error');
|
||||
}
|
||||
})
|
||||
.fail(function(jqXHR, textStatus, errorThrown) {
|
||||
if (console) console.log(textStatus, errorThrown);
|
||||
$.notify('Removing recording ' + name + ' failed', 'error');
|
||||
});
|
||||
} catch (e) {
|
||||
if (console) console.log('Unexpected error', e);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateSize(sizeInByte) {
|
||||
let size = sizeInByte;
|
||||
let unit = "Bytes";
|
||||
|
@ -84,6 +132,32 @@ function isRecordingInArray(array, recording) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function show(recording) {
|
||||
if (console) console.log("Show: " + recording.ko_contactSheet());
|
||||
let localFilePath = recording.ko_contactSheet();
|
||||
let src = getImageUrl(recording.ko_contactSheet());
|
||||
if (console) console.log("Show: " + src);
|
||||
if (src) {
|
||||
if (console) console.log("Image: " + src);
|
||||
// Update the modal's image source and display the modal
|
||||
let modalImage = document.getElementById('imageModalImage');
|
||||
modalImage.src = src;
|
||||
let fileName = localFilePath.split('\\').pop().split('/').pop();
|
||||
let modalTitle = document.getElementById('contactsheetModalLabel');
|
||||
modalTitle.innerText = fileName; // Set the title to the file name
|
||||
// Show the modal (assuming you are using Bootstrap's modal)
|
||||
$('#contactsheetModal').modal('show');
|
||||
// Update the delete button to use this recording
|
||||
document.getElementById('deleteRecordingBtn').onclick = function() {
|
||||
$('#contactsheetModal').modal('hide');
|
||||
deleteRecording(recording);
|
||||
};
|
||||
} else {
|
||||
console.log('No contact sheet available');
|
||||
}
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes recordings from the server with the displayed knockout recordings table
|
||||
*/
|
||||
|
@ -112,9 +186,11 @@ function syncRecordings(recordings) {
|
|||
'second' : '2-digit'
|
||||
})
|
||||
});
|
||||
recording.ko_progressString = ko.observable(recording.progress === -1 ? '' : recording.progress);
|
||||
recording.ko_size = ko.observable(calculateSize(recording.sizeInByte));
|
||||
recording.ko_status = ko.observable(recording.status);
|
||||
recording.ko_contactSheet = ko.observable(
|
||||
recording.associatedFiles?.find(file => file.endsWith('.jpg') || file.endsWith('.png')) || null
|
||||
);
|
||||
if (recording.singleFile) {
|
||||
recording.playlist = '/hls/' + recording.id;
|
||||
} else {
|
||||
|
@ -134,9 +210,13 @@ function syncRecordings(recordings) {
|
|||
r.sizeInByte = recording.sizeInByte;
|
||||
r.status = recording.status;
|
||||
r.startDate = recording.startDate;
|
||||
r.ko_progressString(recording.progress === -1 ? '' : (recording.progress + '%'));
|
||||
r.ko_size(calculateSize(recording.sizeInByte));
|
||||
r.ko_status(recording.status);
|
||||
r.ko_contactSheet(
|
||||
recording.associatedFiles && recording.associatedFiles.find(file =>
|
||||
file.endsWith('.jpg') || file.endsWith('.png')
|
||||
) || null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,4 +284,27 @@ function updateDiskSpace() {
|
|||
if (console)
|
||||
console.log(textStatus, errorThrown);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getImageUrl(localFilePath) {
|
||||
// if (!localFilePath) return null; // Can be removed since button is disabled if no valid content
|
||||
if (console) console.log("getImageUrl: " + localFilePath);
|
||||
// Find recordingsDir from observableSettingsArray
|
||||
let recordingsDirEntry = ko.utils.arrayFirst(observableSettingsArray(), item => item.key === 'recordingsDir');
|
||||
if (!recordingsDirEntry) return null; // Can be removed since it has to exist in the config
|
||||
let recordingsDir = recordingsDirEntry.ko_value();
|
||||
// Normalize paths to use forward slashes
|
||||
const normalizedLocalPath = localFilePath.replace(/\\/g, '/');
|
||||
const normalizedRecordingsDir = recordingsDir.replace(/\\/g, '/');
|
||||
const basePath = normalizedRecordingsDir.endsWith('/') ?
|
||||
normalizedRecordingsDir :
|
||||
normalizedRecordingsDir + '/';
|
||||
if (console) console.log("normalizedLocalPath: " + normalizedLocalPath);
|
||||
if (console) console.log("normalizedRecordingsDir: " + normalizedRecordingsDir);
|
||||
if (console) console.log("basePath: " + basePath);
|
||||
// Check if localFilePath starts with recordingsDir and replace it
|
||||
if (normalizedLocalPath.startsWith(basePath)) {
|
||||
return "/image/recording/" + normalizedLocalPath.substring(basePath.length);
|
||||
}
|
||||
return null; // Return null if the path doesn't match - in theory shouldn't happen
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue