From 2bf6e9787ad1ae1c57b4b63f94606ba111c3a113 Mon Sep 17 00:00:00 2001 From: Jafea7 Date: Wed, 14 May 2025 22:40:16 +1000 Subject: [PATCH] Add Record Until to WebUI --- .../src/main/resources/html/static/index.html | 200 ++++++++++++++++-- .../src/main/resources/html/static/models.js | 75 ++++--- 2 files changed, 215 insertions(+), 60 deletions(-) diff --git a/server/src/main/resources/html/static/index.html b/server/src/main/resources/html/static/index.html index 2351001f..5ba7a2cd 100644 --- a/server/src/main/resources/html/static/index.html +++ b/server/src/main/resources/html/static/index.html @@ -7,9 +7,9 @@ - + - CTB Recorder ${project.version} + CTB Recorder 5.3.2 @@ -87,8 +87,37 @@
- +
+ +
+ + +
+
+
+
+ + + @@ -111,7 +140,7 @@ - + @@ -210,7 +239,14 @@ - + +
+ +
+
+ +
+ @@ -286,24 +322,146 @@ }; let hmac; - function addModelKeyPressed(e) { - let charCode = (typeof e.which === "number") ? e.which : e.keyCode; + // Toggle visibility of stopAt options + $(document).ready(function() { + $('#toggleStopAtOptions').click(function() { + $('#stopAtOptionsRow').toggle(); + + // Set default datetime to current time + 1 hour when showing options + if ($('#stopAtOptionsRow').is(':visible') && !$('#recordUntilDate').val()) { + let now = new Date(); + now.setHours(now.getHours() + 1); + + // Format the date for datetime-local input + let year = now.getFullYear(); + let month = (now.getMonth() + 1).toString().padStart(2, '0'); + let day = now.getDate().toString().padStart(2, '0'); + let hours = now.getHours().toString().padStart(2, '0'); + let minutes = now.getMinutes().toString().padStart(2, '0'); + + $('#recordUntilDate').val(`${year}-${month}-${day}T${hours}:${minutes}`); + } + }); + }); + + // Function to add model (shared by Enter key and Add button) + function addModel() { let val = $('#addModelByUrl').val(); - let duration = $('#recordingDuration').val(); // Get the recording duration input - if (charCode === 13) { - // Check if a valid duration is provided - let durationMinutes = duration ? parseInt(duration) : undefined; - ctbrec.add(val, durationMinutes, function() { - $('#addModelByUrl').val(''); - $('#recordingDuration').val(''); // Clear the recording duration input - }); - } else { - $('#addModelByUrl').autocomplete({ - source: ["BongaCams:", "Cam4:", "Camsoda:", "Chaturbate:", "Dreamcam:", "Fc2Live:", "Flirt4Free:", "MyFreeCams:", "Showup:", "Streamate:", "Streamray:", "Stripchat:", "XloveCam:"] - }); - } + + if (!val) return; // Don't do anything if the input is empty + + // Store if we need to add stopAt parameters + let useStopAt = $('#stopAtOptionsRow').is(':visible') && $('#recordUntilDate').val(); + let stopAtDate = useStopAt ? $('#recordUntilDate').val() : null; + let stopAtAction = useStopAt ? $('#recordUntilAction').val() : null; + let modelInput = val; + + // Step 1: Add the model using the standard approach + ctbrec.add(val, undefined, function() { + console.log("Model added successfully"); + $('#addModelByUrl').val(''); + + if (useStopAt) { + // Step 2: After a delay, set stopAt parameters + setTimeout(function() { + // Extract model name from the input + let modelName = modelInput; + if (modelName.includes(':')) { + modelName = modelName.split(':')[1]; + } else if (modelName.includes('/')) { + modelName = modelName.split('/').filter(Boolean).pop(); + } + + console.log("Looking for model: " + modelName); + console.log("Models in array:", observableModelsArray()); + + // Try to find the model in the observableModelsArray + let existingModel = null; + + // Try various ways to find the model + for (let i = 0; i < observableModelsArray().length; i++) { + let model = observableModelsArray()[i]; + + if (model.name === modelName) { + existingModel = model; + break; + } + + if (model.ko_name && typeof model.ko_name === 'function' && model.ko_name() === modelName) { + existingModel = model; + break; + } + + // Check if URL contains the model name + if (model.url && model.url.includes(modelName)) { + existingModel = model; + break; + } + } + + if (!existingModel) { + console.log("Could not find the model. Creating a simple model object instead."); + // If we can't find the model, create a simple object with just the necessary properties + existingModel = { name: modelName }; + } + + // Add stopAt parameters to the model + // Always use milliseconds for recordUntil + let dateTimestamp = new Date(stopAtDate).getTime(); + existingModel.recordUntil = dateTimestamp; + existingModel.recordUntilSubsequentAction = stopAtAction; + + console.log("Using milliseconds for recordUntil. Value: " + existingModel.recordUntil); + + console.log("Model with stopAt parameters:", existingModel); + + // Send the stopAt action + let stopAtMsg = '{"action": "stopAt", "model": ' + JSON.stringify(existingModel) + '}'; + + $.ajax({ + type: 'POST', + url: '../rec', + dataType: 'json', + async: true, + timeout: 60000, + headers: {'CTBREC-HMAC': CryptoJS.HmacSHA256(stopAtMsg, hmac)}, + data: stopAtMsg + }) + .done(function(data) { + console.log("StopAt response:", data); + if (data.status === 'success') { + // Use simple date formatting + let date = new Date(stopAtDate).toLocaleString(); + $.notify('Recording will stop at ' + date, 'info'); + + // Reset the datetime input and hide the options + $('#recordUntilDate').val(''); + $('#stopAtOptionsRow').hide(); + } else { + $.notify('Setting stopAt parameters failed', 'error'); + } + }) + .fail(function(jqXHR, textStatus, errorThrown) { + console.log(textStatus, errorThrown); + $.notify('Setting stopAt parameters failed', 'error'); + }); + }, 3000); // Wait 3 seconds to make sure the model is registered + } + }); } + 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) { try { diff --git a/server/src/main/resources/html/static/models.js b/server/src/main/resources/html/static/models.js index 4534b531..8244f0c0 100644 --- a/server/src/main/resources/html/static/models.js +++ b/server/src/main/resources/html/static/models.js @@ -43,21 +43,18 @@ function isModelInArray(array, model) { /** * 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 - for ( let idx in observableModelsArray()) { - let model = observableModelsArray()[idx]; - if (!isModelInArray(models, model)) { - observableModelsArray.remove(model); - } - } - - // add models to the observable array, which are new in the - // updated list - for ( let idx in models) { - let model = models[idx]; - if (!isModelInArray(observableModelsArray(), model)) { +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); @@ -71,6 +68,7 @@ function syncModels(models) { model.ko_recording = ko.observable(model.online && !model.suspended); //model.ko_recording_class = ko.observable( (model.online && !model.suspended) ? 'fa fa-circle red' : '' ); 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) { @@ -83,31 +81,30 @@ function syncModels(models) { } }); observableModelsArray.push(model); - } - } - - // update existing models - for ( let i in models) { - let model = models[i]; - for ( let j in observableModelsArray()) { - let m = observableModelsArray()[j]; - if (model.url === m.ko_url()) { - 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; - } + } 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.ko_recording_class( (model.online && !model.suspended) ? 'fa fa-circle red' : ''); - m.swallowEvents = true; - m.ko_suspended(model.suspended); - m.swallowEvents = false; - m.ko_recording(m.ko_online() && !m.ko_suspended()); + } + m.ko_online(onlineState); + //m.ko_recording_class( (model.online && !model.suspended) ? 'fa fa-circle red' : ''); + 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); } } }