Files
vod-system/src/main/java/com/ddf/vodsystem/controllers/ClipController.java
Dylan De Faoite ec9a982fe9
All checks were successful
CI / Maven unit tests (push) Successful in 18s
CI / Build and push images (push) Has been skipped
CI / Deploy to server (push) Has been skipped
docs(docstrings): add docstrings to public method interfaces (#25)
- Resolves #18

Reviewed-on: #25
2026-08-24 16:02:04 +01:00

161 lines
5.8 KiB
Java

package com.ddf.vodsystem.controllers;
import com.ddf.vodsystem.controllers.dto.ClipResponse;
import com.ddf.vodsystem.dto.APIResponse;
import com.ddf.vodsystem.controllers.dto.ClipUpdateRequest;
import com.ddf.vodsystem.entities.Clip;
import com.ddf.vodsystem.services.ClipService;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/v1/clips")
public class ClipController {
private final ClipService clipService;
private static final String SUCCESS = "success";
private static final String FILENAME_HEADER = "inline; filename=\"%s\"";
public ClipController(ClipService clipService) {
this.clipService = clipService;
}
/**
* Lists all clips belonging to the authenticated user.
*
* @return {@code 200 OK} wrapping the user's clips as {@link ClipResponse} DTOs
*/
@GetMapping("")
public ResponseEntity<APIResponse<List<ClipResponse>>> getClips() {
List<Clip> clips = clipService.getClipsByUser();
List<ClipResponse> clipDTOs = clips.stream()
.map(this::convertToDTO)
.toList();
return ResponseEntity.ok(
new APIResponse<>(SUCCESS,
"Clips retrieved successfully",
clipDTOs
)
);
}
/**
* Retrieves a single clip owned by the authenticated user.
*
* @param id the ID of the clip to retrieve
* @return {@code 200 OK} wrapping the clip as a {@link ClipResponse} DTO
*/
@GetMapping("/{id}")
public ResponseEntity<APIResponse<ClipResponse>> getClipById(@PathVariable Long id) {
Clip clip = clipService.getClipById(id);
ClipResponse clipDTO = convertToDTO(clip);
return ResponseEntity.ok(
new APIResponse<>(SUCCESS,
"Clip retrieved successfully",
clipDTO
)
);
}
/**
* Applies a partial update to a clip's metadata; null fields are left unchanged.
*
* @param id the ID of the clip to update
* @param updateFields the title and/or description to overwrite
* @return {@code 200 OK} wrapping the updated clip as a {@link ClipResponse} DTO
*/
@PatchMapping("/{id}")
public ResponseEntity<APIResponse<ClipResponse>> updateClip(@PathVariable Long id,
@RequestBody ClipUpdateRequest updateFields) {
Clip clip = clipService.updateClip(id, updateFields);
ClipResponse clipDTO = convertToDTO(clip);
return ResponseEntity.ok(
new APIResponse<>(SUCCESS, "Clip updated successfully", clipDTO)
);
}
/**
* Deletes a clip and its associated files.
*
* @param id the ID of the clip to delete
* @return {@code 200 OK} with a confirmation message
*/
@DeleteMapping("/{id}")
public ResponseEntity<APIResponse<String>> deleteClip(@PathVariable Long id) {
clipService.deleteClip(id);
return ResponseEntity.ok(
new APIResponse<>(
SUCCESS,
"Clip deleted successfully",
"Clip with ID " + id + " has been deleted"
)
);
}
/**
* Streams the clip's video file inline, supporting HTTP range requests.
*
* @param id the ID of the clip to download
* @return {@code 200 OK} with the video resource, or {@code 404 Not Found} if the file is missing
* @throws IOException if the resource's content length cannot be read
*/
@GetMapping(value = "/{id}/media", produces = MediaType.ALL_VALUE)
public ResponseEntity<Resource> downloadClip(@PathVariable Long id) throws IOException {
Resource resource = clipService.downloadClip(id);
if (resource == null || !resource.exists()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
String.format(FILENAME_HEADER, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource)
.orElse(MediaType.APPLICATION_OCTET_STREAM))
.contentLength(resource.contentLength())
.body(resource);
}
/**
* Streams the clip's thumbnail image with a public cache policy.
*
* @param id the ID of the clip whose thumbnail to download
* @return {@code 200 OK} with the thumbnail resource, or {@code 404 Not Found} if the file is missing
* @throws IOException if the resource's last-modified time cannot be read
*/
@GetMapping("/{id}/thumbnail")
public ResponseEntity<Resource> downloadThumbnail(@PathVariable Long id) throws IOException {
Resource resource = clipService.downloadThumbnail(id);
if (resource == null || !resource.exists()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(2, TimeUnit.DAYS).cachePublic())
.lastModified(resource.lastModified())
.header(HttpHeaders.CONTENT_DISPOSITION, String.format(FILENAME_HEADER, resource.getFilename()))
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(resource);
}
private ClipResponse convertToDTO(Clip clip) {
return new ClipResponse(
clip.getId(),
clip.getUser().getId(),
clip.getTitle(),
clip.getDescription(),
clip.getDuration(),
clip.getCreatedAt()
);
}
}