refactor(stream actions): move segment logic into StreamActionsService
This commit is contained in:
@@ -13,13 +13,10 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MediaService {
|
public class MediaService {
|
||||||
@@ -31,7 +28,6 @@ public class MediaService {
|
|||||||
private final ClipService clipService;
|
private final ClipService clipService;
|
||||||
|
|
||||||
private static final float CLIP_MAX_LENGTH = 180;
|
private static final float CLIP_MAX_LENGTH = 180;
|
||||||
private static final int HLS_SEGMENT_LENGTH = 3;
|
|
||||||
private final StreamActionsService streamActionsService;
|
private final StreamActionsService streamActionsService;
|
||||||
private final VodService vodService;
|
private final VodService vodService;
|
||||||
private final MarkerService markerService;
|
private final MarkerService markerService;
|
||||||
@@ -234,11 +230,11 @@ public class MediaService {
|
|||||||
|
|
||||||
private SegmentContext resolveSegments(User user, Instant startTime, Instant endTime) throws IOException {
|
private SegmentContext resolveSegments(User user, Instant startTime, Instant endTime) throws IOException {
|
||||||
Path streamDir = directoryService.getStreamDir(user.getStreamKey());
|
Path streamDir = directoryService.getStreamDir(user.getStreamKey());
|
||||||
List<Path> segments = getSegmentsInRange(streamDir, startTime, endTime);
|
List<Path> segments = streamActionsService.getSegmentsInRange(streamDir, startTime, endTime);
|
||||||
if (segments.isEmpty()) {
|
if (segments.isEmpty()) {
|
||||||
throw new IllegalArgumentException("No stream segments found in the given time range");
|
throw new IllegalArgumentException("No stream segments found in the given time range");
|
||||||
}
|
}
|
||||||
long firstMs = parseTimestampMs(segments.getFirst());
|
long firstMs = streamActionsService.parseTimestampMs(segments.getFirst());
|
||||||
float trimOffset = Math.max(0f, (startTime.toEpochMilli() - firstMs) / 1000f);
|
float trimOffset = Math.max(0f, (startTime.toEpochMilli() - firstMs) / 1000f);
|
||||||
return new SegmentContext(segments, trimOffset);
|
return new SegmentContext(segments, trimOffset);
|
||||||
}
|
}
|
||||||
@@ -260,26 +256,5 @@ public class MediaService {
|
|||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Path> getSegmentsInRange(Path streamDirectory, Instant startTime, Instant endTime) throws IOException {
|
|
||||||
long startMs = startTime.toEpochMilli();
|
|
||||||
long endMs = endTime.toEpochMilli();
|
|
||||||
|
|
||||||
try (Stream<Path> files = Files.list(streamDirectory)) {
|
|
||||||
return files
|
|
||||||
.filter(p -> p.getFileName().toString().endsWith(".ts"))
|
|
||||||
.filter(p -> {
|
|
||||||
long segMs = parseTimestampMs(p);
|
|
||||||
return segMs < endMs && (segMs + HLS_SEGMENT_LENGTH * 1000) > startMs;
|
|
||||||
})
|
|
||||||
.sorted(Comparator.comparingLong(this::parseTimestampMs))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long parseTimestampMs(Path path) {
|
|
||||||
String name = path.getFileName().toString().replace(".ts", "");
|
|
||||||
long value = Long.parseLong(name);
|
|
||||||
// nginx hls_fragment_naming system uses seconds; convert to ms
|
|
||||||
return value < 1_000_000_000_000L ? value * 1000L : value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service responsible for performing stream-related media actions, such as
|
* Service responsible for performing stream-related media actions, such as
|
||||||
@@ -25,6 +30,8 @@ public class StreamActionsService {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(StreamActionsService.class);
|
private static final Logger logger = LoggerFactory.getLogger(StreamActionsService.class);
|
||||||
private final CommandRunner commandRunner;
|
private final CommandRunner commandRunner;
|
||||||
|
|
||||||
|
private static final int HLS_SEGMENT_LENGTH = 3;
|
||||||
|
|
||||||
public StreamActionsService(CommandRunner commandRunner) {
|
public StreamActionsService(CommandRunner commandRunner) {
|
||||||
this.commandRunner = commandRunner;
|
this.commandRunner = commandRunner;
|
||||||
}
|
}
|
||||||
@@ -85,4 +92,27 @@ public class StreamActionsService {
|
|||||||
return CompletableFuture.failedFuture(e);
|
return CompletableFuture.failedFuture(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Path> getSegmentsInRange(Path streamDirectory, Instant startTime, Instant endTime) throws IOException {
|
||||||
|
long startMs = startTime.toEpochMilli();
|
||||||
|
long endMs = endTime.toEpochMilli();
|
||||||
|
|
||||||
|
try (Stream<Path> files = Files.list(streamDirectory)) {
|
||||||
|
return files
|
||||||
|
.filter(p -> p.getFileName().toString().endsWith(".ts"))
|
||||||
|
.filter(p -> {
|
||||||
|
long segMs = parseTimestampMs(p);
|
||||||
|
return segMs < endMs && (segMs + HLS_SEGMENT_LENGTH * 1000) > startMs;
|
||||||
|
})
|
||||||
|
.sorted(Comparator.comparingLong(this::parseTimestampMs))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long parseTimestampMs(Path path) {
|
||||||
|
String name = path.getFileName().toString().replace(".ts", "");
|
||||||
|
long value = Long.parseLong(name);
|
||||||
|
// nginx hls_fragment_naming system uses seconds; convert to ms
|
||||||
|
return value < 1_000_000_000_000L ? value * 1000L : value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user