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
+23 -5
View File
@@ -71,14 +71,16 @@ interface VideoPageContentProps {
mode: VideoPageMode;
videoId: string;
projectId?: string;
bunnyUploadsEnabled?: boolean;
directUploadsEnabled?: boolean;
directUploadProvider?: import('@/components/video-page/types').DirectUploadProvider;
}
export function VideoPageContent({
mode,
videoId,
projectId: propProjectId,
bunnyUploadsEnabled = true,
directUploadsEnabled = false,
directUploadProvider = 'bunny',
}: VideoPageContentProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const videoRef = useRef<HTMLVideoElement>(null);
@@ -200,7 +202,8 @@ export function VideoPageContent({
} = useVersionActions({
projectId: propProjectId,
videoId,
bunnyUploadsEnabled,
directUploadsEnabled,
directUploadProvider,
setVideo,
activeVersionId,
setActiveVersionId,
@@ -282,6 +285,20 @@ export function VideoPageContent({
if (!bunnyCdnHostname) return '';
return `https://${bunnyCdnHostname}/${activeVersion.videoId}/playlist.m3u8`;
}
if (activeVersion.providerId === 'r2') {
if (activeVersion.originalUrl.startsWith('/api/upload/video/')) {
return activeVersion.originalUrl;
}
if (activeVersion.originalUrl.startsWith('videos/')) {
const filename = activeVersion.originalUrl.slice('videos/'.length);
return `/api/upload/video/${filename}`;
}
if (activeVersion.videoId.startsWith('videos/')) {
const filename = activeVersion.videoId.slice('videos/'.length);
return `/api/upload/video/${filename}`;
}
return activeVersion.originalUrl;
}
try {
const url = new URL(activeVersion.originalUrl);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
@@ -551,7 +568,8 @@ export function VideoPageContent({
const isBunnyVersion = activeVersion?.providerId === 'bunny';
const showBunnyProcessingOverlay =
isBunnyVersion && bunnyPlaybackState === 'processing' && !isReady;
const showBunnyErrorOverlay = isBunnyVersion && bunnyPlaybackState === 'error';
const isR2Version = activeVersion?.providerId === 'r2';
const showBunnyErrorOverlay = (isBunnyVersion || isR2Version) && bunnyPlaybackState === 'error';
const confirmGuestName = useCallback(() => {
if (!guestName.trim()) return;
@@ -732,7 +750,7 @@ export function VideoPageContent({
onDownload={headerActions.onDownload}
projectId={projectId}
videoId={videoId}
bunnyUploadsEnabled={bunnyUploadsEnabled}
directUploadsEnabled={directUploadsEnabled}
showVersionDialog={showVersionDialog}
setShowVersionDialog={setShowVersionDialog}
newVersionMode={newVersionMode}