Skip to main content
This endpoint requires authentication. Users can only access their own media files.

Overview

Retrieve all media files from the user’s Media Vault. This endpoint automatically syncs storage files with the database, backfilling any missing records.

Query Parameters

category
string
default:"all"
Filter media by source category.Options:
  • all - Return all media
  • smart-caption - Only Smart Captions
  • babecock - Only Babecock Studio media
  • hypnococks - Only HypnoCocks media
  • upload - Only user uploads
dateRange
string
default:"all"
Filter media by creation date.Options:
  • all - All time
  • today - Today only
  • week - Last 7 days
  • month - Current month
  • year - Current year
Search media by filename, original name, caption, or character name. Case-insensitive partial matching.

Storage Synchronization

Automatic Backfill

This endpoint performs automatic synchronization between Supabase Storage and the database:
  1. Fetch existing database records for the user
  2. List all files from the following storage folders:
    • Captions/ (Smart Captions)
    • Babecocks/ (Babecock Studio)
    • HypnoCocks/ (HypnoCocks)
    • Uploads/ (General uploads)
  3. Backfill missing records: Any files found in storage but not in the database are automatically added
  4. Queue enrichment: Newly backfilled items are queued for metadata enrichment (dimensions, character detection, etc.)

Backfilled Media Fields

{
  userId: "user_123",
  fileName: "file.jpg",
  originalName: "file.jpg",
  fileUrl: "https://...supabase.co/.../file.jpg",
  filePath: "users/user_123/Captions/file.jpg",
  fileSize: 0, // Size from storage metadata if available
  mimeType: "application/octet-stream", // Or from storage metadata
  source: "smart-caption", // Derived from folder name
  width: 0,
  height: 0,
  isShared: false,
  shareToCommunity: false,
  subreddit: null,
  enrichmentStatus: "pending",
  metadata: {
    tool: "smart-caption",
    backfilledAt: "2024-03-08T12:34:56.789Z"
  }
}

Response

files
array
required
Array of media file objects
totalCount
number
required
Total number of files returned after filtering
categories
object
required
Count of files in each categoryProperties:
  • smart-caption (number)
  • babecock (number)
  • hypnococks (number)
  • upload (number)

File Object Schema

files[].id
number
required
Database ID of the media item (for bulk operations)
files[].name
string
required
Display name of the file (friendly name or filename)
files[].originalName
string
required
Original filename when uploaded
files[].category
string
required
Source category: smart-caption, babecock, hypnococks, or upload
files[].size
number
required
File size in bytes
files[].lastModified
string
required
ISO 8601 timestamp of when the file was created
files[].created_at
string
required
ISO 8601 timestamp of creation (same as lastModified)
files[].url
string
required
Public URL to access the file
files[].path
string
required
Storage path: users/{userId}/{Folder}/{filename}
files[].subreddit
string
Source subreddit if the media was fetched from Reddit
files[].width
number
Image/video width in pixels (if enriched)
files[].height
number
Image/video height in pixels (if enriched)
files[].isShared
boolean
required
Whether the media has a share link generated
files[].shareCode
string
Unique share code if the media is shared
files[].characterName
string
Detected character or model name (if enriched)
files[].enrichmentStatus
string
Status of metadata enrichment: pending, processing, completed, or failed
files[].enrichedAt
string
ISO 8601 timestamp of when enrichment was completed

Success Response Example

{
  "files": [
    {
      "id": 456,
      "name": "caption-1709856234567-beach_photo.jpg",
      "originalName": "beach_photo.jpg",
      "category": "smart-caption",
      "size": 2048576,
      "lastModified": "2024-03-08T10:30:34.567Z",
      "created_at": "2024-03-08T10:30:34.567Z",
      "url": "https://example.supabase.co/storage/v1/object/public/user-media/users/user_123/Captions/caption-1709856234567-beach_photo.jpg",
      "path": "users/user_123/Captions/caption-1709856234567-beach_photo.jpg",
      "subreddit": "EarthPorn",
      "width": 1920,
      "height": 1080,
      "isShared": true,
      "shareCode": "abc123def456",
      "characterName": null,
      "enrichmentStatus": "completed",
      "enrichedAt": "2024-03-08T10:31:05.123Z"
    },
    {
      "id": 457,
      "name": "babecock-1709856300000-composite.png",
      "originalName": "composite.png",
      "category": "babecock",
      "size": 3145728,
      "lastModified": "2024-03-08T11:45:00.000Z",
      "created_at": "2024-03-08T11:45:00.000Z",
      "url": "https://example.supabase.co/storage/v1/object/public/user-media/users/user_123/Babecocks/babecock-1709856300000-composite.png",
      "path": "users/user_123/Babecocks/babecock-1709856300000-composite.png",
      "subreddit": null,
      "width": 2560,
      "height": 1440,
      "isShared": false,
      "shareCode": null,
      "characterName": "Riley Reid",
      "enrichmentStatus": "completed",
      "enrichedAt": "2024-03-08T11:45:30.456Z"
    }
  ],
  "totalCount": 2,
  "categories": {
    "smart-caption": 1,
    "babecock": 1,
    "hypnococks": 0,
    "upload": 0
  }
}

Error Responses

500 Internal Server Error

{
  "message": "Failed to fetch user media"
}

Example Usage

cURL

# Get all media
curl -X GET "https://your-domain.com/api/user/media" \
  -H "Cookie: connect.sid=your-session-cookie"

# Filter by category
curl -X GET "https://your-domain.com/api/user/media?category=smart-caption" \
  -H "Cookie: connect.sid=your-session-cookie"

# Search and filter by date
curl -X GET "https://your-domain.com/api/user/media?search=beach&dateRange=week" \
  -H "Cookie: connect.sid=your-session-cookie"

JavaScript (Fetch API)

// Fetch all media
const response = await fetch('/api/user/media', {
  credentials: 'include'
});
const data = await response.json();
console.log(`Total files: ${data.totalCount}`);
console.log('Files:', data.files);

// Filter by category
const smartCaptionsResponse = await fetch('/api/user/media?category=smart-caption', {
  credentials: 'include'
});
const smartCaptions = await smartCaptionsResponse.json();

React Example with Filtering

import { useState, useEffect } from 'react';

function MediaGallery() {
  const [media, setMedia] = useState([]);
  const [category, setCategory] = useState('all');
  const [search, setSearch] = useState('');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchMedia = async () => {
      setLoading(true);
      const params = new URLSearchParams();
      if (category !== 'all') params.append('category', category);
      if (search) params.append('search', search);

      const response = await fetch(`/api/user/media?${params}`, {
        credentials: 'include'
      });
      const data = await response.json();
      setMedia(data.files);
      setLoading(false);
    };

    fetchMedia();
  }, [category, search]);

  return (
    <div>
      <div>
        <select value={category} onChange={(e) => setCategory(e.target.value)}>
          <option value="all">All Categories</option>
          <option value="smart-caption">Smart Captions</option>
          <option value="babecock">Babecock</option>
          <option value="hypnococks">HypnoCocks</option>
          <option value="upload">Uploads</option>
        </select>
        <input
          type="text"
          placeholder="Search..."
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
      </div>

      {loading ? (
        <p>Loading...</p>
      ) : (
        <div className="grid">
          {media.map((file) => (
            <div key={file.id}>
              <img src={file.url} alt={file.originalName} />
              <p>{file.originalName}</p>
              <p>{file.category}</p>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Notes

  • Media is sorted by creation date in descending order (newest first)
  • The endpoint performs automatic backfill on every request to keep database in sync with storage
  • Metadata files (.json) are automatically excluded from results
  • Only valid media files (jpg, jpeg, png, gif, webp, mp4, mov, webm) are included
  • Enrichment is queued automatically for backfilled items but happens asynchronously

Build docs developers (and LLMs) love