Skip to content

Video Processing

Upload and process videos with HLS streaming powered by Mux.

Upload a Video

Videos are uploaded the same way as images:

javascript
const formData = new FormData();
formData.append('file', videoFile); // Video file
formData.append('tags', 'tutorial,demo');

const response = await fetch('https://api.reimage.dev/upload/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.REIMAGE_API_KEY}`
  },
  body: formData
});

const data = await response.json();
console.log(data);
/*
{
  "object_id": "vid-abc123",
  "object_url": "https://files.reimage.dev/dirname/vid-abc123/",
  "video_url": "https://files.reimage.dev/dirname/vid-abc123/original.m3u8",
  "size": 5242.88,
  "type": "video/mp4"
}
*/
python
import requests

with open('video.mp4', 'rb') as f:
    files = {'file': f}
    data = {'tags': 'tutorial,demo'}
    headers = {'Authorization': 'Bearer YOUR_API_KEY'}

    response = requests.post(
        'https://api.reimage.dev/upload/',
        files=files,
        data=data,
        headers=headers
    )

result = response.json()
print(f"Video URL: {result['video_url']}")
php
<?php
$filePath = 'video.mp4';
$cfile = new CURLFile($filePath, 'video/mp4', 'video.mp4');

$postData = [
    'file' => $cfile,
    'tags' => 'tutorial,demo'
];

$ch = curl_init('https://api.reimage.dev/upload/');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_API_KEY'
]);

$response = curl_exec($ch);
$result = json_decode($response, true);

echo "Video URL: " . $result['video_url'];
?>

Video Response

Videos have a special vid- prefix in their object ID:

json
{
  "object_id": "vid-abc123",
  "video_url": "https://files.reimage.dev/dirname/vid-abc123/original.m3u8"
}

HLS Streaming

Videos are automatically processed for HLS (HTTP Live Streaming):

https://files.reimage.dev/{dirname}/{vid-object_id}/original.m3u8

This provides:

  • Adaptive bitrate streaming
  • Multiple quality levels
  • Efficient bandwidth usage
  • Broad device compatibility

Playing Videos

HTML5 Video with HLS.js

html
<video id="video" controls style="width: 100%; max-width: 800px;"></video>

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
  const video = document.getElementById('video');
  const videoUrl = 'https://files.reimage.dev/dirname/vid-abc123/original.m3u8';

  if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource(videoUrl);
    hls.attachMedia(video);
  }
  // Native HLS support (Safari)
  else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = videoUrl;
  }
</script>

React Video Player

jsx
import { useEffect, useRef } from 'react';
import Hls from 'hls.js';

function VideoPlayer({ videoUrl }) {
  const videoRef = useRef(null);

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    if (Hls.isSupported()) {
      const hls = new Hls();
      hls.loadSource(videoUrl);
      hls.attachMedia(video);

      return () => {
        hls.destroy();
      };
    }
    // Native HLS support (Safari)
    else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = videoUrl;
    }
  }, [videoUrl]);

  return (
    <video
      ref={videoRef}
      controls
      style={{ width: '100%', maxWidth: '800px' }}
    />
  );
}

// Usage
<VideoPlayer videoUrl="https://files.reimage.dev/dirname/vid-abc123/original.m3u8" />

Using Video.js

html
<link href="https://vjs.zencdn.net/8.3.0/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.3.0/video.min.js"></script>

<video
  id="my-video"
  class="video-js"
  controls
  preload="auto"
  width="800"
  height="450"
>
  <source
    src="https://files.reimage.dev/dirname/vid-abc123/original.m3u8"
    type="application/x-mpegURL"
  />
</video>

<script>
  const player = videojs('my-video');
</script>

Video Storage Limits

Video storage is measured in minutes, not kilobytes:

javascript
const user = await fetch('https://api.reimage.dev/user/', {
  headers: { 'Authorization': `Bearer ${API_KEY}` }
}).then(r => r.json());

console.log(`Video: ${user.video_storage_used} / ${user.video_storage_limit} minutes`);

Storage Limits by Tier

TierVideo Minutes
Free0 minutes
Pro120 minutes
EnterpriseCustom

Processing Status

Videos are processed asynchronously:

  1. Upload returns immediately with video_url
  2. Video is processed in the background by Mux
  3. HLS stream becomes available when processing completes
  4. Webhook notification sent when ready (if configured)

Check if Video is Ready

javascript
async function isVideoReady(videoUrl) {
  try {
    const response = await fetch(videoUrl, { method: 'HEAD' });
    return response.ok;
  } catch {
    return false;
  }
}

// Usage
const ready = await isVideoReady(videoData.video_url);
if (ready) {
  console.log('Video is ready to play');
} else {
  console.log('Video still processing...');
}

Supported Video Formats

  • MP4 (.mp4)
  • MOV (.mov)
  • AVI (.avi)
  • WebM (.webm)
  • MKV (.mkv)
  • And most other common formats

Video Upload Progress

jsx
function VideoUploader() {
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [videoData, setVideoData] = useState(null);

  const handleUpload = async (file) => {
    setUploading(true);

    const formData = new FormData();
    formData.append('file', file);
    formData.append('tags', 'user-video');

    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100;
        setProgress(percent);
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        const data = JSON.parse(xhr.responseText);
        setVideoData(data);
        setUploading(false);
      }
    });

    xhr.open('POST', 'https://api.reimage.dev/upload/');
    xhr.setRequestHeader('Authorization', `Bearer ${process.env.REACT_APP_REIMAGE_API_KEY}`);
    xhr.send(formData);
  };

  return (
    <div>
      <input
        type="file"
        accept="video/*"
        onChange={(e) => handleUpload(e.target.files[0])}
        disabled={uploading}
      />

      {uploading && (
        <div>
          <progress value={progress} max="100" />
          <span>{progress.toFixed(0)}%</span>
        </div>
      )}

      {videoData && (
        <div>
          <h3>Upload Complete!</h3>
          <p>Video is processing... This may take a few minutes.</p>
          <VideoPlayer videoUrl={videoData.video_url} />
        </div>
      )}
    </div>
  );
}

Best Practices

  1. Compress before upload - Use H.264 codec for best compatibility
  2. Reasonable resolutions - 1080p is usually sufficient
  3. Monitor usage - Keep track of video minutes used
  4. Handle processing time - Videos aren't immediately available
  5. Use HLS.js - For broad browser compatibility

Thumbnail Generation

Video thumbnails are not automatically generated. You can:

  1. Extract a frame client-side
  2. Upload a separate thumbnail image
  3. Use a video poster image
html
<video
  controls
  poster="https://files.reimage.dev/dirname/thumbnail/w-800.jpg"
>
  <source src="video.m3u8" type="application/x-mpegURL" />
</video>

Error Handling

javascript
async function uploadVideo(file) {
  // Check user's video storage before upload
  const user = await fetch('https://api.reimage.dev/user/', {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  }).then(r => r.json());

  if (user.video_storage_used >= user.video_storage_limit) {
    throw new Error('Video storage limit exceeded');
  }

  // Proceed with upload
  const formData = new FormData();
  formData.append('file', file);

  const response = await fetch('https://api.reimage.dev/upload/', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${API_KEY}` },
    body: formData
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'Upload failed');
  }

  return await response.json();
}

Next Steps

Released under the MIT License.