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
+35 -11
View File
@@ -85,12 +85,13 @@ class StreamScheduler:
finally:
session.close()
def _get_active_recording_for_job(self, job_id: int):
def _get_active_scheduled_recording_for_job(self, job_id: int):
session = get_session()
try:
rec = session.query(Recording).filter(
Recording.job_id == job_id,
Recording.status.in_(["running", "starting"]),
Recording.is_scheduled == True,
).first()
return rec.id if rec else None
finally:
@@ -114,7 +115,8 @@ class StreamScheduler:
"segment_duration": j.segment_duration,
"schedule_once": j.schedule_once, "schedule_date": j.schedule_date,
"schedule_days": j.schedule_days, "schedule_start": j.schedule_start,
"schedule_stop": j.schedule_stop,
"schedule_stop": j.schedule_stop, "schedule_end_date": j.schedule_end_date,
"description": j.description,
"metadata_monitor_enabled": j.metadata_monitor_enabled,
"metadata_pattern": j.metadata_pattern,
"metadata_grace_period": j.metadata_grace_period,
@@ -131,6 +133,8 @@ class StreamScheduler:
continue
is_today = False
multi_day = bool(job.get("schedule_end_date") and job["schedule_once"])
if job["schedule_once"]:
if job["schedule_date"] == today_date:
is_today = True
@@ -139,9 +143,21 @@ class StreamScheduler:
is_today = True
in_window = False
is_cross_midnight = False
if is_today:
if job["schedule_stop"]:
in_window = _in_time_window(now_time, job["schedule_start"], job["schedule_stop"])
if multi_day:
now_min = _time_to_minutes(now_time)
start_min = _time_to_minutes(job["schedule_start"])
in_window = now_min >= start_min
elif job["schedule_stop"]:
start_min = _time_to_minutes(job["schedule_start"])
stop_min = _time_to_minutes(job["schedule_stop"])
if stop_min <= start_min:
is_cross_midnight = True
now_min = _time_to_minutes(now_time)
in_window = now_min >= start_min
else:
in_window = _in_time_window(now_time, job["schedule_start"], job["schedule_stop"])
else:
now_min = _time_to_minutes(now_time)
start_min = _time_to_minutes(job["schedule_start"])
@@ -153,12 +169,19 @@ class StreamScheduler:
if occ_key not in self._started_keys and not self._is_job_recording(job["id"]):
duration = job["max_duration"]
if not duration and job["schedule_stop"]:
start_min = _time_to_minutes(job["schedule_start"])
stop_min = _time_to_minutes(job["schedule_stop"])
if stop_min > start_min:
duration = (stop_min - start_min) * 60
if multi_day:
start_dt = datetime.datetime.strptime(
f"{job['schedule_date']} {job['schedule_start']}", "%Y-%m-%d %H:%M")
end_dt = datetime.datetime.strptime(
f"{job['schedule_end_date']} {job['schedule_stop']}", "%Y-%m-%d %H:%M")
duration = max(0, int((end_dt - start_dt).total_seconds()))
else:
duration = (1440 - start_min + stop_min) * 60
start_min = _time_to_minutes(job["schedule_start"])
stop_min = _time_to_minutes(job["schedule_stop"])
if stop_min > start_min:
duration = (stop_min - start_min) * 60
else:
duration = (1440 - start_min + stop_min) * 60
self.recorder.start_recording(
job_id=job["id"],
@@ -170,6 +193,7 @@ class StreamScheduler:
extra_ffmpeg_args=job["extra_ffmpeg_args"] or "",
segment_duration=job["segment_duration"],
is_scheduled=True,
description=job.get("description", ""),
metadata_monitor_enabled=job.get("metadata_monitor_enabled", False),
metadata_pattern=job.get("metadata_pattern", ""),
metadata_grace_period=job.get("metadata_grace_period") or 300,
@@ -180,8 +204,8 @@ class StreamScheduler:
)
self._started_keys.add(occ_key)
else:
if job["schedule_stop"] and self._is_job_recording(job["id"]):
rec_id = self._get_active_recording_for_job(job["id"])
if not multi_day and not is_cross_midnight and job["schedule_stop"] and self._is_job_recording(job["id"]):
rec_id = self._get_active_scheduled_recording_for_job(job["id"])
if rec_id:
self.recorder.stop_recording(rec_id)
self._started_keys.discard(occ_key)