ADD MetadataService to track inital metadata
This commit is contained in:
@@ -15,15 +15,17 @@ public class Job {
|
|||||||
private File outputFile;
|
private File outputFile;
|
||||||
|
|
||||||
// configs
|
// configs
|
||||||
private VideoMetadata videoMetadata;
|
private VideoMetadata inputVideoMetadata;
|
||||||
|
private VideoMetadata outputVideoMetadata;
|
||||||
|
|
||||||
// job status
|
// job status
|
||||||
private JobStatus status = JobStatus.NOT_READY;
|
private JobStatus status = JobStatus.NOT_READY;
|
||||||
private Float progress = 0.0f;
|
private Float progress = 0.0f;
|
||||||
|
|
||||||
public Job(String uuid, File inputFile, File outputFile) {
|
public Job(String uuid, File inputFile, File outputFile, VideoMetadata inputVideoMetadata) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.inputFile = inputFile;
|
this.inputFile = inputFile;
|
||||||
this.outputFile = outputFile;
|
this.outputFile = outputFile;
|
||||||
|
this.inputVideoMetadata = inputVideoMetadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,12 +109,12 @@ public class CompressionService {
|
|||||||
public void run(Job job) throws IOException, InterruptedException {
|
public void run(Job job) throws IOException, InterruptedException {
|
||||||
logger.info("FFMPEG starting...");
|
logger.info("FFMPEG starting...");
|
||||||
|
|
||||||
ProcessBuilder pb = buildCommand(job.getInputFile(), job.getOutputFile(), job.getVideoMetadata());
|
ProcessBuilder pb = buildCommand(job.getInputFile(), job.getOutputFile(), job.getOutputVideoMetadata());
|
||||||
Process process = pb.start();
|
Process process = pb.start();
|
||||||
job.setStatus(JobStatus.RUNNING);
|
job.setStatus(JobStatus.RUNNING);
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
float length = job.getVideoMetadata().getEndPoint() - job.getVideoMetadata().getStartPoint();
|
float length = job.getOutputVideoMetadata().getEndPoint() - job.getOutputVideoMetadata().getStartPoint();
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class EditService {
|
|||||||
public void edit(String uuid, VideoMetadata videoMetadata) {
|
public void edit(String uuid, VideoMetadata videoMetadata) {
|
||||||
Job job = jobService.getJob(uuid);
|
Job job = jobService.getJob(uuid);
|
||||||
validateClipConfig(videoMetadata);
|
validateClipConfig(videoMetadata);
|
||||||
job.setVideoMetadata(videoMetadata);
|
job.setOutputVideoMetadata(videoMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(String uuid) {
|
public void process(String uuid) {
|
||||||
|
|||||||
123
src/main/java/com/ddf/vodsystem/services/MetadataService.java
Normal file
123
src/main/java/com/ddf/vodsystem/services/MetadataService.java
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package com.ddf.vodsystem.services;
|
||||||
|
|
||||||
|
import com.ddf.vodsystem.entities.VideoMetadata;
|
||||||
|
import com.ddf.vodsystem.exceptions.FFMPEGException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MetadataService {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(MetadataService.class);
|
||||||
|
|
||||||
|
public VideoMetadata getVideoMetadata(File file) {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder("ffprobe",
|
||||||
|
"-v", "quiet",
|
||||||
|
"-print_format", "json",
|
||||||
|
"-show_format", "-select_streams",
|
||||||
|
"v:0", "-show_entries", "stream=duration,width,height,r_frame_rate:format=size,duration",
|
||||||
|
"-i", file.getAbsolutePath());
|
||||||
|
|
||||||
|
Process process;
|
||||||
|
|
||||||
|
try {
|
||||||
|
process = pb.start();
|
||||||
|
handleFfprobeError(process);
|
||||||
|
return parseVideoMetadata(readStandardOutput(process));
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new FFMPEGException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode readStandardOutput(Process process) throws IOException{
|
||||||
|
// Read the standard output (JSON metadata)
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||||
|
StringBuilder jsonOutput = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
jsonOutput.append(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the JSON output
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
return mapper.readTree(jsonOutput.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFfprobeError(Process process) throws IOException, InterruptedException {
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||||
|
StringBuilder errorOutput = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = errorReader.readLine()) != null) {
|
||||||
|
errorOutput.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FFMPEGException("ffprobe exited with code " + exitCode + ". Error: " + errorOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private VideoMetadata parseVideoMetadata(JsonNode node) {
|
||||||
|
VideoMetadata metadata = new VideoMetadata();
|
||||||
|
metadata.setStartPoint(0f);
|
||||||
|
|
||||||
|
JsonNode streamNode = node.path("streams").get(0);
|
||||||
|
|
||||||
|
// if stream doesn't exist
|
||||||
|
if (streamNode == null || streamNode.isMissingNode()) {
|
||||||
|
throw new FFMPEGException("ffprobe streams missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamNode.has("duration")) {
|
||||||
|
metadata.setEndPoint(Float.valueOf(streamNode.get("duration").asText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamNode.has("width")) {
|
||||||
|
metadata.setWidth(streamNode.get("width").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamNode.has("height")) {
|
||||||
|
metadata.setHeight(streamNode.get("height").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamNode.has("r_frame_rate")) {
|
||||||
|
String fpsFraction = streamNode.get("r_frame_rate").asText();
|
||||||
|
|
||||||
|
if (fpsFraction.contains("/")) {
|
||||||
|
String[] parts = fpsFraction.split("/");
|
||||||
|
double numerator = Float.parseFloat(parts[0]);
|
||||||
|
double denominator = Float.parseFloat(parts[1]);
|
||||||
|
if (denominator != 0) {
|
||||||
|
metadata.setFps((float) (numerator / denominator));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadata.setFps(Float.valueOf(fpsFraction)); // Handle cases like "25" directly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract from the 'format' section
|
||||||
|
JsonNode formatNode = node.path("format");
|
||||||
|
if (formatNode != null && !formatNode.isMissingNode()) {
|
||||||
|
if (formatNode.has("size")) {
|
||||||
|
metadata.setFileSize(Float.parseFloat(formatNode.get("size").asText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use format duration as a fallback or primary source if stream duration is absent/zero
|
||||||
|
if (formatNode.has("duration") && metadata.getEndPoint() == null) {
|
||||||
|
metadata.setEndPoint(Float.parseFloat(formatNode.get("duration").asText()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.ddf.vodsystem.services;
|
package com.ddf.vodsystem.services;
|
||||||
|
|
||||||
import com.ddf.vodsystem.entities.Job;
|
import com.ddf.vodsystem.entities.Job;
|
||||||
|
import com.ddf.vodsystem.entities.VideoMetadata;
|
||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
import com.vaadin.hilla.Endpoint;
|
import com.vaadin.hilla.Endpoint;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
@@ -34,10 +35,12 @@ public class UploadService {
|
|||||||
private String outputDir;
|
private String outputDir;
|
||||||
|
|
||||||
private final JobService jobService;
|
private final JobService jobService;
|
||||||
|
private final MetadataService metadataService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UploadService(JobService jobService) {
|
public UploadService(JobService jobService, MetadataService metadataService) {
|
||||||
this.jobService = jobService;
|
this.jobService = jobService;
|
||||||
|
this.metadataService = metadataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String upload(MultipartFile file) {
|
public String upload(MultipartFile file) {
|
||||||
@@ -55,7 +58,8 @@ public class UploadService {
|
|||||||
moveToFile(file, inputFile);
|
moveToFile(file, inputFile);
|
||||||
|
|
||||||
// add job
|
// add job
|
||||||
Job job = new Job(uuid, inputFile, outputFile);
|
VideoMetadata videoMetadata = metadataService.getVideoMetadata(inputFile);
|
||||||
|
Job job = new Job(uuid, inputFile, outputFile, videoMetadata);
|
||||||
jobService.add(job);
|
jobService.add(job);
|
||||||
|
|
||||||
return uuid;
|
return uuid;
|
||||||
|
|||||||
Reference in New Issue
Block a user