I’m building a Spring Boot app similar to YouTube, I can successfully upload videos but I can’t write comments or like and dislike reactions, and for some reason the thumbnail I uploaded with the video is not showing up
so this is my home controller
@Controller
public class HomeController {
@Autowired
private VideoRepository videoRepository;
@Autowired
private VideoService videoService;
@GetMapping("/springtube")
public String home(Model model) {
model.addAttribute("videos", videoRepository.findAll());
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
boolean isAuthenticated = auth.isAuthenticated();
model.addAttribute("isAuthenticated", isAuthenticated);
return "home";
}
@GetMapping("/video_detail")
public String videoDetailPage() {
return "video_detail"; // Return the video_detail.html template
}
@GetMapping("/files/{file-name:.+}")
public void getFile(@PathVariable("file-name") String fileName, HttpServletResponse response){
Video video = videoService.findByStorageName(fileName);
if (video == null) {
// Handle file not found
return;
}
if (fileName.endsWith("_thumbnail")) {
videoService.writeThumbnailToResponse(video.getThumbnailUrl(), response);
} else {
videoService.writeFileToResponse(fileName, response);
}
}
}
Video model
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Data
public class Video {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String originalName;
private String storageFileName;
private String url;
private Long size;
private String type;
private String thumbnailUrl;
@ManyToOne
@JoinColumn(name = "channel_id")
private Channel channel;
@OneToMany(mappedBy = "video")
private Set<Comment> comments = new HashSet<>();
}
@Controller
public class VideoController {
@Autowired
private UserRepository userRepository;
@Autowired
private VideoService videoService;
@GetMapping("/channel")
public String showUploadForm(Model model, Principal principal) {
// Fetch the authenticated user's channel
String email = principal.getName();
Optional<User> optionalUser = userRepository.findByEmail(email);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
List<Video> userVideos = videoService.getUploadedVideos(user.getId());
model.addAttribute("videos", userVideos);// Add a new Video object to the model);
model.addAttribute("channelName", user.getChannel().getName()); // Add channel name
return "upload"; // Returns the upload form HTML page
}
return "redirect:/error";
}
@PostMapping("/channel")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
@RequestParam("thumbnail") MultipartFile thumbnail,
Principal principal) {
// Validate the uploaded files
if (file.isEmpty() || thumbnail.isEmpty()) {
return String.valueOf(ResponseEntity.badRequest().body("Please select both video and thumbnail files."));
}
videoService.saveFile(file, thumbnail, principal);
return "redirect:/channel";
}
}
@Repository
public interface VideoRepository extends JpaRepository<Video, Long> {
Video findByStorageFileName (String fileName);
}
public interface VideoService {
Video findById(Long id);
String saveFile(MultipartFile uploadFile, MultipartFile thumbnailFile, Principal principal);
void writeFileToResponse(String fileName, HttpServletResponse response);
Video findByStorageName(String storageName);
List<Video> getUploadedVideos(Long userId);
void writeThumbnailToResponse(String fileName, HttpServletResponse response);
}
@Component
public class VideoServiceImpl implements VideoService {
@Autowired
private VideoRepository videoRepository;
@Autowired
private UserRepository userRepository;
@Value("${storage.path}")
private String storagePath;
@Override
public Video findById(Long id) {
return videoRepository.findById(id).get();
}
@Override
public String saveFile(MultipartFile uploadFile, MultipartFile thumbnailFile, Principal principal) {
String email = principal.getName();
Optional<User> optionalUser = userRepository.findByEmail(email);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
Channel channel = user.getChannel();
if (channel != null) {
String videoExtension = FilenameUtils.getExtension(uploadFile.getOriginalFilename());
String thumbnailExtension = FilenameUtils.getExtension(thumbnailFile.getOriginalFilename());
String videoStorageName = UUID.randomUUID().toString() + "." + videoExtension;
String thumbnailStorageName = UUID.randomUUID().toString() + "_thumbnail." + thumbnailExtension;
try {
String videoFullPath = Paths.get("src/main/resources", storagePath, videoStorageName).toString();
String thumbnailFullPath = Paths.get("src/main/resources", storagePath, thumbnailStorageName).toString();
Files.createDirectories(Paths.get(videoFullPath).getParent()); // Create directories if they don't exist
Files.createDirectories(Paths.get(thumbnailFullPath).getParent()); // Create directories if they don't exist
Files.copy(uploadFile.getInputStream(), Paths.get(videoFullPath));
Files.copy(thumbnailFile.getInputStream(), Paths.get(thumbnailFullPath));
} catch (IOException e) {
throw new IllegalStateException("Failed to save files", e);
}
Video video = Video.builder()
.type(uploadFile.getContentType())
.originalName(uploadFile.getOriginalFilename())
.size(uploadFile.getSize())
.storageFileName(videoStorageName)
.url(storagePath + "/" + videoStorageName)
.thumbnailUrl(storagePath + "/" + thumbnailStorageName) // Set the thumbnail URL
.channel(channel)
.build();
videoRepository.save(video);
return video.getStorageFileName();
}
}
throw new IllegalStateException("Failed to save files: User's channel not found");
}
@Override
public void writeFileToResponse(String fileName, HttpServletResponse response) {
Video fileInfo = videoRepository.findByStorageFileName(fileName);
if (fileInfo == null) {
throw new IllegalArgumentException("File not found with name: " + fileName);
}
response.setContentType(fileInfo.getType());
try {
IOUtils.copy(new FileInputStream("src/main/resources"+"/"+fileInfo.getUrl()), response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public Video findByStorageName(String storageName) {
return videoRepository.findByStorageFileName(storageName);
}
@Override
public List<Video> getUploadedVideos(Long userId) {
return null;
}
@Override
public void writeThumbnailToResponse(String fileName, HttpServletResponse response) {
Video thumbnailVideo = videoRepository.findByStorageFileName(fileName);
if (thumbnailVideo == null) {
throw new IllegalArgumentException("Thumbnail file not found with name: " + fileName);
}
response.setContentType("image/png"); // Assuming thumbnails are JPEG images
try {
// Read the thumbnail image from file and write it to the response output stream
IOUtils.copy(new FileInputStream("src/main/resources/" + thumbnailVideo.getThumbnailUrl()), response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
throw new IllegalArgumentException("Error writing thumbnail to response", e);
}
}
}
home.js
document.addEventListener('DOMContentLoaded', function() {
// Get all video items
const videoItems = document.querySelectorAll('.video-item');
// Add click event listener to each video item
videoItems.forEach(videoItem => {
videoItem.addEventListener('click', function() {
// Extract video ID or storage file name from data attribute
const videoId = this.getAttribute('data-video-id');
// Construct URL for video detail page with parameters
const videoDetailURL = `/video_detail.ftlh?videoId=${videoId}`;
// Redirect user to video detail page
window.location.href = videoDetailURL;
});
});
});
video_detail
<main>
<div class="video-container">
</div>
<div class="video-actions">
<!-- Like, dislike, and subscribe buttons -->
<button class="like-btn"><i class="fas fa-thumbs-up"></i> Like</button>
<button class="dislike-btn"><i class="fas fa-thumbs-down"></i> Dislike</button>
<button class="subscribe-btn"><i class="fas fa-heart"></i> Subscribe</button>
</div>
<div class="comment-section">
</div>
</main>
knowing that the thumbnail is being saved successfully but