Files
2026-01-30 03:04:10 +00:00

217 lines
6.2 KiB
Bash

#!/bin/bash
# fal.ai File Upload Script
# Usage: ./upload.sh --file /path/to/file
# Returns: CDN URL for the uploaded file
#
# Upload flow:
# 1. Get CDN token from rest.alpha.fal.ai
# 2. Upload file to CDN (v3b.fal.media)
# 3. Return access_url
set -e
FAL_TOKEN_ENDPOINT="https://rest.alpha.fal.ai/storage/auth/token?storage_type=fal-cdn-v3"
# Default values
FILE_PATH=""
OUTPUT_JSON=false
# Check for --add-fal-key first
for arg in "$@"; do
if [ "$arg" = "--add-fal-key" ]; then
shift
KEY_VALUE=""
if [[ -n "$1" && ! "$1" =~ ^-- ]]; then
KEY_VALUE="$1"
fi
if [ -z "$KEY_VALUE" ]; then
echo "Enter your fal.ai API key:" >&2
read -r KEY_VALUE
fi
if [ -n "$KEY_VALUE" ]; then
grep -v "^FAL_KEY=" .env > .env.tmp 2>/dev/null || true
mv .env.tmp .env 2>/dev/null || true
echo "FAL_KEY=$KEY_VALUE" >> .env
echo "FAL_KEY saved to .env" >&2
fi
exit 0
fi
done
# Load .env if exists
if [ -f ".env" ]; then
source .env 2>/dev/null || true
fi
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--file|-f)
FILE_PATH="$2"
shift 2
;;
--json)
OUTPUT_JSON=true
shift
;;
--help|-h)
echo "fal.ai File Upload Script" >&2
echo "" >&2
echo "Usage:" >&2
echo " ./upload.sh --file /path/to/file" >&2
echo "" >&2
echo "Options:" >&2
echo " --file, -f Local file path (required)" >&2
echo " --json Output raw JSON response" >&2
echo " --add-fal-key Setup FAL_KEY in .env" >&2
echo "" >&2
echo "Supported file types:" >&2
echo " Images: jpg, jpeg, png, gif, webp, bmp, tiff" >&2
echo " Videos: mp4, mov, avi, webm, mkv" >&2
echo " Audio: mp3, wav, flac, ogg, m4a" >&2
echo "" >&2
echo "Examples:" >&2
echo " ./upload.sh --file photo.jpg" >&2
echo " ./upload.sh --file video.mp4" >&2
echo "" >&2
echo "Usage with generate.sh:" >&2
echo " URL=\$(./upload.sh --file photo.jpg)" >&2
echo " ./generate.sh --image-url \"\$URL\" --prompt \"...\"" >&2
exit 0
;;
*)
# If no flag, treat as file path
if [ -z "$FILE_PATH" ] && [ -f "$1" ]; then
FILE_PATH="$1"
fi
shift
;;
esac
done
# Validate FAL_KEY
if [ -z "$FAL_KEY" ]; then
echo "Error: FAL_KEY not set" >&2
echo "" >&2
echo "Run: ./upload.sh --add-fal-key" >&2
echo "Or: export FAL_KEY=your_key_here" >&2
exit 1
fi
# Validate file
if [ -z "$FILE_PATH" ]; then
echo "Error: --file is required" >&2
exit 1
fi
if [ ! -f "$FILE_PATH" ]; then
echo "Error: File not found: $FILE_PATH" >&2
exit 1
fi
# Get filename
FILENAME=$(basename "$FILE_PATH")
# Detect content type
EXTENSION="${FILENAME##*.}"
EXTENSION_LOWER=$(echo "$EXTENSION" | tr '[:upper:]' '[:lower:]')
case "$EXTENSION_LOWER" in
jpg|jpeg) CONTENT_TYPE="image/jpeg" ;;
png) CONTENT_TYPE="image/png" ;;
gif) CONTENT_TYPE="image/gif" ;;
webp) CONTENT_TYPE="image/webp" ;;
bmp) CONTENT_TYPE="image/bmp" ;;
tiff|tif) CONTENT_TYPE="image/tiff" ;;
mp4) CONTENT_TYPE="video/mp4" ;;
mov) CONTENT_TYPE="video/quicktime" ;;
avi) CONTENT_TYPE="video/x-msvideo" ;;
webm) CONTENT_TYPE="video/webm" ;;
mkv) CONTENT_TYPE="video/x-matroska" ;;
mp3) CONTENT_TYPE="audio/mpeg" ;;
wav) CONTENT_TYPE="audio/wav" ;;
flac) CONTENT_TYPE="audio/flac" ;;
ogg) CONTENT_TYPE="audio/ogg" ;;
m4a) CONTENT_TYPE="audio/mp4" ;;
*) CONTENT_TYPE="application/octet-stream" ;;
esac
# Get file size
FILE_SIZE=$(stat -f%z "$FILE_PATH" 2>/dev/null || stat -c%s "$FILE_PATH" 2>/dev/null)
FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024))
# Check if file is too large (>100MB needs multipart)
if [ "$FILE_SIZE" -gt 104857600 ]; then
echo "Error: File too large (${FILE_SIZE_MB}MB > 100MB)" >&2
echo "Large file multipart upload not yet supported." >&2
echo "Please use the Python client for files >100MB." >&2
exit 1
fi
echo "Uploading: $FILENAME (${FILE_SIZE_MB}MB)" >&2
# Step 1: Get CDN token
echo "Getting CDN token..." >&2
TOKEN_RESPONSE=$(curl -s -X POST "$FAL_TOKEN_ENDPOINT" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d '{}')
# Check for token error
if echo "$TOKEN_RESPONSE" | grep -q '"detail"'; then
ERROR_MSG=$(echo "$TOKEN_RESPONSE" | grep -o '"msg":"[^"]*"' | head -1 | cut -d'"' -f4)
echo "Token error: $ERROR_MSG" >&2
exit 1
fi
TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
TOKEN_TYPE=$(echo "$TOKEN_RESPONSE" | grep -o '"token_type":"[^"]*"' | cut -d'"' -f4)
BASE_URL=$(echo "$TOKEN_RESPONSE" | grep -o '"base_url":"[^"]*"' | cut -d'"' -f4)
if [ -z "$TOKEN" ] || [ -z "$BASE_URL" ]; then
echo "Error: Failed to get CDN token" >&2
echo "$TOKEN_RESPONSE" >&2
exit 1
fi
# Step 2: Upload file
echo "Uploading to CDN..." >&2
UPLOAD_RESPONSE=$(curl -s -X POST "${BASE_URL}/files/upload" \
-H "Authorization: $TOKEN_TYPE $TOKEN" \
-H "Content-Type: $CONTENT_TYPE" \
-H "X-Fal-File-Name: $FILENAME" \
--data-binary "@$FILE_PATH")
# Check for upload error
if echo "$UPLOAD_RESPONSE" | grep -q '"error"'; then
ERROR_MSG=$(echo "$UPLOAD_RESPONSE" | grep -o '"message":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ -z "$ERROR_MSG" ]; then
ERROR_MSG=$(echo "$UPLOAD_RESPONSE" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
fi
echo "Upload error: $ERROR_MSG" >&2
exit 1
fi
# Extract URL
ACCESS_URL=$(echo "$UPLOAD_RESPONSE" | grep -o '"access_url":"[^"]*"' | cut -d'"' -f4)
if [ -z "$ACCESS_URL" ]; then
echo "Error: Failed to get URL from response" >&2
echo "$UPLOAD_RESPONSE" >&2
exit 1
fi
echo "Upload complete!" >&2
echo "URL: $ACCESS_URL" >&2
# Output
if [ "$OUTPUT_JSON" = true ]; then
echo "$UPLOAD_RESPONSE"
else
# Output just the URL for easy piping
echo "$ACCESS_URL"
fi