{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://videobook.club/schema/videobook-1.schema.json",
  "title": "videobook.json (videobook/1)",
  "description": "Describes a videobook: the work, rights, narration, and the start time of each book page in seconds within its chapter video.",
  "type": "object",
  "required": ["format", "title", "author", "language", "chapters"],
  "properties": {
    "format": { "const": "videobook/1" },
    "slug": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
    "status": { "enum": ["published", "in_progress", "draft"] },
    "title": { "type": "string", "minLength": 1, "maxLength": 200 },
    "subtitle": { "type": ["string", "null"], "maxLength": 200 },
    "author": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": { "type": "string", "minLength": 1, "maxLength": 120 },
        "sort": { "type": "string", "description": "Sort name, e.g. \"Kafka, Franz\"" },
        "born": { "type": ["integer", "null"], "description": "Year; negative for BCE" },
        "died": { "type": ["integer", "null"], "description": "Year; relevant to public-domain status (EU: life plus 70 years)" }
      }
    },
    "language": { "type": "string", "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$", "description": "Language of the videobook" },
    "original": {
      "type": ["object", "null"],
      "properties": {
        "year": { "type": ["integer", "null"] },
        "language": { "type": ["string", "null"] },
        "note": { "type": ["string", "null"] }
      }
    },
    "translation": {
      "type": ["object", "null"],
      "required": ["translator"],
      "properties": {
        "translator": { "type": "string" },
        "translator_died": { "type": ["integer", "null"] },
        "own": { "type": "boolean", "description": "true = your own translation, licensed through submission" },
        "license": { "type": "string" }
      }
    },
    "text_source": {
      "type": "object",
      "required": ["label"],
      "properties": { "label": { "type": "string" }, "url": { "type": "string" } }
    },
    "rights": { "type": "object", "properties": { "basis": { "type": "string" } } },
    "description": { "type": "string", "maxLength": 2400 },
    "narration": { "type": "object", "properties": { "voice": { "type": "string" }, "engine": { "type": "string" } } },
    "credits": { "type": "array", "items": { "type": "object", "required": ["role", "name"], "properties": { "role": { "type": "string" }, "name": { "type": "string" } } } },
    "cover": { "type": ["string", "null"], "description": "Portrait cover (2:3)" },
    "thumbnail": { "type": ["string", "null"], "description": "16:9 still image, also for YouTube" },
    "tags": { "type": "array", "items": { "type": "string" } },
    "chapters": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["n"],
        "allOf": [{
          "if": { "properties": { "status": { "const": "published" } }, "required": ["status"] },
          "then": { "required": ["video", "pages"], "properties": { "video": { "anyOf": [
            { "required": ["youtube"], "properties": { "youtube": { "type": "string" } } },
            { "required": ["file"], "properties": { "file": { "type": "string", "minLength": 1 } } }
          ] } } }
        }],
        "properties": {
          "n": { "type": "integer", "minimum": 1, "description": "Stable episode identity: unique, strictly increasing; gaps allowed. Never renumber after publication." },
          "status": { "enum": ["published", "in_progress", "draft"], "description": "Optional episode status. Legacy video-backed episodes default to published; otherwise in_progress." },
          "title": { "type": ["string", "null"] },
          "video": {
            "type": "object",
            "properties": {
              "youtube": { "type": ["string", "null"], "pattern": "^[A-Za-z0-9_-]{11}$" },
              "file": { "type": ["string", "null"] },
              "duration": { "type": "number", "exclusiveMinimum": 0, "description": "Seconds" }
            }
          },
          "pages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": ["n", "start"],
              "properties": {
                "n": { "type": "integer", "minimum": 1, "description": "Page number within this episode; may restart at 1 for each episode" },
                "start": { "type": "number", "minimum": 0, "description": "Time in seconds within the chapter video when the page begins" },
                "incipit": { "type": "string", "maxLength": 240, "description": "Opening words of the page" },
                "text": { "type": "string", "description": "Full page text (optional, for search and accessibility)" }
              }
            }
          },
          "credits_start": { "type": ["number", "null"], "description": "Time in seconds when the closing credits begin" }
        }
      }
    }
  }
}
