function add(input, duration, onsuccess) { try { let model = { type: null, name: '', url: input }; if (console) console.log(model); let action = input.startsWith('http') ? 'startByUrl' : 'startByName'; const msg = JSON.stringify({ action: action, model: model }); const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(msg, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: msg }) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`HTTP ${response.status}: ${text}`); }); } return response.json(); }) .then(data => { if (console) console.log(data); if (data.status === 'success') { if (onsuccess) { onsuccess.call(data); } $.notify('Model added', 'info'); } else { $.notify('Adding model failed', 'error'); } }) .catch(error => { if (console) console.log('Failed to add model:', error); $.notify('Adding model failed: ' + error.message, 'error'); }); } catch (e) { if (console) console.log('Unexpected error:', e); } } function resume(model) { try { const action = JSON.stringify({ action: "resume", model: model }); const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(action, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: action }) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`HTTP ${response.status}: ${text}`); }); } return response.json(); }) .then(data => { if (data.status === 'success') { $.notify('Recording of ' + model.name + ' resumed', 'info'); } else { $.notify('Resuming recording of model ' + model.name + ' failed', 'error'); } }) .catch(error => { if (console) console.log('Failed to resume recording:', error); $.notify('Resuming recording of model ' + model.name + ' failed: ' + error.message, 'error'); }); } catch (e) { if (console) console.log('Unexpected error:', e); } } function suspend(model) { try { const action = JSON.stringify({ action: "suspend", model: model }); const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(action, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: action }) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`HTTP ${response.status}: ${text}`); }); } return response.json(); }) .then(data => { if (data.status === 'success') { $.notify('Recording of ' + model.name + ' suspended', 'info'); } else { $.notify('Suspending recording of model ' + model.name + ' failed', 'error'); } }) .catch(error => { if (console) console.log('Failed to suspend recording:', error); $.notify('Suspending recording of model ' + model.name + ' failed: ' + error.message, 'error'); }); } catch (e) { if (console) console.log('Unexpected error:', e); } } function stop(model) { try { const action = JSON.stringify({ action: "stop", model: model }); const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(action, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: action }) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(`HTTP ${response.status}: ${text}`); }); } return response.json(); }) .then(data => { if (data.status === 'success') { $.notify('Removed ' + model.name, 'info'); observableModelsArray.remove(model); } else { $.notify('Removing model ' + model.name + ' failed', 'error'); } }) .catch(error => { if (console) console.log('Failed to remove model:', error); $.notify('Removing model ' + model.name + ' failed: ' + error.message, 'error'); }); } catch (e) { if (console) console.log('Unexpected error:', e); } } function updateOnlineModels() { try { const action = '{"action": "listOnline"}'; const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(action, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: action }) .then(response => { if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); }) .then(data => { if (data.status === 'success') { onlineModels = data.models; } else { if (console) console.log('Request failed:', data); } updateModels(); }) .catch(error => { if (console) console.log('Failed to fetch online models:', error); }); } catch (e) { if (console) console.log('Unexpected error:', e); } setTimeout(updateOnlineModels, 3000); } function isModelInArray(array, model) { for (let idx in array) { let m = array[idx]; if (m.url === model.url) { return true; } } return false; } /** * Synchronizes models from the server with the displayed knockout model table */ function syncModels(models) { // remove models from the observable array, which are not in the updated list var newModelsMap = new Map(models.map(m => [m.url, m])); observableModelsArray.remove(m => !newModelsMap.get(m.ko_url())); var modelsMap = new Map(observableModelsArray().map(m => [m.ko_url(), m])); for (let model of models) { const m = modelsMap.get(model.url); if (!m) { // add models to the observable array, which are new in the updated list model.ko_name = ko.observable(model.name); model.ko_url = ko.observable(model.url); model.ko_online = ko.observable(false); for (let i in onlineModels) { let onlineModel = onlineModels[i]; if (onlineModel.url === model.url) { model.ko_online(true); break; } } model.ko_recording = ko.observable(model.online && !model.suspended); model.ko_suspended = ko.observable(model.suspended); model.ko_recordUntil = ko.observable(model.recordUntil); model.swallowEvents = false; model.ko_suspended.subscribe(function(checked) { if (model.swallowEvents) { return; } if (!checked) { ctbrec.resume(model); } else { ctbrec.suspend(model); } }); observableModelsArray.push(model); } else { // update existing models m.ko_name(model.name); m.ko_url(model.url); let onlineState = false; for (let i in onlineModels) { let onlineModel = onlineModels[i]; if (onlineModel.url === model.url) { onlineState = true; break; } } m.ko_online(onlineState); m.swallowEvents = true; m.ko_suspended(model.suspended); m.swallowEvents = false; m.ko_recording(m.ko_online() && !m.ko_suspended()); // Update the recordUntil property to ensure clock icon refreshes if (m.ko_recordUntil && typeof m.ko_recordUntil === 'function') { m.ko_recordUntil(model.recordUntil); } else { m.ko_recordUntil = ko.observable(model.recordUntil); } } } } function updateModels() { try { const action = '{"action": "list"}'; const headers = { 'Content-Type': 'application/json' }; if (hmac && hmac.length > 0) { headers['CTBREC-HMAC'] = CryptoJS.HmacSHA256(action, hmac).toString(CryptoJS.enc.Base64); } fetch('../rec', { method: 'POST', headers: headers, body: action }) .then(response => { if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); }) .then(data => { if (data.status === 'success') { syncModels(data.models); } else { if (console) console.log('Request failed:', data); } }) .catch(error => { if (console) console.log('Failed to fetch models:', error); }); } catch (e) { if (console) console.log('Unexpected error:', e); } }