refactor(media): consolidate upload/edit/download into MediaService

Merge UploadService, EditService, JobOrchestrationService, and
DownloadService into a single MediaService with a unified Job lifecycle
(READY → PROCESSING → SUCCEEDED/FAILED via JobState enum).

- Add MediaController (/api/v1/media) exposing compress, save, and clip
- Add JobController (/api/v1/job) for status polling and file download
- Move clip/thumbnail download into ClipController (/{id}/media, /{id}/thumbnail)
- Move stream saveSection/clip from StreamService into MediaService
- Simplify Job DTO: remove dual ClipOptions/files, add ProgressTracker and download file
- Refactor JobRegistryService.add() → generateJob() to co-locate creation and registration
- Wire FFmpeg out_time_ms progress parsing into StreamActionsService via CommandRunner
- Rename GET /api/v1/auth/user → GET /api/v1/user/me
- Delete DownloadController, MetadataController, UploadController
This commit is contained in:
2026-06-22 20:54:16 +01:00
parent b732c76aef
commit da6bf16d07
25 changed files with 511 additions and 765 deletions

View File

@@ -12,15 +12,14 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import com.ddf.vodsystem.exceptions.ClipNotFound;
import com.ddf.vodsystem.exceptions.FFMPEGException;
import com.ddf.vodsystem.exceptions.NotAuthenticated;
import com.ddf.vodsystem.exceptions.StorageException;
import com.ddf.vodsystem.exceptions.*;
import com.ddf.vodsystem.repositories.ClipRepository;
import com.ddf.vodsystem.services.media.MetadataService;
import com.ddf.vodsystem.services.media.ThumbnailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
@Service
@@ -146,6 +145,51 @@ public class ClipService {
return user.get().getId().equals(clip.getUser().getId());
}
public Resource downloadClip(Long id) {
Optional<Clip> possibleClip = getClipById(id);
if (possibleClip.isEmpty()) {
throw new ClipNotFound("Clip " + id + " doesn't exist");
}
Clip clip = possibleClip.get();
if (!isAuthenticatedForClip(clip)) {
throw new NotAuthenticated("Not authenticated for this clip");
}
String path = clip.getVideoPath();
File file = new File(path);
if (!file.exists()) {
throw new JobNotFound("Clip file not found");
}
return new FileSystemResource(file);
}
public Resource downloadThumbnail(Long id) {
Optional<Clip> possibleClip = getClipById(id);
if (possibleClip.isEmpty()) {
throw new ClipNotFound("Clip " + id + " doesn't exist");
}
Clip clip = possibleClip.get();
if (!isAuthenticatedForClip(clip)) {
throw new NotAuthenticated("Not authenticated for this clip thumbnail");
}
String path = clip.getThumbnailPath();
File file = new File(path);
if (!file.exists()) {
throw new JobNotFound("Thumbnail file not found");
}
return new FileSystemResource(file);
}
/**
* Persists a clip to the database
* @param options ClipOptions object of the clip metadata to save to the database. All fields required except for title, description