File size: 7,183 Bytes
0a8cce5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40ff32c
 
0a8cce5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import { useState, useRef, useCallback } from "react";
import GlassContainer from "./GlassContainer";
import GlassButton from "./GlassButton";
import { GLASS_EFFECTS } from "../constants";
import type { VideoUploadState } from "../types";

interface VideoUploadScreenProps {
  onVideoReady: (videoState: VideoUploadState) => void;
  onBack: () => void;
}

export default function VideoUploadScreen({ onVideoReady, onBack }: VideoUploadScreenProps) {
  const [dragActive, setDragActive] = useState(false);
  const [uploadError, setUploadError] = useState<string | null>(null);
  const [isProcessing, setIsProcessing] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const handleFiles = useCallback(async (files: FileList | null) => {
    if (!files || files.length === 0) return;

    const file = files[0];
    
    // Validate file type
    if (!file.type.startsWith('video/')) {
      setUploadError('Please select a valid video file.');
      return;
    }

    // Validate file size (100MB limit)
    if (file.size > 1024 * 1024 * 1024) {
      setUploadError('Video file is too large. Please select a file under 1GB.');
      return;
    }

    setIsProcessing(true);
    setUploadError(null);

    try {
      // Create video element
      const videoElement = document.createElement('video');
      videoElement.muted = true;
      videoElement.controls = false;
      videoElement.preload = 'metadata';
      
      // Create object URL for the video
      const videoUrl = URL.createObjectURL(file);
      videoElement.src = videoUrl;

      // Wait for video metadata to load
      await new Promise<void>((resolve, reject) => {
        videoElement.onloadedmetadata = () => resolve();
        videoElement.onerror = () => reject(new Error('Failed to load video'));
        videoElement.load();
      });

      // Validate video duration (max 10 minutes)
      if (videoElement.duration > 600) {
        setUploadError('Video is too long. Please select a video under 10 minutes.');
        URL.revokeObjectURL(videoUrl);
        return;
      }

      const videoState: VideoUploadState = {
        file,
        videoElement,
        isReady: true
      };

      onVideoReady(videoState);
    } catch (error) {
      console.error('Error processing video:', error);
      setUploadError('Failed to process video file. Please try a different file.');
    } finally {
      setIsProcessing(false);
    }
  }, [onVideoReady]);

  const handleDrag = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);

  const handleDragIn = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
      setDragActive(true);
    }
  }, []);

  const handleDragOut = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(false);
  }, []);

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setDragActive(false);
    
    if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
      handleFiles(e.dataTransfer.files);
    }
  }, [handleFiles]);

  const handleFileSelect = useCallback(() => {
    fileInputRef.current?.click();
  }, []);

  const handleFileChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    handleFiles(e.target.files);
  }, [handleFiles]);

  return (
    <div className="absolute inset-0 text-white flex items-center justify-center p-8">
      <div className="max-w-2xl w-full space-y-8">
        {/* Header */}
        <GlassContainer
          className="rounded-3xl shadow-2xl hover:scale-105 transition-transform duration-200"
          role="banner"
        >
          <div className="p-8 text-center">
            <h1 className="text-4xl font-bold text-gray-100 mb-4">Upload Video</h1>
            <p className="text-lg text-gray-300 leading-relaxed">
              Select a video file to analyze with FastVLM
            </p>
          </div>
        </GlassContainer>

        {/* Upload Area */}
        <GlassContainer
          className={`rounded-2xl shadow-2xl transition-all duration-200 border-2 border-dashed ${
            dragActive
              ? 'border-blue-400 bg-blue-500/10 scale-105'
              : uploadError
              ? 'border-red-400 bg-red-500/10'
              : 'border-gray-500 hover:border-gray-400 hover:scale-105'
          }`}
          bgColor={
            dragActive
              ? GLASS_EFFECTS.COLORS.BUTTON_BG
              : uploadError
              ? GLASS_EFFECTS.COLORS.ERROR_BG
              : GLASS_EFFECTS.COLORS.DEFAULT_BG
          }
          onDrag={handleDrag}
          onDragStart={handleDrag}
          onDragEnd={handleDrag}
          onDragOver={handleDrag}
          onDragEnter={handleDragIn}
          onDragLeave={handleDragOut}
          onDrop={handleDrop}
        >
          <div className="p-12 text-center">
            {isProcessing ? (
              <div className="space-y-4">
                <div className="text-6xl"></div>
                <h3 className="text-xl font-semibold text-gray-200">Processing video...</h3>
                <p className="text-gray-400">Please wait while we prepare your video</p>
              </div>
            ) : (
              <div className="space-y-6">
                <div className="text-6xl">{dragActive ? '📂' : '📁'}</div>
                <div>
                  <h3 className="text-xl font-semibold text-gray-200 mb-2">
                    {dragActive ? 'Drop your video here' : 'Upload Video File'}
                  </h3>
                  <p className="text-gray-400 mb-4">
                    Drag and drop a video file or click to browse
                  </p>
                  <div className="text-sm text-gray-500 space-y-1">
                    <p>Supported formats: MP4, WebM, AVI, MOV</p>
                    <p>Maximum size: 100MB | Maximum duration: 10 minutes</p>
                  </div>
                </div>
                
                <GlassButton
                  onClick={handleFileSelect}
                  className="px-6 py-3 rounded-xl"
                  disabled={isProcessing}
                >
                  Choose File
                </GlassButton>
              </div>
            )}

            {uploadError && (
              <div className="mt-6 p-4 rounded-xl bg-red-500/20 border border-red-500/30">
                <p className="text-red-300 font-medium">{uploadError}</p>
              </div>
            )}
          </div>
        </GlassContainer>

        {/* Back Button */}
        <div className="flex justify-center">
          <GlassButton
            onClick={onBack}
            className="px-6 py-3 rounded-xl"
            disabled={isProcessing}
          >
            ← Back to Options
          </GlassButton>
        </div>

        {/* Hidden file input */}
        <input
          ref={fileInputRef}
          type="file"
          accept="video/*"
          onChange={handleFileChange}
          className="hidden"
        />
      </div>
    </div>
  );
}