refactor(media)!: unify processing under asynchronous job workflow #3

Merged
dylan merged 14 commits from refactor/generalize-job-abstraction into master 2026-06-25 20:26:09 +01:00
13 changed files with 31 additions and 27 deletions
Showing only changes of commit 42294703ae - Show all commits

View File

@@ -1,6 +1,6 @@
package com.ddf.vodsystem.controllers.dto;
import java.time.LocalDateTime;
import java.time.Instant;
public record ClipResponse(
Long id,
@@ -8,5 +8,5 @@ public record ClipResponse(
String title,
String description,
Float duration,
LocalDateTime createdAt
Instant createdAt
) {}

View File

@@ -5,7 +5,7 @@ import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.Instant;
@Entity
@Table(name = "clips")
@@ -28,7 +28,7 @@ public class Clip {
private String description;
@Column(name = "created_at")
private LocalDateTime createdAt;
private Instant createdAt;
@Column(name = "width", nullable = false)
private Integer width;

View File

@@ -4,7 +4,7 @@ import jakarta.persistence.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.time.LocalDateTime;
import java.time.Instant;
@Entity
@Table(name = "streams")
@@ -21,11 +21,11 @@ public class Stream {
private User user;
@Column(name = "start_time")
private LocalDateTime startDate;
private Instant startDate;
@Column(name = "end_time")
private LocalDateTime endDate;
private Instant endDate;
@Column(name = "last_seen")
private LocalDateTime lastSeen;
private Instant lastSeen;
}

View File

@@ -3,7 +3,7 @@ package com.ddf.vodsystem.entities;
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.Instant;
@Entity
@Table(name = "users")
@@ -33,7 +33,7 @@ public class User {
private Integer role; // 0: user, 1: admin
@Column(name = "created_at")
private LocalDateTime createdAt;
private Instant createdAt;
@Column(name = "stream_key")
private String streamKey;

View File

@@ -5,7 +5,7 @@ import com.ddf.vodsystem.entities.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@@ -13,5 +13,5 @@ import java.util.Optional;
public interface StreamRepository extends JpaRepository<Stream, Long> {
Optional<Stream> findByUserAndEndDateIsNull(User user);
List<Stream> findByUser(User user);
List<Stream> findByEndDateIsNullAndLastSeenBefore(LocalDateTime cutoff);
List<Stream> findByEndDateIsNullAndLastSeenBefore(Instant cutoff);
}

View File

@@ -8,7 +8,7 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@@ -208,7 +208,7 @@ public class ClipService {
clip.setUser(user);
clip.setTitle(options.getTitle() != null ? options.getTitle() : "Untitled Clip");
clip.setDescription(options.getDescription() != null ? options.getDescription() : "");
clip.setCreatedAt(LocalDateTime.now());
clip.setCreatedAt(Instant.now());
clip.setWidth(options.getWidth());
clip.setHeight(options.getHeight());
clip.setFps(options.getFps());

View File

@@ -12,7 +12,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.Instant;
import java.util.*;
@Service
@@ -37,7 +37,7 @@ public class StreamService {
throw new AlreadyStreaming("User " + user.getUsername() + " is already streaming.");
});
LocalDateTime now = LocalDateTime.now();
Instant now = Instant.now();
Stream stream = new Stream();
stream.setUser(user);
stream.setStartDate(now);
@@ -52,7 +52,7 @@ public class StreamService {
.orElseThrow(() -> new IllegalStateException(
"No active stream found for user " + user.getUsername()));
stream.setEndDate(LocalDateTime.now());
stream.setEndDate(Instant.now());
streamRepository.saveAndFlush(stream);
}
@@ -63,7 +63,7 @@ public class StreamService {
.orElseThrow(() -> new IllegalStateException(
"No active stream found for user " + user.getUsername()));
stream.setLastSeen(LocalDateTime.now());
stream.setLastSeen(Instant.now());
streamRepository.saveAndFlush(stream);
}
@@ -97,11 +97,11 @@ public class StreamService {
@Scheduled(fixedDelay = 30_000)
@Transactional
public void endStaleStreams() {
LocalDateTime cutoff = LocalDateTime.now().minusSeconds(HEARTBEAT_TIMEOUT_SECONDS);
Instant cutoff = Instant.now().minusSeconds(HEARTBEAT_TIMEOUT_SECONDS);
List<Stream> stale = streamRepository.findByEndDateIsNullAndLastSeenBefore(cutoff);
if (stale.isEmpty()) return;
LocalDateTime now = LocalDateTime.now();
Instant now = Instant.now();
for (Stream stream : stale) {
stream.setEndDate(now);
logger.warn("Stream {} for user {} ended due to heartbeat timeout", stream.getId(), stream.getUser().getUsername());

View File

@@ -17,7 +17,7 @@ import org.springframework.stereotype.Service;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.Instant;
import java.util.Collections;
import java.util.HexFormat;
import java.util.Optional;
@@ -77,7 +77,7 @@ public class UserService {
if (existingUser.isEmpty()) {
user.setRole(0);
user.setCreatedAt(LocalDateTime.now());
user.setCreatedAt(Instant.now());
user.setStreamKey(generateStreamKey());
return userRepository.saveAndFlush(user);
}

View File

@@ -38,7 +38,7 @@ public class CompressionService {
outputFile,
clipOptions
);
CommandOutput result = CommandRunner.run(command, line -> setProgress(line, progress, clipOptions.getDuration()));
CommandOutput result = CommandRunner.run(command, line -> CommandRunner.setProgress(line, progress, clipOptions.getDuration()));
progress.markComplete();
return CompletableFuture.completedFuture(result);

View File

@@ -72,6 +72,7 @@ public class StreamActionsService {
logger.info("Saving section with ({} segments) to '{}'",
segments.size(), outputFile);
CommandRunner.run(command, line -> CommandRunner.setProgress(line, progressTracker, duration));
progressTracker.markComplete();
return CompletableFuture.completedFuture(outputFile);
} catch (Exception e) {

View File

@@ -1,6 +1,6 @@
spring.docker.compose.file=docker-compose.dev.yml
media.temp.inputs=./video/inputs
media.temp.inputs=./videos/inputs
media.temp.outputs=./videos/outputs
media.users=${MEDIA_DATA}
media.streams=${STREAM_DATA}

View File

@@ -31,4 +31,7 @@ media.users=/app/media/
media.streams=/app/stream/
## Server Configuration
server.servlet.session.timeout=30m
server.servlet.session.timeout=30m
# Serialize Instant as ISO-8601 UTC string (not epoch seconds)
spring.jackson.serialization.write-dates-as-timestamps=false

View File

@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS users (
name VARCHAR(100) NOT NULL,
profile_picture_url VARCHAR(255),
role INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMPTZ DEFAULT NOW(),
stream_key VARCHAR(64) NOT NULL UNIQUE
);
@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS clips (
user_id BIGINT NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMPTZ DEFAULT NOW(),
width INTEGER NOT NULL,
height INTEGER NOT NULL,
fps FLOAT NOT NULL,