feat: enable S3 video uploads and update related configurations

- Added support for self-hosted S3 video uploads with new environment variables: OPENFRAME_ENABLE_S3_VIDEO_UPLOADS and OPENFRAME_MAX_VIDEO_UPLOAD_BYTES.
- Updated .env.example and .env.docker.example to reflect new configuration options.
- Enhanced Content Security Policy to include origins for S3-compatible storage.
- Updated dependencies for AWS SDK to support new features.
- Refactored upload logic to accommodate both Bunny and S3 upload providers.
- Updated documentation to clarify the usage of direct uploads and S3 configurations.
- Closes #11
This commit is contained in:
yusufipk
2026-05-27 17:04:39 +02:00
parent b6de3a29aa
commit 4bf6e821af
57 changed files with 2707 additions and 436 deletions
@@ -51,6 +51,15 @@ interface UseCommentActionsParams extends CommentActionsConfig {
fetchAssets: () => Promise<void>;
}
function getAudioUploadFilename(blob: Blob): string {
const mime = blob.type.split(';')[0].trim().toLowerCase();
if (mime === 'audio/mp4') return 'recording.m4a';
if (mime === 'audio/ogg' || mime === 'audio/opus') return 'recording.ogg';
if (mime === 'audio/mpeg') return 'recording.mp3';
if (mime === 'audio/wav') return 'recording.wav';
return 'recording.webm';
}
export function useCommentActions({
videoId,
setVideo,
@@ -430,7 +439,8 @@ export function useCommentActions({
};
mediaRecorder.onstop = () => {
const blob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
const recordedMime = mediaRecorder.mimeType || 'audio/webm';
const blob = new Blob(audioChunksRef.current, { type: recordedMime });
setAudioBlob(blob);
stream.getTracks().forEach((track) => track.stop());
if (recordingTimerRef.current) {
@@ -472,7 +482,8 @@ export function useCommentActions({
try {
const formData = new FormData();
formData.append('audio', audioBlob, 'recording.webm');
const uploadFilename = getAudioUploadFilename(audioBlob);
formData.append('audio', audioBlob, uploadFilename);
formData.append('videoId', videoId);
const uploadToken = await getGuestUploadToken('audio');
if (uploadToken) formData.append('uploadToken', uploadToken);
@@ -514,7 +525,7 @@ export function useCommentActions({
let voiceData: { url: string; duration: number } | undefined;
if (audioBlob) {
const formData = new FormData();
formData.append('audio', audioBlob, 'recording.webm');
formData.append('audio', audioBlob, getAudioUploadFilename(audioBlob));
formData.append('videoId', videoId);
const uploadToken = await getGuestUploadToken('audio');
if (uploadToken) formData.append('uploadToken', uploadToken);
@@ -829,7 +840,8 @@ export function useCommentActions({
if (e.data.size > 0) replyAudioChunksRef.current.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(replyAudioChunksRef.current, { type: 'audio/webm' });
const recordedMime = mediaRecorder.mimeType || 'audio/webm';
const blob = new Blob(replyAudioChunksRef.current, { type: recordedMime });
setReplyAudioBlob(blob);
stream.getTracks().forEach((track) => track.stop());
if (replyRecordingTimerRef.current) {
@@ -870,7 +882,7 @@ export function useCommentActions({
setIsUploadingReplyAudio(true);
try {
const formData = new FormData();
formData.append('audio', replyAudioBlob, 'recording.webm');
formData.append('audio', replyAudioBlob, getAudioUploadFilename(replyAudioBlob));
formData.append('videoId', videoId);
const uploadToken = await getGuestUploadToken('audio');
if (uploadToken) formData.append('uploadToken', uploadToken);
@@ -912,7 +924,7 @@ export function useCommentActions({
if (replyAudioBlob) {
const formData = new FormData();
formData.append('audio', replyAudioBlob, 'recording.webm');
formData.append('audio', replyAudioBlob, getAudioUploadFilename(replyAudioBlob));
formData.append('videoId', videoId);
const uploadToken = await getGuestUploadToken('audio');
if (uploadToken) formData.append('uploadToken', uploadToken);