{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Franchise file",
  "description": "One franchise.yaml: the films and series of one story in the order the story plays, with an optional calendar, eras, and universes. README.md in this repository defines the format.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "name",
    "order"
  ],
  "dependentRequired": {
    "eras": [
      "calendar"
    ]
  },
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "description": "The name to show."
    },
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri"
      },
      "description": "Where the author read the order and the times. The next author reads these first."
    },
    "art": {
      "$ref": "#/$defs/art"
    },
    "calendar": {
      "$ref": "#/$defs/calendar"
    },
    "universe": {
      "type": "string",
      "minLength": 1,
      "description": "The universe the story happens in. An entry that names no universes of its own is in this one."
    },
    "eras": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/era"
      },
      "description": "Named stretches of the timeline. Needs a calendar. Spans may overlap."
    },
    "order": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/entry"
      },
      "description": "The story order, first to last. A depth-first walk of this list is the order."
    }
  },
  "$defs": {
    "id": {
      "type": "string",
      "pattern": "^[a-z0-9]+:[A-Za-z0-9_-]+$",
      "description": "A provider id as scheme:id. Films use tmdb and series use tvdb."
    },
    "art_url": {
      "type": "string",
      "format": "uri",
      "pattern": "^https://",
      "description": "A link to a public image a reader may fetch."
    },
    "art": {
      "type": "object",
      "additionalProperties": false,
      "description": "Art for the whole franchise. The repository holds links and never image bytes. A link points at a public image a reader may fetch. The first film's collection art on TMDB is the usual source. The five names are Kodi's names for the same art kinds, so a reader that knows Kodi's names knows these.",
      "properties": {
        "poster": {
          "$ref": "#/$defs/art_url",
          "description": "A 2:3 portrait image."
        },
        "fanart": {
          "$ref": "#/$defs/art_url",
          "description": "A 16:9 background image with no title on it."
        },
        "landscape": {
          "$ref": "#/$defs/art_url",
          "description": "A 16:9 image that carries the title or the logo."
        },
        "logo": {
          "$ref": "#/$defs/art_url",
          "description": "A transparent PNG of the franchise's wordmark."
        },
        "banner": {
          "$ref": "#/$defs/art_url",
          "description": "A wide thin strip, about 5:1."
        }
      }
    },
    "calendar": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "unit"
      ],
      "properties": {
        "unit": {
          "type": "string",
          "enum": [
            "years",
            "days"
          ]
        },
        "zero": {
          "type": "string",
          "minLength": 1,
          "description": "The event the times count from. Without it, the times are plain calendar years."
        },
        "before": {
          "type": "string",
          "minLength": 1,
          "description": "The label after a negative time, such as BBY."
        },
        "after": {
          "type": "string",
          "minLength": 1,
          "description": "The label after a positive time, such as ABY."
        }
      }
    },
    "time": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "from",
        "to"
      ],
      "properties": {
        "from": {
          "type": "number"
        },
        "to": {
          "type": "number"
        }
      },
      "description": "A span in the calendar's unit. from and to are equal for a story that stays in one year. Needs a calendar."
    },
    "era": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name",
        "from",
        "to"
      ],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1
        },
        "from": {
          "type": "number"
        },
        "to": {
          "type": "number"
        }
      }
    },
    "common": {
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "description": "The name to show when no collection holds the member. The title carries no year. released carries the date."
        },
        "released": {
          "type": "string",
          "pattern": "^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$",
          "description": "The real-world date as an ISO 8601 string, as much of it as is known: 1999, 1999-05, or 1999-05-19. On a film it is the first public release. On a series it is the day the first episode aired. It is never the story's own calendar, which time carries."
        },
        "time": {
          "$ref": "#/$defs/time"
        },
        "universes": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "minLength": 1
          },
          "description": "The universes this entry belongs to. The rule is about story, not about place: an entry names every universe whose story it continues or joins, even when the story never goes there. An entry with no universes belongs to the franchise's own universe. An entry that names one other universe belongs to that one. An entry that names several belongs to all of them at once."
        },
        "note": {
          "type": "string",
          "description": "Free text for the next author, never shown to a viewer."
        }
      }
    },
    "movie": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "movie"
      ],
      "properties": {
        "movie": {
          "$ref": "#/$defs/id"
        },
        "title": {
          "$ref": "#/$defs/common/properties/title"
        },
        "released": {
          "$ref": "#/$defs/common/properties/released"
        },
        "time": {
          "$ref": "#/$defs/time"
        },
        "universes": {
          "$ref": "#/$defs/common/properties/universes"
        },
        "note": {
          "$ref": "#/$defs/common/properties/note"
        }
      }
    },
    "series": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "series"
      ],
      "properties": {
        "series": {
          "$ref": "#/$defs/id"
        },
        "seasons": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/season"
          },
          "description": "The seasons that play here, in order. Without it, the entry means the whole show."
        },
        "title": {
          "$ref": "#/$defs/common/properties/title"
        },
        "released": {
          "$ref": "#/$defs/common/properties/released"
        },
        "time": {
          "$ref": "#/$defs/time"
        },
        "universes": {
          "$ref": "#/$defs/common/properties/universes"
        },
        "note": {
          "$ref": "#/$defs/common/properties/note"
        }
      }
    },
    "season": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "season"
      ],
      "properties": {
        "season": {
          "type": "integer",
          "minimum": 0,
          "description": "The season number. Specials are season 0."
        },
        "episodes": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "pattern": "^S[0-9]{2,}E[0-9]{2,}(-S[0-9]{2,}E[0-9]{2,})?$"
          },
          "description": "Episode codes or ranges, in the order they play. Without it, the season plays whole in aired order."
        },
        "time": {
          "$ref": "#/$defs/time"
        },
        "note": {
          "$ref": "#/$defs/common/properties/note"
        }
      }
    },
    "entry": {
      "oneOf": [
        {
          "$ref": "#/$defs/movie"
        },
        {
          "$ref": "#/$defs/series"
        }
      ]
    }
  }
}
