Files
thamanyah/cms/internal/views/video.templ
T
2026-08-16 18:57:40 +03:00

157 lines
4.5 KiB
Plaintext

package views
import "thamanyah/cms/v2/internal/views/layouts"
templ VideoUpload() {
@layouts.Layout("Upload Video · Thamanyah CMS") {
<h1>Upload a video</h1>
<p>Provide the video file along with its metadata.</p>
<form id="upload-form">
<div class="field">
<label for="title">Title</label>
<input type="text" id="title" name="title" required/>
</div>
<div class="field">
<label for="description">Description</label>
<textarea id="description" name="description" rows="4"></textarea>
</div>
<div class="field">
<label for="category">Category</label>
<select id="category" name="category">
<option value="documentary">Documentary</option>
<option value="news">News</option>
<option value="entertainment">Entertainment</option>
<option value="podcast">Podcast</option>
<option value="other">Other</option>
</select>
</div>
<div class="field">
<label for="tags">Tags</label>
<input type="text" id="tags" name="tags" placeholder="comma, separated, tags"/>
</div>
<div class="field">
<label for="video">Video file</label>
<input type="file" id="video" name="video" accept="video/*" required/>
</div>
<progress id="upload-progress" value="0" max="100"></progress>
<button type="submit">Upload</button>
</form>
<div id="upload-result"></div>
<script>
document.getElementById('upload-form').addEventListener('submit', function (evt) {
evt.preventDefault();
var form = evt.target;
var fileInput = document.getElementById('video');
var file = fileInput.files[0];
var resultEl = document.getElementById('upload-result');
var progressEl = document.getElementById('upload-progress');
var button = form.querySelector('button[type="submit"]');
function showError(message) {
resultEl.innerHTML = '<div class="result result-error"><p>' + message + '</p></div>';
}
if (!file) {
showError('A video file is required.');
return;
}
var contentType = file.type || 'application/octet-stream';
button.disabled = true;
progressEl.value = 0;
resultEl.innerHTML = '';
fetch('/videos/presign', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fileName: file.name, contentType: contentType })
}).then(function (res) {
return res.json().then(function (body) {
if (!res.ok) {
throw new Error(body.error || 'Could not prepare upload.');
}
return body;
});
}).then(function (presigned) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('PUT', presigned.uploadUrl);
xhr.setRequestHeader('Content-Type', contentType);
xhr.upload.addEventListener('progress', function (e) {
if (e.lengthComputable) {
progressEl.value = Math.round((e.loaded / e.total) * 100);
}
});
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(presigned.key);
} else {
reject(new Error('Upload to storage failed.'));
}
};
xhr.onerror = function () {
reject(new Error('Upload to storage failed.'));
};
xhr.send(file);
});
}).then(function (key) {
return fetch('/videos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: form.title.value.trim(),
description: form.description.value.trim(),
category: form.category.value,
tags: form.tags.value.trim(),
fileName: file.name,
key: key
})
});
}).then(function (res) {
return res.text().then(function (html) {
resultEl.innerHTML = html;
});
}).catch(function (err) {
showError(err.message);
}).finally(function () {
button.disabled = false;
});
});
</script>
}
}
templ VideoUploadSuccess(v VideoMetadata) {
<div class="result result-success">
<h2>Upload complete</h2>
<dl>
<dt>Title</dt>
<dd>{ v.Title }</dd>
if v.Description != "" {
<dt>Description</dt>
<dd>{ v.Description }</dd>
}
<dt>Category</dt>
<dd>{ v.Category }</dd>
<dt>Enqueue Job ID</dt>
<dd>{ v.JobID }</dd>
if v.Tags != "" {
<dt>Tags</dt>
<dd>{ v.Tags }</dd>
}
<dt>File</dt>
<dd>
{ v.FileName } ({ formatSize(v.SizeBytes) })
</dd>
</dl>
</div>
}
templ VideoUploadError(message string) {
<div class="result result-error">
<p style="color:green">{ message }</p>
</div>
}