feat(stream): implement clipping functionality
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -81,6 +81,10 @@
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.ddf.vodsystem.controllers;
|
||||
|
||||
import com.ddf.vodsystem.controllers.input.ClipSectionRequest;
|
||||
import com.ddf.vodsystem.dto.APIResponse;
|
||||
import com.ddf.vodsystem.dto.SaveSectionRequest;
|
||||
import com.ddf.vodsystem.controllers.input.SaveSectionRequest;
|
||||
import com.ddf.vodsystem.entities.Stream;
|
||||
import com.ddf.vodsystem.services.StreamService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -53,6 +54,13 @@ public class StreamController {
|
||||
return ResponseEntity.accepted().body(new APIResponse<>("success", "Section saved", null));
|
||||
}
|
||||
|
||||
@PostMapping("/clip")
|
||||
public ResponseEntity<APIResponse<Void>> saveClip(
|
||||
@RequestBody ClipSectionRequest request) throws IOException, InterruptedException {
|
||||
streamService.clip(request.duration());
|
||||
return ResponseEntity.accepted().body(new APIResponse<>("success", "Clip saved", null));
|
||||
}
|
||||
|
||||
@GetMapping("/history/{userId}")
|
||||
public ResponseEntity<APIResponse<List<Stream>>> getStreamHistory(@PathVariable Long userId) {
|
||||
List<Stream> history = streamService.getStreamHistory(userId);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ddf.vodsystem.controllers.input;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record ClipSectionRequest(
|
||||
@NotNull float duration
|
||||
) {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ddf.vodsystem.controllers.input;
|
||||
|
||||
import java.time.Instant;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public record SaveSectionRequest(
|
||||
@NotNull Instant startTime,
|
||||
@NotNull Instant endTime) {}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.ddf.vodsystem.dto;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record SaveSectionRequest(Instant startTime, Instant endTime) {}
|
||||
@@ -17,6 +17,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
@@ -28,7 +29,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class StreamService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StreamService.class);
|
||||
|
||||
private static final int HEARTBEAT_TIMEOUT_SECONDS = 60;
|
||||
private static final float CLIP_MAX_LENGTH = 180;
|
||||
|
||||
private final StreamRepository streamRepository;
|
||||
private final UserService userService;
|
||||
@@ -118,6 +121,34 @@ public class StreamService {
|
||||
});
|
||||
}
|
||||
|
||||
public void clip(float duration) throws IOException, InterruptedException {
|
||||
if (duration <= 0 || duration > CLIP_MAX_LENGTH) {
|
||||
throw new IllegalArgumentException("Clip length must be between 0 and " + CLIP_MAX_LENGTH + " seconds");
|
||||
}
|
||||
|
||||
User user = userService.getLoggedInUser()
|
||||
.orElseThrow(() -> new NotAuthenticated("User is not authenticated"));
|
||||
|
||||
Instant endTime = Instant.now();
|
||||
// minusSeconds() is possible, but only does integer seconds, not float
|
||||
Instant startTime = Instant.now().minus(Duration.ofMillis((long) (duration * 1000)));
|
||||
|
||||
String streamDirectory = streamDataPath + File.separator + user.getStreamKey();
|
||||
List<File> fileSegments = getSegmentsInRange(streamDirectory, startTime, endTime);
|
||||
|
||||
if (fileSegments.isEmpty()) {
|
||||
throw new IllegalArgumentException("No stream segments found in the given time range");
|
||||
}
|
||||
|
||||
long firstSegmentMs = parseTimestampMs(fileSegments.getFirst());
|
||||
float trimOffset = Math.max(0f, (startTime.toEpochMilli() - firstSegmentMs) / 1000f);
|
||||
|
||||
streamActionsService.saveSection(fileSegments, trimOffset, duration)
|
||||
.exceptionally(ex -> {
|
||||
throw new FFMPEGException("Saving stream section failed due to FFMPEG Error");
|
||||
});
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 30_000)
|
||||
@Transactional
|
||||
public void endStaleStreams() {
|
||||
|
||||
Reference in New Issue
Block a user