fixed potential timezone issue

This commit is contained in:
dekzter 2025-04-06 19:14:32 -04:00
parent 8807b442db
commit b3551fe32f
5 changed files with 98 additions and 4 deletions

View file

@ -4,6 +4,8 @@ from apps.epg.serializers import EPGDataSerializer
from core.models import StreamProfile
from apps.epg.models import EPGData
from django.urls import reverse
from rest_framework import serializers
from django.utils import timezone
class LogoSerializer(serializers.ModelSerializer):
cache_url = serializers.SerializerMethodField()
@ -239,3 +241,20 @@ class RecordingSerializer(serializers.ModelSerializer):
model = Recording
fields = '__all__'
read_only_fields = ['task_id']
def validate(self, data):
start_time = data.get('start_time')
end_time = data.get('end_time')
now = timezone.now() # timezone-aware current time
if end_time < now:
raise serializers.ValidationError("End time must be in the future.")
if start_time < now:
# Optional: Adjust start_time if it's in the past but end_time is in the future
data['start_time'] = now # or: timezone.now() + timedelta(seconds=1)
if end_time <= data['start_time']:
raise serializers.ValidationError("End time must be after start time.")
return data

View file

@ -9,6 +9,8 @@ from apps.m3u.models import M3UAccount
from apps.epg.tasks import parse_programs_for_tvg_id
import logging, requests, time
from .tasks import run_recording
from django.utils.timezone import now, is_aware, make_aware
from datetime import timedelta
logger = logging.getLogger(__name__)
@ -96,10 +98,32 @@ def revoke_old_task_on_update(sender, instance, **kwargs):
@receiver(post_save, sender=Recording)
def schedule_task_on_save(sender, instance, created, **kwargs):
if not instance.task_id and instance.start_time > now():
task_id = schedule_recording_task(instance)
instance.task_id = task_id
instance.save(update_fields=['task_id'])
try:
if not instance.task_id:
start_time = instance.start_time
# Make both datetimes aware (in UTC)
if not is_aware(start_time):
print("Start time was not aware, making aware")
start_time = make_aware(start_time)
current_time = now()
# Debug log
print(f"Start time: {start_time}, Now: {current_time}")
# Optionally allow slight fudge factor (1 second) to ensure scheduling happens
if start_time > current_time - timedelta(seconds=1):
print("Scheduling recording task!")
task_id = schedule_recording_task(instance)
instance.task_id = task_id
instance.save(update_fields=['task_id'])
else:
print("Start time is in the past. Not scheduling.")
except Exception as e:
import traceback
print("Error in post_save signal:", e)
traceback.print_exc()
@receiver(post_delete, sender=Recording)
def revoke_task_on_delete(sender, instance, **kwargs):

View file

@ -17,6 +17,9 @@ from apps.channels.models import Channel
from apps.epg.models import EPGData, EPGSource
from core.models import CoreSettings
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from core.utils import SentenceTransformer
@ -238,6 +241,16 @@ def run_recording(channel_id, start_time_str, end_time_str):
duration_seconds = int((end_time - start_time).total_seconds())
filename = f'{slugify(channel.name)}-{start_time.strftime("%Y-%m-%d_%H-%M-%S")}.mp4'
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
"updates",
{
"type": "update",
"data": {"success": True, "type": "recording_started", "channel": channel.name}
},
)
logger.info(f"Starting recording for channel {channel.name}")
with requests.get(f"http://localhost:5656/proxy/ts/stream/{channel.uuid}", headers={
'User-Agent': 'Dispatcharr-DVR',
@ -255,5 +268,13 @@ def run_recording(channel_id, start_time_str, end_time_str):
# Write the chunk to the file
file.write(chunk)
async_to_sync(channel_layer.group_send)(
"updates",
{
"type": "update",
"data": {"success": True, "type": "recording_ended", "channel": channel.name}
},
)
# After the loop, the file and response are closed automatically.
logger.info(f"Finished recording for channel {channel.name}")

View file

@ -127,6 +127,20 @@ export const WebsocketProvider = ({ children }) => {
setProfilePreview(event.data.search_preview, event.data.result);
break;
case 'recording_started':
notifications.show({
title: 'Recording started!',
message: `Started recording channel ${event.data.channel}`,
});
break;
case 'recording_ended':
notifications.show({
title: 'Recording finished!',
message: `Stopped recording channel ${event.data.channel}`,
});
break;
default:
console.error(`Unknown websocket event type: ${event.type}`);
break;

View file

@ -158,6 +158,15 @@ export default function TVChannelGuide({ startDate, endDate }) {
return guideChannels.find((ch) => ch.epg_data?.tvg_id === tvgId);
}
const record = (program) => {
const channel = findChannelByTvgId(program.tvg_id);
API.createRecording({
channel: `${channel.id}`,
start_time: program.start_time,
end_time: program.end_time,
});
};
// The Watch Now click => show floating video
const { showVideo } = useVideoStore(); // or useVideoStore()
function handleWatchStream(program) {
@ -457,6 +466,13 @@ export default function TVChannelGuide({ startDate, endDate }) {
{now.isAfter(dayjs(selectedProgram.start_time)) &&
now.isBefore(dayjs(selectedProgram.end_time)) && (
<Flex mih={50} gap="xs" justify="flex-end" align="flex-end">
<Button
variant="transparent"
color="gray"
onClick={() => record(selectedProgram)}
>
Record
</Button>
<Button
variant="transparent"
color="gray"