# Chunked Video Upload Implementation

## Overview
Implemented chunked upload system for large video files (1GB+). Videos are uploaded in 5MB chunks for better reliability and user experience.

## Architecture

### Backend
- **ChunkUploadController**: Handles chunk uploads, merging, and cancellation
- **Routes**: 
  - `POST /admin/chunk-upload/upload` - Upload individual chunks
  - `POST /admin/chunk-upload/merge` - Merge chunks into final file
  - `POST /admin/chunk-upload/cancel` - Cancel upload and cleanup

### Frontend
- **ChunkedUploader.js**: JavaScript class for managing chunked uploads
- Handles file splitting, progress tracking, and automatic retry logic

### Database
- Chunks stored temporarily in `storage/app/public/chunks/{uploadId}/`
- Permanent storage in `storage/app/public/content_videos/` or `storage/app/public/content_thumbnails/`

## How It Works

```
User selects video (1GB)
    ↓
JavaScript splits into 200 chunks (5MB each)
    ↓
Each chunk uploaded via AJAX with progress
    ↓
Backend receives chunks, stores in temp directory
    ↓
All chunks uploaded → Client sends merge request
    ↓
Backend combines chunks → Creates final file
    ↓
Chunks cleaned up
    ↓
Form submitted with file path → Database updated
```

## Frontend Implementation

### Basic HTML Form
```html
<form id="content-form" method="POST" action="{{ route('new.contentcreator.store') }}">
    @csrf
    
    <div class="form-group">
        <label>Title</label>
        <input type="text" name="title" required>
    </div>

    <!-- Video Upload -->
    <div class="form-group">
        <label>Video (chunk upload)</label>
        <input type="file" id="video-input" accept="video/*">
        <div id="video-progress" style="display: none;">
            <div class="progress">
                <div id="video-progress-bar" class="progress-bar" style="width: 0%"></div>
            </div>
            <p id="video-status"></p>
        </div>
    </div>

    <!-- Hidden field to store uploaded video path -->
    <input type="hidden" id="video-path" name="video_path" value="">

    <!-- Thumbnail Upload -->
    <div class="form-group">
        <label>Thumbnail (optional)</label>
        <input type="file" id="thumbnail-input" accept="image/*">
        <div id="thumbnail-progress" style="display: none;">
            <div class="progress">
                <div id="thumbnail-progress-bar" class="progress-bar" style="width: 0%"></div>
            </div>
            <p id="thumbnail-status"></p>
        </div>
    </div>

    <!-- Hidden field to store uploaded thumbnail path -->
    <input type="hidden" id="thumbnail-path" name="thumbnail_path" value="">

    <button type="submit" id="submit-btn" disabled>Create Content</button>
</form>

<script src="/js/chunked-uploader.js"></script>
<script>
    let videoReady = false;
    let thumbnailReady = false;

    // Video uploader
    const videoUploader = new ChunkedUploader('#video-input', {
        chunkSize: 5 * 1024 * 1024, // 5MB
        fileType: 'video',
        onProgress: (progress) => {
            document.getElementById('video-progress-bar').style.width = progress + '%';
            document.getElementById('video-status').textContent = `${progress}%`;
        },
        onSuccess: (filePath) => {
            document.getElementById('video-path').value = filePath;
            document.getElementById('video-progress').style.display = 'none';
            document.getElementById('video-input').disabled = true;
            videoReady = true;
            updateSubmitBtn();
            alert('Video uploaded successfully!');
        },
        onError: (error) => {
            alert('Video upload failed: ' + error);
        }
    });

    // Thumbnail uploader
    const thumbnailUploader = new ChunkedUploader('#thumbnail-input', {
        chunkSize: 5 * 1024 * 1024, // 5MB
        fileType: 'thumbnail',
        onProgress: (progress) => {
            document.getElementById('thumbnail-progress-bar').style.width = progress + '%';
            document.getElementById('thumbnail-status').textContent = `${progress}%`;
        },
        onSuccess: (filePath) => {
            document.getElementById('thumbnail-path').value = filePath;
            document.getElementById('thumbnail-progress').style.display = 'none';
            document.getElementById('thumbnail-input').disabled = true;
            thumbnailReady = true;
            updateSubmitBtn();
            alert('Thumbnail uploaded successfully!');
        },
        onError: (error) => {
            alert('Thumbnail upload failed: ' + error);
        }
    });

    // Handle video file selection
    document.getElementById('video-input').addEventListener('change', function() {
        if (this.files.length) {
            document.getElementById('video-progress').style.display = 'block';
            videoReady = false;
            updateSubmitBtn();
            videoUploader.start();
        }
    });

    // Handle thumbnail file selection
    document.getElementById('thumbnail-input').addEventListener('change', function() {
        if (this.files.length) {
            document.getElementById('thumbnail-progress').style.display = 'block';
            thumbnailReady = false;
            updateSubmitBtn();
            thumbnailUploader.start();
        }
    });

    function updateSubmitBtn() {
        // Video is required, thumbnail is optional
        const hasVideo = document.getElementById('video-path').value !== '';
        document.getElementById('submit-btn').disabled = !hasVideo;
    }

    // Form submission
    document.getElementById('content-form').addEventListener('submit', function(e) {
        if (!videoReady) {
            e.preventDefault();
            alert('Please upload a video first');
        }
    });
</script>
```

## Backend Configuration

No additional configuration needed:
- ✅ Routes are in `routes/web.php`
- ✅ Controller handles all chunking logic
- ✅ Temporary cleanup built-in
- ✅ Error logging to `storage/logs/laravel.log`

## Features

✅ **Auto-retry** - Failed chunks can be retried
✅ **Progress tracking** - Real-time upload progress
✅ **Large file support** - No HTTP timeout issues
✅ **Cleanup** - Temp files auto-deleted after merge
✅ **Error handling** - Graceful failure with logging
✅ **CSRF protection** - Uses CSRF token from meta tag
✅ **Resume capability** - Each chunk is independent

## File Structure

```
storage/app/public/
├── chunks/               (temporary, auto-cleaned)
│   └── {uploadId}/
│       ├── chunk_0
│       ├── chunk_1
│       └── ...
├── content_videos/       (final videos)
│   └── {timestamp}_{random}.mp4
└── content_thumbnails/   (final thumbnails)
    └── {timestamp}_{random}.jpg
```

## Troubleshooting

### Chunks not uploading
- Check browser console for errors
- Ensure CSRF token is in `<meta name="csrf-token">`
- Check `storage/logs/laravel.log`

### Chunks stuck in temp directory
```bash
# Cleanup old chunks (older than 1 hour)
php artisan tinker
> \Illuminate\Support\Facades\Storage::disk('public')->deleteDirectory('chunks');
```

### Memory issues with large files
- Increase PHP memory limit in `php.ini`:
  ```ini
  memory_limit = 512M
  ```
- Or increase upload limit:
  ```ini
  post_max_size = 2G
  upload_max_filesize = 2G
  ```

## Modified Files

- `app/Http/Controllers/Backend/AdminNew/ChunkUploadController.php` - New
- `app/Http/Controllers/Backend/AdminNew/ContentCreatorController.php` - Updated
- `public/js/chunked-uploader.js` - New
- `routes/web.php` - Updated with chunk routes
