Skip to content

Migrate format_presets.py to spec + code generation #185

@rtibbles

Description

@rtibbles

This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.

Overview

Migrate le_utils/constants/format_presets.py from the legacy JSON-as-data approach to the modern spec + code generation system. This is the most complex module with 10+ fields per preset and references to content kinds and file formats.

This module references constants from both file_formats and content_kinds, so both must be migrated first.

Context

Currently, le_utils/constants/format_presets.py uses the legacy approach:

  • Loads resources/presetlookup.json at runtime
  • Manual Python constants (VIDEO_HIGH_RES = "high_res_video", etc.)
  • Complex namedtuple with many fields
  • No JavaScript export available

Current Structure

File: le_utils/resources/presetlookup.json

{
  "high_res_video": {
    "readable_name": "High Resolution",
    "multi_language": false,
    "supplementary": false,
    "thumbnail": false,
    "subtitle": false,
    "display": true,
    "order": 1,
    "kind": "video",
    "allowed_formats": ["mp4", "webm"],
    "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
  },
  ...
}

Python module has:

  • Namedtuple: class Preset(namedtuple("Preset", ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"])): pass
  • Manual constants for each preset
  • PRESETLIST with Preset namedtuples
  • choices tuple

Target Spec Format

Create spec/constants-format_presets.json with all preset data:

{
  "namedtuple": {
    "name": "Preset",
    "fields": ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"]
  },
  "constants": {
    "high_res_video": {
      "readable_name": "High Resolution",
      "multi_language": false,
      "supplementary": false,
      "thumbnail": false,
      "subtitle": false,
      "display": true,
      "order": 1,
      "kind_id": "video",
      "allowed_formats": ["mp4", "webm"],
      "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    },
    "low_res_video": {
      "readable_name": "Low Resolution",
      "multi_language": false,
      "supplementary": false,
      "thumbnail": false,
      "subtitle": false,
      "display": true,
      "order": 2,
      "kind_id": "video",
      "allowed_formats": ["mp4", "webm"],
      "convertible_formats": ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    }
  }
}

Copy all presets from presetlookup.json, changing "kind" to "kind_id" to match the namedtuple field name.

Generated Output Example

Python (le_utils/constants/format_presets.py):

# Generated by scripts/generate_from_specs.py
from collections import namedtuple

class Preset(namedtuple("Preset", ["id", "readable_name", "multi_language", "supplementary", "thumbnail", "subtitle", "display", "order", "kind_id", "allowed_formats", "convertible_formats"])):
    pass

VIDEO_HIGH_RES = "high_res_video"
VIDEO_LOW_RES = "low_res_video"
# ...

choices = (
    (VIDEO_HIGH_RES, "Video High Res"),
    (VIDEO_LOW_RES, "Video Low Res"),
    # ...
)

PRESETLIST = [
    Preset(
        id="high_res_video",
        readable_name="High Resolution",
        multi_language=False,
        supplementary=False,
        thumbnail=False,
        subtitle=False,
        display=True,
        order=1,
        kind_id="video",
        allowed_formats=["mp4", "webm"],
        convertible_formats=["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    ),
    # ...
]

JavaScript (js/FormatPresets.js):

// Generated by scripts/generate_from_specs.py

export default {
    VIDEO_HIGH_RES: "high_res_video",
    VIDEO_LOW_RES: "low_res_video",
    // ...
};

export const PresetsList = [
    {
        id: "high_res_video",
        readable_name: "High Resolution",
        multi_language: false,
        supplementary: false,
        thumbnail: false,
        subtitle: false,
        display: true,
        order: 1,
        kind_id: "video",
        allowed_formats: ["mp4", "webm"],
        convertible_formats: ["avi", "mov", "mpg", "wmv", "mkv", "flv", "ogv", "m4v"]
    },
    // ...
];

export const PresetsMap = new Map(
    PresetsList.map(preset => [preset.id, preset])
);

Testing Updates

File: tests/test_presets.py

Update to test against spec:

spec_path = os.path.join(os.path.dirname(__file__), "..", "spec", "constants-format_presets.json")
with open(spec_path) as f:
    spec = json.load(f)
    presetlookup = spec["constants"]

# Verify all presets generated correctly
# Verify array fields (allowed_formats, convertible_formats) handled properly

How to Run Tests

pytest tests/test_presets.py -v
pytest tests/ -v

Acceptance Criteria

  • spec/constants-format_presets.json created with all preset data
  • Changed "kind" to "kind_id" in spec to match namedtuple field
  • make build successfully generates Python and JavaScript files
  • Generated le_utils/constants/format_presets.py has:
    • Preset namedtuple with 11 fields
    • Uppercase constants (VIDEO_HIGH_RES, etc.)
    • choices tuple
    • PRESETLIST with Preset namedtuples
    • Array fields (allowed_formats, convertible_formats) as lists
  • Generated js/FormatPresets.js has:
    • Default export with constants
    • PresetsList with full preset data
    • PresetsMap for lookups
    • Array fields properly exported
  • tests/test_presets.py updated to test against spec
  • All tests pass
  • resources/presetlookup.json deleted

Disclosure

🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions