linkbay-media 0.1.0
FastAPI package for asset management (images, videos, files) with S3-compatible storage, CDN integration, and multi-tenant support
License: MIT
Requires Python: >=3.8
Keywords: fastapi, media, asset-management, s3, cdn, image-optimization, video-processing, multi-tenant

# LinkBay Media v0.1.0

[![License](https://img.shields.io/badge/license-MIT-blue)]() [![Python](https://img.shields.io/badge/python-3.8+-blue)]()

FastAPI package for comprehensive asset management (images, videos, files) with S3-compatible storage, CDN integration, and multi-tenant support.

## Features

- **S3-Compatible Storage**: Works with AWS S3, MinIO, DigitalOcean Spaces, and other S3-compatible services
- **Direct Upload**: Pre-signed URLs for direct client-to-S3 uploads
- **Image Processing**: 
  - Automatic resize and thumbnail generation
  - Format conversion (PNG → WebP)
  - Compression and optimization
  - EXIF metadata extraction
- **Video Processing**:
  - Thumbnail generation from video frames
  - Video transcoding (requires ffmpeg)
  - Metadata extraction
- **CDN Integration**: 
  - Automatic CDN URL generation
  - Cache purging for Cloudflare and CloudFront
- **Multi-tenant**: Isolated storage per tenant
- **Flexible**: No database models included - use your own!

## When to Use This Library

✅ **Use LinkBay Media if you need:**
- Asset management with S3-compatible storage
- Image optimization and thumbnail generation
- Video processing and thumbnails
- CDN integration with automatic cache management
- Multi-tenant file isolation
- Direct client-to-S3 uploads with pre-signed URLs

❌ **Don't use this library if:**
- You need local filesystem storage only
- You don't need image/video processing
- You want a complete CMS (this is just the media layer)

## Installation

```bash
pip install linkbay-media
```

Or install from source:

```bash
pip install git+https://github.com/AlessioQuagliara/LinkBay-Media
```

### Optional Dependencies

For video processing, install ffmpeg:
```bash
# macOS
brew install ffmpeg

# Ubuntu/Debian
sudo apt-get install ffmpeg

# Windows
# Download from https://ffmpeg.org/download.html
```

## Quick Start

### 1. Configure the Package

```python
from fastapi import FastAPI, Header, Depends
from linkbay_media import media_router, MediaConfig

# Create FastAPI app
app = FastAPI()

# Configure media settings
media_config = MediaConfig(
    s3_endpoint_url="https://s3.amazonaws.com",  # Or MinIO, DigitalOcean Spaces
    s3_access_key="your-access-key",
    s3_secret_key="your-secret-key",
    s3_bucket_name="your-bucket",
    s3_region="us-east-1",
    
    # CDN settings (optional)
    cdn_enabled=True,
    cdn_provider="cloudflare",
    cdn_base_url="https://cdn.yourdomain.com",
    cdn_api_key="your-cdn-api-key",
    cdn_zone_id="your-zone-id",
    
    # Upload settings
    max_file_size_mb=50,
    allowed_image_types=["image/jpeg", "image/png", "image/webp"],
    allowed_video_types=["video/mp4", "video/webm"],
    
    # Image processing
    image_optimization_enabled=True,
    convert_to_webp=True,
    thumbnail_sizes=[(100, 100), (300, 300), (800, 800)],
)
```

### 2. Override Dependencies

```python
from linkbay_media.router import get_config, get_tenant_id, get_user_id

# Override configuration dependency
async def override_get_config():
    return media_config

app.dependency_overrides[get_config] = override_get_config

# Override tenant ID (integrate with linkbay-multitenant)
async def override_get_tenant_id(tenant_id: str = Header(..., alias="X-Tenant-ID")):
    return tenant_id

app.dependency_overrides[get_tenant_id] = override_get_tenant_id

# Override user ID (integrate with your auth)
async def override_get_user_id(current_user = Depends(get_current_user)):
    return current_user.id

app.dependency_overrides[get_user_id] = override_get_user_id
```

### 3. Include Router and Create Database Model

```python
# Include media router
app.include_router(media_router)

# Create your own database model (example with SQLAlchemy)
from sqlalchemy import Column, String, Integer, DateTime, JSON, ARRAY
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Media(Base):
    __tablename__ = "media"
    
    id = Column(String, primary_key=True)
    tenant_id = Column(String, nullable=False, index=True)
    filename = Column(String, nullable=False)
    original_filename = Column(String, nullable=False)
    mime_type = Column(String, nullable=False)
    size_bytes = Column(Integer, nullable=False)
    width = Column(Integer)
    height = Column(Integer)
    url = Column(String, nullable=False)
    cdn_url = Column(String)
    s3_key = Column(String, nullable=False)
    folder = Column(String)
    tags = Column(ARRAY(String), default=[])
    metadata = Column(JSON)
    uploaded_by = Column(String)
    created_at = Column(DateTime, nullable=False)
    updated_at = Column(DateTime, nullable=False)
```



## API Endpoints

### Upload Flow

1. **Initiate Upload** - Get pre-signed URL
```http
POST /media/upload
Content-Type: application/json

{
  "filename": "photo.jpg",
  "mime_type": "image/jpeg",
  "size_bytes": 1024000,
  "folder": "photos",
  "tags": ["vacation", "2024"]
}
```

2. **Upload to S3** (from client using the pre-signed URL)

3. **Complete Upload**
```http
POST /media/upload/complete
Content-Type: application/json

{
  "upload_id": "uuid",
  "s3_key": "tenant-id/2024/11/abc123.jpg"
}
```

### Other Endpoints

- `GET /media` - List media with filters
- `GET /media/{id}` - Get media by ID
- `DELETE /media/{id}` - Delete media
- `POST /media/{id}/variants` - Generate thumbnails/variants
- `POST /media/cdn/purge` - Purge CDN cache

## Direct Service Usage

```python
from linkbay_media import StorageService, ImageProcessor, CDNService
import io

# Initialize services
storage = StorageService(media_config)
image_processor = ImageProcessor(media_config)
cdn = CDNService(media_config)

# Generate pre-signed URL
s3_key = storage.generate_s3_key("tenant-123", "photo.jpg")
presigned = storage.generate_presigned_upload_url(s3_key, "image/jpeg")

# Process image
with open("photo.jpg", "rb") as f:
    image_bytes = f.read()

# Extract metadata
metadata = image_processor.extract_metadata(image_bytes)

# Generate thumbnails
thumbnails = image_processor.generate_all_thumbnails(image_bytes)

# Upload to S3
for thumb in thumbnails:
    thumb_key = f"{s3_key}_thumb_{thumb['size'][0]}x{thumb['size'][1]}"
    storage.upload_file(
        io.BytesIO(thumb['bytes']),
        thumb_key,
        f"image/{thumb['format']}"
    )

# Get CDN URL
cdn_url = cdn.get_cdn_url(s3_key)

# Purge cache
await cdn.purge_cache([cdn_url])
```

## Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `s3_endpoint_url` | str | None | S3 endpoint (for MinIO, DO Spaces) |
| `s3_access_key` | str | Required | S3 access key |
| `s3_secret_key` | str | Required | S3 secret key |
| `s3_bucket_name` | str | Required | S3 bucket name |
| `s3_region` | str | us-east-1 | S3 region |
| `cdn_enabled` | bool | False | Enable CDN integration |
| `cdn_provider` | str | None | cloudflare, cloudfront, custom |
| `cdn_base_url` | str | None | CDN base URL |
| `max_file_size_mb` | int | 50 | Max upload size in MB |
| `image_quality` | int | 85 | Compression quality (1-100) |
| `convert_to_webp` | bool | True | Auto-convert to WebP |
| `thumbnail_sizes` | list | [(100,100), (300,300), (800,800)] | Thumbnail sizes |

## Multi-Tenant Isolation

Files are automatically organized by tenant:
```
bucket/
  tenant-123/
    2024/
      11/
        abc123.jpg
        abc123_thumb_100x100.webp
        abc123_thumb_300x300.webp
  tenant-456/
    2024/
      11/
        def456.mp4
```

## Security Considerations

- **Pre-signed URLs**: Expire after configured time (default 1 hour)
- **File Size Limits**: Configurable per tenant tier
- **MIME Type Validation**: Only allowed types can be uploaded
- **Tenant Isolation**: Files are isolated by tenant ID in S3
- **CDN Security**: Use signed URLs for private content

## Testing

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=linkbay_media
```

## Known Limitations

- Video transcoding requires ffmpeg to be installed on the system
- Large file uploads may require increased timeout settings
- CDN purging is asynchronous and may take time to propagate

## Roadmap

- [ ] Add support for more CDN providers (Fastly, Akamai)
- [ ] Implement HLS streaming for videos
- [ ] Add image face detection and smart cropping
- [ ] Support for audio file processing
- [ ] Add watermarking capabilities
- [ ] Implement AI-powered image tagging

## License

MIT License - see LICENSE file for details.

## Support

- Issues: https://github.com/AlessioQuagliara/LinkBay-Media/issues
- Email: quagliara.alessio@gmail.com

## Credits

Created by Alessio Quagliara for the LinkBay CMS ecosystem.

Part of the LinkBay suite:
- `linkbay-core` - Core utilities
- `linkbay-multitenant` - Multi-tenancy support
- `linkbay-media` - Media management (this package)

Contributions and feedback are welcome! 🧡