Skip to main content
The presets service provides functionality for saving, loading, and managing conversion presets. Presets allow users to save their favorite conversion configurations and reuse them across multiple files.

Overview

The presets service handles:
  • Loading built-in and custom presets
  • Saving custom presets to persistent storage
  • Creating new presets from conversion configs
  • Cloning preset configurations
  • Managing preset defaults

Import

import {
  DEFAULT_PRESETS,
  loadCustomPresets,
  saveCustomPresets,
  createCustomPreset,
  cloneConfig,
  getDefaultConfig
} from '$lib/services/presets';

Type Definitions

PresetDefinition

interface PresetDefinition {
  id: string;
  name: string;
  config: ConversionConfig;
  builtIn?: boolean;
}
Properties:
  • id - Unique identifier (UUID for custom presets)
  • name - Display name for the preset
  • config - Complete conversion configuration
  • builtIn - Optional flag indicating built-in preset (cannot be deleted)

Constants

DEFAULT_PRESETS

Array of built-in presets provided by Frame. These presets are read-only and optimized for common use cases.
const DEFAULT_PRESETS: PresetDefinition[];
Built-in Presets:
  1. Balanced MP4 - General purpose H.264 MP4 with AAC audio
    • Container: MP4
    • Video: H.264, CRF 23, medium preset
    • Audio: AAC 128 kbps
  2. Archive H.265 - High-quality archival with H.265
    • Container: MKV
    • Video: H.265, CRF 18, slow preset
    • Audio: AC3 192 kbps
  3. Web Share - Optimized for web delivery
    • Container: WebM
    • Video: VP9, CRF 30, 720p
    • Audio: Opus 96 kbps
  4. GIF Web Small - Small animated GIFs for web
    • Container: GIF
    • Resolution: 640x360, 12 fps
    • Colors: 128, sierra2_4a dither
  5. GIF High Quality - Higher quality animated GIFs
    • Container: GIF
    • Resolution: 720p, 15 fps
    • Colors: 256, floyd_steinberg dither
  6. Audio MP3 - Extract audio as MP3
    • Container: MP3
    • Audio: MP3 128 kbps stereo
  7. Audio FLAC (Lossless) - Lossless audio extraction
    • Container: FLAC
    • Audio: FLAC lossless
  8. Audio ALAC (Apple) - Apple lossless audio
    • Container: M4A
    • Audio: ALAC lossless
  9. Audio WAV (Lossless) - Uncompressed PCM audio
    • Container: WAV
    • Audio: PCM 16-bit
  10. Social (TikTok/Reels) - Vertical video for social media
    • Container: MP4
    • Resolution: 1080x1920, 30 fps
    • Video: H.264 6000 kbps bitrate mode
    • Audio: AAC 128 kbps, normalized
  11. YouTube 1080p - Optimized for YouTube uploads
    • Container: MP4
    • Resolution: 1080p
    • Video: H.264 10000 kbps bitrate mode
    • Audio: AAC 320 kbps, normalized
  12. YouTube 4K - 4K video for YouTube
    • Container: MP4
    • Resolution: 3840x2160
    • Video: H.264 40000 kbps bitrate mode
    • Audio: AAC 320 kbps, normalized
  13. X (Landscape) - Optimized for Twitter/X landscape videos
    • Container: MP4
    • Resolution: 720p, 30 fps
    • Video: H.264 2500 kbps bitrate mode
    • Audio: AAC 128 kbps, normalized
  14. X (Mobile/Portrait) - Portrait videos for Twitter/X
    • Container: MP4
    • Resolution: 720x1280, 30 fps
    • Video: H.264 2000 kbps bitrate mode
    • Audio: AAC 128 kbps, normalized
  15. Discord - Optimized for Discord uploads
    • Container: MP4
    • Resolution: 720p, 30 fps
    • Video: H.264 1000 kbps bitrate mode
    • Audio: AAC 64 kbps, normalized

Functions

loadCustomPresets

Loads user-created custom presets from persistent storage.
async function loadCustomPresets(): Promise<PresetDefinition[]>
Returns: Array of custom preset definitions Example:
const customPresets = await loadCustomPresets();
const allPresets = [...DEFAULT_PRESETS, ...customPresets];

saveCustomPresets

Saves custom presets to persistent storage.
async function saveCustomPresets(
  presets: PresetDefinition[]
): Promise<void>
Parameters:
  • presets - Array of custom presets to save
Note: This overwrites all existing custom presets. Built-in presets are stored separately. Example:
const updated = [...customPresets, newPreset];
await saveCustomPresets(updated);

createCustomPreset

Creates a new custom preset from a conversion configuration.
function createCustomPreset(
  name: string,
  config: ConversionConfig
): PresetDefinition
Parameters:
  • name - Display name for the preset (will be trimmed)
  • config - Conversion configuration to save
Returns: New preset definition with generated UUID Example:
import { createCustomPreset } from '$lib/services/presets';
import type { ConversionConfig } from '$lib/types';

const config: ConversionConfig = {
  container: 'mp4',
  videoCodec: 'libx264',
  videoBitrateMode: 'crf',
  crf: 20,
  // ... rest of config
};

const preset = createCustomPreset('My Custom Preset', config);
// preset.id is a generated UUID
// preset.name is 'My Custom Preset'
// preset.config is a deep clone of the config

cloneConfig

Creates a deep copy of a conversion configuration.
function cloneConfig(
  config: ConversionConfig
): ConversionConfig
Parameters:
  • config - Configuration to clone
Returns: Deep copy of the configuration Example:
const original = getDefaultConfig();
const copy = cloneConfig(original);

copy.crf = 18; // Doesn't affect original

getDefaultConfig

Returns the default conversion configuration (from the first built-in preset).
function getDefaultConfig(): ConversionConfig
Returns: Default conversion configuration Example:
const config = getDefaultConfig();
// Returns a copy of the 'Balanced MP4' preset config

Storage

Custom presets are stored using Tauri’s plugin-store:
  • File: presets.dat
  • Key: presets
  • Format: JSON array of PresetDefinition objects

Real-World Usage

Here’s how the presets service is used in Frame’s codebase:
// From: src/lib/features/conversion/usePresets.svelte.ts

import {
  DEFAULT_PRESETS,
  loadCustomPresets,
  saveCustomPresets,
  createCustomPreset,
  cloneConfig
} from '$lib/services/presets';
import { normalizeConversionConfig } from '$lib/services/config';
import { FileStatus, type PresetDefinition } from '$lib/types';

export function createPresetsManager(callbacks) {
  let customPresets = $state<PresetDefinition[]>([]);
  
  // Combine built-in and custom presets
  const presets = $derived([
    ...DEFAULT_PRESETS, 
    ...customPresets
  ] as PresetDefinition[]);

  // Load custom presets on init
  async function loadPresets() {
    customPresets = await loadCustomPresets();
  }

  // Apply preset to selected file
  function applyPresetToSelection(preset: PresetDefinition) {
    const selectedFile = callbacks.getSelectedFile();
    if (!selectedFile) return;

    const nextConfig = normalizeConversionConfig(
      cloneConfig(preset.config),
      selectedFile.metadata
    );
    
    callbacks.onFilesUpdate((files) =>
      files.map((f) => 
        f.id === selectedFile.id 
          ? { ...f, config: nextConfig } 
          : f
      )
    );
  }

  // Apply preset to all idle files
  function handleApplyPresetToAll(preset: PresetDefinition) {
    callbacks.onFilesUpdate((files) =>
      files.map((f) =>
        f.status === FileStatus.IDLE
          ? {
              ...f,
              config: normalizeConversionConfig(
                cloneConfig(preset.config), 
                f.metadata
              )
            }
          : f
      )
    );
  }

  // Save current config as new preset
  async function handleSavePreset(name: string): Promise<boolean> {
    const selectedFile = callbacks.getSelectedFile();
    if (!selectedFile) return false;

    const trimmedName = name.trim();
    if (!trimmedName) return false;

    const newPreset = createCustomPreset(trimmedName, selectedFile.config);
    const updated = [...customPresets, newPreset];
    
    try {
      await saveCustomPresets(updated);
      customPresets = updated;
      return true;
    } catch (error) {
      console.error('Failed to save preset:', error);
      return false;
    }
  }

  // Delete custom preset
  async function handleDeletePreset(id: string): Promise<boolean> {
    const target = customPresets.find((p) => p.id === id);
    if (!target) return false;

    const updated = customPresets.filter((p) => p.id !== id);
    
    try {
      await saveCustomPresets(updated);
      customPresets = updated;
      return true;
    } catch (error) {
      console.error('Failed to delete preset:', error);
      return false;
    }
  }

  return {
    get presets() { return presets; },
    loadPresets,
    applyPresetToSelection,
    handleApplyPresetToAll,
    handleSavePreset,
    handleDeletePreset
  };
}

Best Practices

Always Clone Configs

When applying a preset, always clone the config to avoid modifying the original:
// Good
const config = cloneConfig(preset.config);
applyToFile(config);

// Bad - modifies the preset!
preset.config.crf = 18;
applyToFile(preset.config);

Normalize After Cloning

Always normalize configs after cloning to ensure compatibility:
import { normalizeConversionConfig } from '$lib/services/config';

const config = normalizeConversionConfig(
  cloneConfig(preset.config),
  fileMetadata
);

Validate Before Saving

Validate preset names before saving:
const name = userInput.trim();
if (!name) {
  console.error('Preset name cannot be empty');
  return;
}

const preset = createCustomPreset(name, config);

Handle Storage Errors

Always handle potential storage errors:
try {
  await saveCustomPresets(updated);
  showSuccess('Preset saved');
} catch (error) {
  console.error('Failed to save preset:', error);
  showError('Failed to save preset');
  // Revert to previous state
}

Build docs developers (and LLMs) love