v3.0.0: Docker-Migration mit CI-Pipeline und neuen Features
Build Release Docker Image / build-arm64 (release) Successful in 1m0s
Build Release Docker Image / build-amd64 (release) Successful in 2m50s
Build Release Docker Image / publish-release-manifest (release) Successful in 7s

- Gitea CI-Workflows fuer Multi-Arch Docker Builds (amd64 + arm64)
- Beschreibungsfeld und mehrtaegige Zeitplanung fuer Jobs
- Benachrichtigungen auf Deutsch
- docker-compose nutzt Registry-Image

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Asmus
2026-09-16 09:40:40 +02:00
co-authored by Claude Opus 4.6
parent 9fa589789e
commit b446ce9b3e
9 changed files with 298 additions and 28 deletions
+45 -9
View File
@@ -450,6 +450,10 @@
<label>Name *</label>
<input type="text" id="job-name" placeholder="Mein Stream">
</div>
<div class="form-group">
<label>Beschreibung (optional)</label>
<input type="text" id="job-description" placeholder="z.B. Freitagssendung, Live-Event...">
</div>
<div class="form-group">
<label>Typ</label>
<select id="job-type">
@@ -498,9 +502,13 @@
</div>
</div>
<div class="form-group" id="scheduleDateGroup" style="display:none">
<label>Datum</label>
<label>Startdatum</label>
<input type="date" id="job-schedule-date">
</div>
<div class="form-group" id="scheduleEndDateGroup" style="display:none">
<label>Enddatum (leer = gleicher Tag)</label>
<input type="date" id="job-schedule-end-date" oninput="updateDurationField()">
</div>
<div class="form-group" id="scheduleDaysGroup">
<label>Tage (z.B. Mo-Fr, Sa,So, *)</label>
<input type="text" id="job-schedule-days" value="*" placeholder="*">
@@ -721,6 +729,7 @@ async function loadJobs() {
<button class="btn btn-sm btn-danger" onclick="deleteJob(${j.id})">Löschen</button>
</div>
</div>
${j.description ? `<div class="meta" style="margin-bottom:4px"><span class="meta-item" style="color:var(--text)">${esc(j.description)}</span></div>` : ''}
<div class="meta">
<span class="meta-item">${esc(j.stream_type || 'auto')}</span>
<span class="meta-item">${esc(j.stream_url)}</span>
@@ -744,6 +753,7 @@ function openJobModal(job = null) {
document.getElementById('job-url').value = job ? job.stream_url : '';
document.getElementById('job-name').value = job ? job.name : '';
document.getElementById('job-type').value = job ? job.stream_type : 'auto';
document.getElementById('job-description').value = job ? job.description || '' : '';
document.getElementById('job-format').value = job ? job.output_format || '' : '';
document.getElementById('job-duration').value = job && job.max_duration ? Math.round(job.max_duration / 60) : '';
document.getElementById('job-segment').value = job && job.segment_duration ? Math.round(job.segment_duration / 60) : '';
@@ -751,6 +761,7 @@ function openJobModal(job = null) {
document.getElementById('job-schedule-enabled').checked = job ? job.schedule_enabled : false;
document.getElementById('job-schedule-once').checked = job ? job.schedule_once : false;
document.getElementById('job-schedule-date').value = job ? job.schedule_date || '' : '';
document.getElementById('job-schedule-end-date').value = job ? job.schedule_end_date || '' : '';
document.getElementById('job-schedule-days').value = job ? job.schedule_days || '*' : '*';
document.getElementById('job-schedule-start').value = job ? job.schedule_start || '' : '';
document.getElementById('job-schedule-stop').value = job ? job.schedule_stop || '' : '';
@@ -791,6 +802,8 @@ async function saveJob() {
schedule_days: document.getElementById('job-schedule-days').value || '*',
schedule_start: document.getElementById('job-schedule-start').value,
schedule_stop: document.getElementById('job-schedule-stop').value,
schedule_end_date: document.getElementById('job-schedule-end-date').value,
description: document.getElementById('job-description').value,
ntfy_enabled: document.getElementById('job-ntfy-enabled').checked,
plik_enabled: document.getElementById('job-plik-enabled').checked,
delete_after_upload: document.getElementById('job-delete-after').checked,
@@ -844,27 +857,50 @@ function updateDurationField() {
const scheduleOn = document.getElementById('job-schedule-enabled').checked;
const start = document.getElementById('job-schedule-start').value;
const stop = document.getElementById('job-schedule-stop').value;
const once = document.getElementById('job-schedule-once').checked;
const startDate = document.getElementById('job-schedule-date').value;
const endDate = document.getElementById('job-schedule-end-date').value;
const hasScheduleStop = scheduleOn && start && stop;
document.getElementById('durationGroup').style.display = hasScheduleStop ? 'none' : '';
document.getElementById('durationAutoGroup').style.display = hasScheduleStop ? '' : 'none';
if (hasScheduleStop) {
const [sh, sm] = start.split(':').map(Number);
const [eh, em] = stop.split(':').map(Number);
let diff = (eh * 60 + em) - (sh * 60 + sm);
if (diff <= 0) diff += 1440;
const h = Math.floor(diff / 60);
const m = diff % 60;
const text = h > 0 ? `${h} Std. ${m > 0 ? m + ' Min.' : ''}` : `${m} Min.`;
document.getElementById('durationAutoText').textContent = `${text} (automatisch aus Zeitplan)`;
let text;
if (once && startDate && endDate && endDate !== startDate) {
const startDt = new Date(`${startDate}T${start}`);
const endDt = new Date(`${endDate}T${stop}`);
let diffMin = Math.round((endDt - startDt) / 60000);
if (diffMin <= 0) { text = 'Enddatum muss nach Startdatum liegen'; }
else {
const d = Math.floor(diffMin / 1440);
const h = Math.floor((diffMin % 1440) / 60);
const m = diffMin % 60;
const parts = [];
if (d > 0) parts.push(`${d} Tag${d > 1 ? 'e' : ''}`);
if (h > 0) parts.push(`${h} Std.`);
if (m > 0) parts.push(`${m} Min.`);
text = `${parts.join(' ')} (mehrtägig, aus Zeitplan)`;
}
} else {
const [sh, sm] = start.split(':').map(Number);
const [eh, em] = stop.split(':').map(Number);
let diff = (eh * 60 + em) - (sh * 60 + sm);
if (diff <= 0) diff += 1440;
const h = Math.floor(diff / 60);
const m = diff % 60;
text = (h > 0 ? `${h} Std. ${m > 0 ? m + ' Min.' : ''}` : `${m} Min.`) + ' (automatisch aus Zeitplan)';
}
document.getElementById('durationAutoText').textContent = text;
}
}
function toggleScheduleOnce() {
const once = document.getElementById('job-schedule-once').checked;
document.getElementById('scheduleDateGroup').style.display = once ? 'block' : 'none';
document.getElementById('scheduleEndDateGroup').style.display = once ? 'block' : 'none';
document.getElementById('scheduleDaysGroup').style.display = once ? 'none' : 'block';
updateDurationField();
}
function togglePlik() {