ADD thumbnail to database (#12)
* ADD thumbnail to database * ADD thumbnail generation and retrieval functionality * ADD thumbnail availability check in VideoCard component * ADD ClipDTO to reduce exposed internals * REFactor move APIResponse and VideoMetadata to dto package * REMOVE unused props from VideoCard * ADD isThumbnailAvailable function
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.ddf.vodsystem.services;
|
||||
|
||||
import com.ddf.vodsystem.dto.VideoMetadata;
|
||||
import com.ddf.vodsystem.entities.*;
|
||||
|
||||
import java.io.File;
|
||||
@@ -83,8 +84,20 @@ public class ClipService {
|
||||
private void persistClip(VideoMetadata videoMetadata, User user, Job job) {
|
||||
// Move clip from temp to output directory
|
||||
String fileExtension = directoryService.getFileExtension(job.getOutputFile().getAbsolutePath());
|
||||
File outputFile = directoryService.getOutputFile(job.getUuid(), fileExtension);
|
||||
directoryService.copyFile(job.getOutputFile(), outputFile);
|
||||
|
||||
File clipOutputDir = directoryService.getUserClipsDir(user.getId());
|
||||
File clipOutputFile = new File(clipOutputDir, job.getUuid() + "." + fileExtension);
|
||||
directoryService.copyFile(job.getOutputFile(), clipOutputFile);
|
||||
|
||||
File thumbnailOutputDir = directoryService.getUserThumbnailsDir(user.getId());
|
||||
File thumbnailOutputFile = new File(thumbnailOutputDir, job.getUuid() + ".png");
|
||||
|
||||
try {
|
||||
ffmpegService.generateThumbnail(clipOutputFile, thumbnailOutputFile, 0.0f);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
logger.error("Error generating thumbnail for clip: {}", e.getMessage());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// Save clip to database
|
||||
Clip clip = new Clip();
|
||||
@@ -97,7 +110,8 @@ public class ClipService {
|
||||
clip.setFps(videoMetadata.getFps());
|
||||
clip.setDuration(videoMetadata.getEndPoint() - videoMetadata.getStartPoint());
|
||||
clip.setFileSize(videoMetadata.getFileSize());
|
||||
clip.setVideoPath(outputFile.getPath());
|
||||
clip.setVideoPath(clipOutputFile.getPath());
|
||||
clip.setThumbnailPath(thumbnailOutputFile.getPath());
|
||||
clipRepository.save(clip);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,32 @@ public class DirectoryService {
|
||||
return new File(dir);
|
||||
}
|
||||
|
||||
public File getUserClipsDir(Long userId) {
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("User ID cannot be null");
|
||||
}
|
||||
|
||||
String dir = outputDir + File.separator + userId + File.separator + "clips";
|
||||
return new File(dir);
|
||||
}
|
||||
|
||||
public File getUserThumbnailsDir(Long userId) {
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("User ID cannot be null");
|
||||
}
|
||||
|
||||
String dir = outputDir + File.separator + userId + File.separator + "thumbnails";
|
||||
File thumbnailDir = new File(dir);
|
||||
|
||||
try {
|
||||
createDirectory(thumbnailDir.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
logger.error("Error creating thumbnails directory: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return thumbnailDir;
|
||||
}
|
||||
|
||||
public void saveAtDir(File file, MultipartFile multipartFile) {
|
||||
try {
|
||||
createDirectory(file.getAbsolutePath());
|
||||
@@ -118,7 +144,6 @@ public class DirectoryService {
|
||||
Files.delete(f.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
||||
@@ -65,4 +65,19 @@ public class DownloadService {
|
||||
Clip clip = clipRepository.findById(id).orElseThrow(() -> new JobNotFound("Clip not found with id: " + id));
|
||||
return downloadClip(clip);
|
||||
}
|
||||
|
||||
public Resource downloadThumbnail(Clip clip) {
|
||||
String path = clip.getThumbnailPath();
|
||||
File file = new File(path);
|
||||
if (!file.exists()) {
|
||||
throw new JobNotFound("Thumbnail file not found");
|
||||
}
|
||||
|
||||
return new FileSystemResource(file);
|
||||
}
|
||||
|
||||
public Resource downloadThumbnail(Long id) {
|
||||
Clip clip = clipRepository.findById(id).orElseThrow(() -> new JobNotFound("Clip not found with id: " + id));
|
||||
return downloadThumbnail(clip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ddf.vodsystem.services;
|
||||
|
||||
import com.ddf.vodsystem.dto.VideoMetadata;
|
||||
import com.ddf.vodsystem.entities.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.ddf.vodsystem.services;
|
||||
|
||||
import com.ddf.vodsystem.entities.VideoMetadata;
|
||||
import com.ddf.vodsystem.dto.VideoMetadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -48,6 +48,35 @@ public class FfmpegService {
|
||||
runWithProgress(inputFile, outputFile, videoMetadata, new AtomicReference<>(0f));
|
||||
}
|
||||
|
||||
public void generateThumbnail(File inputFile, File outputFile, Float time) throws IOException, InterruptedException {
|
||||
logger.info("Generating thumbnail at {} seconds", time);
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("ffmpeg");
|
||||
command.add("-ss");
|
||||
command.add(time.toString());
|
||||
command.add("-i");
|
||||
command.add(inputFile.getAbsolutePath());
|
||||
command.add("-frames:v");
|
||||
command.add("1");
|
||||
command.add(outputFile.getAbsolutePath());
|
||||
|
||||
String strCommand = String.join(" ", command);
|
||||
logger.info("FFMPEG thumbnail command: {}", strCommand);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
Process process = processBuilder.start();
|
||||
|
||||
if (process.waitFor() != 0) {
|
||||
logger.error("FFMPEG process failed to generate thumbnail");
|
||||
throw new IOException("FFMPEG process failed to generate thumbnail");
|
||||
}
|
||||
|
||||
logger.info("Thumbnail generated successfully at {}", outputFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
private void updateJobProgress(Process process, AtomicReference<Float> progress, Float length) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.ddf.vodsystem.services;
|
||||
|
||||
import com.ddf.vodsystem.entities.VideoMetadata;
|
||||
import com.ddf.vodsystem.dto.VideoMetadata;
|
||||
import com.ddf.vodsystem.exceptions.FFMPEGException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.ddf.vodsystem.services;
|
||||
|
||||
import com.ddf.vodsystem.entities.Job;
|
||||
import com.ddf.vodsystem.entities.VideoMetadata;
|
||||
import com.ddf.vodsystem.dto.VideoMetadata;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
Reference in New Issue
Block a user