otb-cloud secure encrypted backups
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

137 lines
3.9 KiB

{% extends "portal_base.html" %}
{% block title %}Video Workshop - OTB Cloud{% endblock %}
{% block portal_content %}
<style>
#profile {
background: #1e293b;
color: #e5e7eb;
border: 1px solid rgba(255,255,255,0.18);
}
#profile option {
background: #1e293b;
color: #e5e7eb;
}
</style>
<div class="portal-page-header">
<div>
<h1 class="portal-page-title">Video Workshop</h1>
<p class="portal-page-subtitle">Device ID: <strong>{{ device_id }}</strong></p>
</div>
<div class="portal-toolbar" style="display:flex;gap:10px;flex-wrap:wrap;">
<a class="portal-btn" href="/devices/{{ device_id }}/files">Back to Device Files</a>
<a class="portal-btn" href="/portal">Back to Portal</a>
</div>
</div>
<div class="service-card" style="margin-top:18px;">
<div class="service-card-header">
<div>
<h2>Queue Video Jobs</h2>
<p>Selected files from the device browser are staged in your browser and can now be queued for processing.</p>
</div>
<div>
<span class="service-badge service-badge-beta">alpha3-a</span>
</div>
</div>
<div class="service-card-body" style="display:flex;flex-direction:column;gap:16px;">
<div>
<label for="profile"><strong>Profile</strong></label><br>
<select id="profile" class="portal-input" style="max-width:320px;margin-top:8px;">
<option value="default">Default</option>
<option value="compress">Compress</option>
<option value="hq">High Quality</option>
</select>
</div>
<div>
<strong>Selected items</strong>
<pre id="selected-files" style="white-space:pre-wrap;background:rgba(255,255,255,0.04);padding:12px;border-radius:12px;overflow:auto;min-height:80px;"></pre>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<button class="portal-btn primary" type="button" onclick="processWorkshop()">Process</button>
<button class="portal-btn" type="button" onclick="loadJobs()">Refresh Jobs</button>
<button class="portal-btn" type="button" onclick="clearWorkshopSelection()">Clear Selection</button>
</div>
</div>
</div>
<div class="service-card" style="margin-top:18px;">
<div class="service-card-header">
<div>
<h2>Jobs</h2>
<p>Live queue/status feed for this tenant.</p>
</div>
</div>
<div class="service-card-body">
<pre id="jobs" style="white-space:pre-wrap;background:rgba(255,255,255,0.04);padding:12px;border-radius:12px;overflow:auto;min-height:140px;"></pre>
</div>
</div>
<script>
function getWorkshopSelection() {
try {
return JSON.parse(localStorage.getItem("videoSelection") || "[]");
} catch (e) {
return [];
}
}
function renderWorkshopSelection() {
const files = getWorkshopSelection();
document.getElementById("selected-files").textContent =
files.length ? JSON.stringify(files, null, 2) : "No files currently staged.";
}
function clearWorkshopSelection() {
localStorage.removeItem("videoSelection");
renderWorkshopSelection();
}
function processWorkshop() {
const files = getWorkshopSelection();
if (!files.length) {
alert("No files staged for workshop.");
return;
}
fetch("/api/video/enqueue", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
device_id: {{ device_id }},
files: files,
profile: document.getElementById("profile").value
})
})
.then(r => r.json())
.then(d => {
document.getElementById("jobs").textContent = JSON.stringify(d, null, 2);
loadJobs();
})
.catch(err => {
document.getElementById("jobs").textContent = "Enqueue failed: " + err;
});
}
function loadJobs() {
fetch("/api/video/jobs")
.then(r => r.json())
.then(d => {
document.getElementById("jobs").textContent = JSON.stringify(d, null, 2);
})
.catch(err => {
document.getElementById("jobs").textContent = "Job load failed: " + err;
});
}
renderWorkshopSelection();
loadJobs();
setInterval(loadJobs, 3000);
</script>
{% endblock %}