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

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions