{
  "swagger": "2.0",
  "info": {
    "title": "evey/v1/admin_media.proto",
    "version": "version not set"
  },
  "tags": [
    {
      "name": "AdminMediaService"
    }
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/api/v1/admin/collections/relink": {
      "post": {
        "summary": "Repairs saga membership for movies that belong to no collection: their\nsequels are already in the library, but membership is only written while\na title is enriched, so anything enriched before that path existed never\njoined its saga. Returns immediately; progress shows up as the\n\"collection_relink\" background task.",
        "operationId": "AdminMediaService_RelinkCollections",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RelinkCollectionsResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1RelinkCollectionsRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/audio-remux-library": {
      "post": {
        "summary": "RemuxAudioLibrary is RETIRED: it always fails with FAILED_PRECONDITION.\nSee RemuxAudioMedia - this is the bulk form that did the July damage.",
        "operationId": "AdminMediaService_RemuxAudioLibrary",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RemuxAudioLibraryResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1RemuxAudioLibraryRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/match-orphans": {
      "post": {
        "summary": "MatchOrphansInLibrary targets only items missing provider IDs (TMDB\nnever matched them at scan time) and queues an async re-enrichment.\nReturns immediately with the count of queued items; the enricher\npool drains them in the background under its existing concurrency\nand rate-limit caps. Idempotent - re-run to pick up the still-\norphaned remainder once any rate limits clear.",
        "operationId": "AdminMediaService_MatchOrphansInLibrary",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1MatchOrphansInLibraryResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1MatchOrphansInLibraryRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/refresh-metadata": {
      "post": {
        "operationId": "AdminMediaService_RefreshAllMetadata",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RefreshAllMetadataResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "libraryId",
            "description": "Optional: when set, only items of this library are refreshed.\nEmpty (default) refreshes every library.",
            "in": "query",
            "required": false,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/repairs": {
      "get": {
        "summary": "ListMediaRepairs answers \"what did the repairs touch\" across the whole\nlibrary: every journal entry in a time window, newest first, optionally\none batch kind. The 98 files rewritten on 2026-07-13 were only ever found\nby a hand-written query on file modification times; this is that\nquestion, asked of the journal, from the health page.",
        "operationId": "AdminMediaService_ListMediaRepairs",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ListMediaRepairsResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "sinceMs",
            "description": "since_ms / until_ms bound the window (unix ms); 0 = open on that side.",
            "in": "query",
            "required": false,
            "type": "string",
            "format": "int64"
          },
          {
            "name": "untilMs",
            "in": "query",
            "required": false,
            "type": "string",
            "format": "int64"
          },
          {
            "name": "job",
            "description": "job keeps only one batch kind (\"reindex\", \"rollback\", \"purge\",\n\"fix_container\", \"scan\"); empty = every entry, operator actions included.",
            "in": "query",
            "required": false,
            "type": "string"
          },
          {
            "name": "limit",
            "description": "Optional cap, default 100.",
            "in": "query",
            "required": false,
            "type": "integer",
            "format": "int32"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index": {
      "get": {
        "summary": "ScanSeekIndex returns the cached result of the last completed scan for the\nscope - instant, no disk I/O. scanned_at_ms is 0 when no scan has completed\nyet; kick one with StartSeekIndexScan and poll GetSeekIndexJob. Files\nwithout an index seek/resume slowly because the player has to scan\nlinearly; has_backup reports whether a prior reindex left a restorable\n.mkvgobak.",
        "operationId": "AdminMediaService_ScanSeekIndex",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ScanSeekIndexResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "libraryId",
            "description": "Optional: when set, only this library is scanned. Empty = every library.",
            "in": "query",
            "required": false,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/ack": {
      "post": {
        "summary": "AckSeekIndexMedia records (or withdraws) the operator's acknowledgement of\none file's verdict. Refused (FailedPrecondition) for a repairable file:\nwhat can be repaired is repaired, not waved through.",
        "operationId": "AdminMediaService_AckSeekIndexMedia",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1AckSeekIndexMediaResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1AckSeekIndexMediaRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/ack-group": {
      "post": {
        "summary": "AckSeekIndexGroup acknowledges every pending file of one verdict\n(\"informational\" - the only ackable one) in scope. Returns how many it\nmarked.",
        "operationId": "AdminMediaService_AckSeekIndexGroup",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1AckSeekIndexGroupResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1AckSeekIndexGroupRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/delete-backup": {
      "post": {
        "summary": "DeleteSeekIndexBackup removes one media's reindex backup (.mkvgobak),\nreclaiming the duplicated disk space once the repair is confirmed fine.\nSynchronous (an unlink) - no job involved.",
        "operationId": "AdminMediaService_DeleteSeekIndexBackup",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1DeleteSeekIndexBackupResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1DeleteSeekIndexBackupRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/job": {
      "get": {
        "summary": "GetSeekIndexJob reports the live progress of the running seek-index job\n(scan, reindex or rollback) plus a summary of the last finished one.\nPoll it after any Start*/Reindex*/Rollback* call.",
        "operationId": "AdminMediaService_GetSeekIndexJob",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1GetSeekIndexJobResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/job/cancel": {
      "post": {
        "summary": "CancelSeekIndexJob stops the running seek-index job at its next file\nboundary (a repair in flight finishes or rolls back; no file is left\nhalf-written) and returns at once. cancelled=false when nothing was\nrunning. Observe the end via GetSeekIndexJob (last.cancelled).",
        "operationId": "AdminMediaService_CancelSeekIndexJob",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1CancelSeekIndexJobResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1CancelSeekIndexJobRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/purge-backups": {
      "post": {
        "summary": "PurgeSeekIndexBackups kicks a background job that re-scans the scope then\ndeletes EVERY reindex backup in it. Returns immediately; progress via\nGetSeekIndexJob.",
        "operationId": "AdminMediaService_PurgeSeekIndexBackups",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1PurgeSeekIndexBackupsResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1PurgeSeekIndexBackupsRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/reindex-library": {
      "post": {
        "summary": "ReindexLibrarySeekIndex kicks a background job that re-scans the library\nthen rebuilds the seek index of every un-indexed file. Returns immediately;\nprogress (scanning → processing, done/total) via GetSeekIndexJob.",
        "operationId": "AdminMediaService_ReindexLibrarySeekIndex",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ReindexLibrarySeekIndexResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1ReindexLibrarySeekIndexRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/rollback-library": {
      "post": {
        "summary": "RollbackLibrarySeekIndex kicks a background job that re-scans the library\nthen restores every file that has a .mkvgobak backup. Returns immediately;\nprogress via GetSeekIndexJob.",
        "operationId": "AdminMediaService_RollbackLibrarySeekIndex",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RollbackLibrarySeekIndexResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1RollbackLibrarySeekIndexRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/seek-index/scan": {
      "post": {
        "summary": "StartSeekIndexScan kicks a background scan of the library (all libraries\nwhen library_id is empty) and returns immediately. Scanning walks every\nMKV/WebM on disk so it can take minutes on a large/network-mounted library\n- a synchronous call would time out behind a reverse proxy. Read-only.\nFails with RESOURCE_EXHAUSTED if a seek-index job is already running.",
        "operationId": "AdminMediaService_StartSeekIndexScan",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1StartSeekIndexScanResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/v1StartSeekIndexScanRequest"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{id}": {
      "delete": {
        "operationId": "AdminMediaService_DeleteMediaItem",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1DeleteMediaItemResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/audio-remux": {
      "post": {
        "summary": "RemuxAudioMedia is RETIRED: it always fails with FAILED_PRECONDITION.\nIt used to pull every audio track that started after the picture back\nonto it by its own declared delay, with no upper bound - which is exactly\nhow a deliberate dub alignment looks - and rewrote 98 files that way on\n2026-07-13 with no surviving backup. An audio start offset is a fact\nabout a file, measured and shown by the scan (audio_delay_sec,\naudio_tracks_lag_sec), never something Evey rewrites. The RPC stays so a\ndeployed client gets a translated refusal instead of UNIMPLEMENTED.",
        "operationId": "AdminMediaService_RemuxAudioMedia",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RemuxAudioMediaResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/audio-shift": {
      "post": {
        "summary": "ApplyAudioShift moves one audio track by an amount a PERSON judged on a\npreview clip. Nothing this server measures can say whether the sound\nfollows the picture - comparing a file's own tracks says only whether they\nagree with each other - so this is the one path where a human's ear is the\nevidence, and the journal records it as exactly that.",
        "operationId": "AdminMediaService_ApplyAudioShift",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ApplyAudioShiftResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/AdminMediaServiceApplyAudioShiftBody"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/fix-container": {
      "post": {
        "summary": "FixContainerMedia renames one .mkv whose bytes are really MP4/MOV to .mp4\nand repoints the catalog, in the background (outcome via GetSeekIndexJob).",
        "operationId": "AdminMediaService_FixContainerMedia",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1FixContainerMediaResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/reinterleave": {
      "post": {
        "summary": "ReinterleaveMedia re-muxes one file whose tracks are stored in blocks of\none track at a time (see SeekIndexItem.interleave_backstep_sec), in the\nbackground; the original is kept as a backup. Outcome via GetSeekIndexJob.",
        "operationId": "AdminMediaService_ReinterleaveMedia",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ReinterleaveMediaResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/repair-history": {
      "get": {
        "summary": "GetMediaRepairHistory returns what has been done to one file's bytes,\nnewest first: which operation, on what evidence, and whether it can still\nbe undone. Evey keeps no copy of a repaired file - the entry itself is the\nway back - so this is the only place that history exists.",
        "operationId": "AdminMediaService_GetMediaRepairHistory",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1GetMediaRepairHistoryResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "limit",
            "description": "Optional cap, default 20.",
            "in": "query",
            "required": false,
            "type": "integer",
            "format": "int32"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/seek-index/reindex": {
      "post": {
        "summary": "ReindexMediaSeekIndex rebuilds one media file's seek index in the\nbackground (a rewrite of a multi-GB file takes minutes) and returns\nimmediately with accepted=true. Outcome is observable via GetSeekIndexJob.\nThe rebuild is validated + cross-checked before an atomic swap and the\noriginal is kept as a .mkvgobak; on any failure the original is left\nuntouched. Fails with RESOURCE_EXHAUSTED if a job is already running.",
        "operationId": "AdminMediaService_ReindexMediaSeekIndex",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1ReindexMediaSeekIndexResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/AdminMediaServiceReindexMediaSeekIndexBody"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/seek-index/rollback": {
      "post": {
        "summary": "RollbackMediaSeekIndex restores one media file from its .mkvgobak backup\nin the background, undoing a prior reindex. Returns accepted=true\nimmediately; outcome via GetSeekIndexJob. Fails fast if the media has no\nfile, or with RESOURCE_EXHAUSTED if a job is already running.",
        "operationId": "AdminMediaService_RollbackMediaSeekIndex",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1RollbackMediaSeekIndexResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/media/{mediaId}/undo-repair": {
      "post": {
        "summary": "UndoMediaRepair replays the file's most recent reversible repair\nbackwards, from its journal entry. Refused when the last repair discarded\nbytes (nothing can bring those back) or was written by a newer version\nwhose payload this build cannot read safely.",
        "operationId": "AdminMediaService_UndoMediaRepair",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1UndoMediaRepairResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "parameters": [
          {
            "name": "mediaId",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/AdminMediaServiceUndoMediaRepairBody"
            }
          }
        ],
        "tags": [
          "AdminMediaService"
        ]
      }
    },
    "/api/v1/admin/trickplay/generate-all": {
      "post": {
        "operationId": "AdminMediaService_GenerateAllTrickplays",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/v1GenerateAllTrickplaysResponse"
            }
          },
          "default": {
            "description": "An unexpected error response.",
            "schema": {
              "$ref": "#/definitions/rpcStatus"
            }
          }
        },
        "tags": [
          "AdminMediaService"
        ]
      }
    }
  },
  "definitions": {
    "AdminMediaServiceApplyAudioShiftBody": {
      "type": "object",
      "properties": {
        "track": {
          "type": "string",
          "format": "uint64",
          "description": "track is the Matroska track number to move, as listed by the preview."
        },
        "shiftSec": {
          "type": "number",
          "format": "double",
          "description": "shift_sec is signed: positive delays the track against the picture."
        }
      }
    },
    "AdminMediaServiceReindexMediaSeekIndexBody": {
      "type": "object",
      "properties": {
        "mode": {
          "type": "string",
          "description": "Repair strategy: \"inplace\" (surgical patch, file-write only, no backup),\n\"auto\" (try the patch, fall back to the rewrite when the file layout\ndefeats it - backup kept on the fallback), or \"rewrite\" (full rebuild +\n.mkvgobak rollback backup). Empty = rewrite."
        },
        "deepVerify": {
          "type": "boolean",
          "description": "deep_verify makes mkvgo prove every rebuilt cue lands on a real video\nkeyframe before committing (a full read of the file), on top of the\nalways-on head-only check. A failure rolls the in-place patch back and\naborts the rewrite before its swap. Recommended with \"inplace\", which\nkeeps no backup."
        },
        "skipBackup": {
          "type": "boolean",
          "description": "skip_backup drops the .mkvgobak of a rewrite once the swap has succeeded,\nso the volume never accumulates one extra copy per repaired file - the\noption for repairing a nearly-full disk. The server compensates by\nforcing deep_verify on. No effect on \"inplace\" (never keeps a backup)."
        },
        "resync": {
          "type": "boolean",
          "description": "resync repairs a file the strict reindex REFUSED for a corrupted region\n(junk between clusters, a lying size): the tolerant rewrite skips the\ndamaged span and resynchronises, recovering everything before and after.\nIt ALWAYS rewrites and forces a backup - this is the one repair that loses\ndata (the skipped span, logged). For the per-file \"repair anyway\" action\non a file that failed a normal repair. Overrides mode/skip_backup."
        }
      }
    },
    "AdminMediaServiceUndoMediaRepairBody": {
      "type": "object"
    },
    "protobufAny": {
      "type": "object",
      "properties": {
        "@type": {
          "type": "string"
        }
      },
      "additionalProperties": {}
    },
    "rpcStatus": {
      "type": "object",
      "properties": {
        "code": {
          "type": "integer",
          "format": "int32"
        },
        "message": {
          "type": "string"
        },
        "details": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/protobufAny"
          }
        }
      }
    },
    "v1AckSeekIndexGroupRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string",
          "description": "Optional scope: empty = all libraries."
        },
        "verdict": {
          "type": "string",
          "description": "\"informational\" - the only verdict one can acknowledge. A repairable\ndefect is repaired, not waved through."
        }
      }
    },
    "v1AckSeekIndexGroupResponse": {
      "type": "object",
      "properties": {
        "acked": {
          "type": "integer",
          "format": "int32"
        }
      }
    },
    "v1AckSeekIndexMediaRequest": {
      "type": "object",
      "properties": {
        "mediaId": {
          "type": "string"
        },
        "acked": {
          "type": "boolean",
          "description": "acked=false withdraws a previous acknowledgement."
        }
      }
    },
    "v1AckSeekIndexMediaResponse": {
      "type": "object"
    },
    "v1ApplyAudioShiftResponse": {
      "type": "object"
    },
    "v1CancelSeekIndexJobRequest": {
      "type": "object"
    },
    "v1CancelSeekIndexJobResponse": {
      "type": "object",
      "properties": {
        "cancelled": {
          "type": "boolean",
          "title": "a job was running and has been asked to stop"
        }
      }
    },
    "v1DeleteMediaItemResponse": {
      "type": "object"
    },
    "v1DeleteSeekIndexBackupRequest": {
      "type": "object",
      "properties": {
        "mediaId": {
          "type": "string"
        }
      }
    },
    "v1DeleteSeekIndexBackupResponse": {
      "type": "object"
    },
    "v1FixContainerMediaResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        }
      }
    },
    "v1GenerateAllTrickplaysResponse": {
      "type": "object",
      "properties": {
        "generated": {
          "type": "integer",
          "format": "int32"
        }
      }
    },
    "v1GetMediaRepairHistoryResponse": {
      "type": "object",
      "properties": {
        "entries": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/v1MediaRepairEntry"
          }
        }
      }
    },
    "v1GetSeekIndexJobResponse": {
      "type": "object",
      "properties": {
        "running": {
          "type": "boolean"
        },
        "kind": {
          "type": "string",
          "title": "\"scan\" | \"reindex\" | \"rollback\""
        },
        "scope": {
          "type": "string",
          "title": "\"\" = all libraries, else the library id"
        },
        "phase": {
          "type": "string",
          "title": "\"scanning\" | \"processing\""
        },
        "done": {
          "type": "integer",
          "format": "int32"
        },
        "failed": {
          "type": "integer",
          "format": "int32"
        },
        "skipped": {
          "type": "integer",
          "format": "int32"
        },
        "total": {
          "type": "integer",
          "format": "int32"
        },
        "currentPath": {
          "type": "string"
        },
        "startedAtMs": {
          "type": "string",
          "format": "int64"
        },
        "last": {
          "$ref": "#/definitions/v1SeekIndexJobSummary",
          "title": "unset until a first job finishes"
        }
      }
    },
    "v1ListMediaRepairsResponse": {
      "type": "object",
      "properties": {
        "entries": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/v1MediaRepairEntry"
          }
        }
      }
    },
    "v1MatchOrphansInLibraryRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string",
          "description": "Optional: when set, only orphans of this library are queued.\nEmpty (default) targets orphans in every library."
        }
      }
    },
    "v1MatchOrphansInLibraryResponse": {
      "type": "object",
      "properties": {
        "queued": {
          "type": "integer",
          "format": "int32",
          "description": "Number of orphaned items queued for enrichment. The job runs async;\nthe actual matched count is observable by re-listing items with\nempty providerIds."
        }
      }
    },
    "v1MediaRepairEntry": {
      "type": "object",
      "properties": {
        "atMs": {
          "type": "string",
          "format": "int64"
        },
        "op": {
          "type": "string",
          "description": "op: \"reindex\", \"resync_damaged\", \"container_fix\", \"audio_retime\",\n\"rollback\"."
        },
        "outcome": {
          "type": "string",
          "description": "outcome: \"applied\", \"skipped\", \"rolled_back\", \"failed\"."
        },
        "error": {
          "type": "string"
        },
        "job": {
          "type": "string",
          "description": "job names the batch this belonged to, so a whole run reads as one thing."
        },
        "evidenceMethod": {
          "type": "string",
          "description": "evidence_method says what justified acting: \"track-coherence\" is a\nmeasurement, \"declared-defect\" is the file's own claim taken at face\nvalue - the weak case that caused the 2026-07-13 incident."
        },
        "evidenceLagSec": {
          "type": "number",
          "format": "double"
        },
        "evidenceConfidence": {
          "type": "number",
          "format": "double"
        },
        "shiftSec": {
          "type": "object",
          "additionalProperties": {
            "type": "number",
            "format": "double"
          },
          "description": "shift_sec is what was applied per audio track, signed. Its opposite is the\nundo, which is why no copy of the original is kept."
        },
        "audibleBefore": {
          "type": "integer",
          "format": "int32"
        },
        "audibleAfter": {
          "type": "integer",
          "format": "int32"
        },
        "sizeBefore": {
          "type": "string",
          "format": "int64"
        },
        "sizeAfter": {
          "type": "string",
          "format": "int64"
        },
        "reversible": {
          "type": "boolean",
          "description": "reversible: this entry alone carries enough to put the file back."
        },
        "mediaId": {
          "type": "string",
          "description": "media_id and path name the file, for the cross-library listing where an\nentry is not read under one media's row. path is the file as it was\nrecorded, which survives a media being deleted and re-added."
        },
        "path": {
          "type": "string"
        }
      },
      "description": "MediaRepairEntry is one rewrite of one file, as recorded when it happened."
    },
    "v1PurgeSeekIndexBackupsRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string",
          "description": "Optional: when set, only this library's backups are purged. Empty = all."
        }
      }
    },
    "v1PurgeSeekIndexBackupsResponse": {
      "type": "object"
    },
    "v1RefreshAllMetadataResponse": {
      "type": "object",
      "properties": {
        "refreshed": {
          "type": "integer",
          "format": "int32"
        }
      }
    },
    "v1ReindexLibrarySeekIndexRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string"
        },
        "mode": {
          "type": "string",
          "description": "Repair strategy - see ReindexMediaSeekIndexRequest.mode."
        },
        "deepVerify": {
          "type": "boolean",
          "description": "See ReindexMediaSeekIndexRequest.deep_verify. Costs a full read PER FILE,\nso a whole-library sweep with it on is a long job."
        },
        "skipBackup": {
          "type": "boolean",
          "description": "See ReindexMediaSeekIndexRequest.skip_backup. For a whole-library sweep\nthis is the difference between one temporary copy at a time and one\nPERSISTENT copy per repaired file on the volume."
        },
        "backupWindow": {
          "type": "integer",
          "format": "int32",
          "description": "backup_window (with backups on) keeps only the N most recent repairs'\n.mkvgobak while the sweep runs: as the job advances past the window, the\noldest backup is purged automatically, and the job's end drains the\nremainder - the volume never holds more than N extra copies and finishes\nwith none. 0 keeps every backup. Ignored with skip_backup or \"inplace\"."
        },
        "includeAudio": {
          "type": "boolean",
          "description": "include_audio also repairs the audio start-delay of every flagged file in\nthe same job (needs the ffmpeg remuxer wired; ignored otherwise). A file\nthat also lacks an index is only reindexed this run - its audio waits for\nthe next, so its true original backup is never clobbered.\ninclude_audio is IGNORED since the triage rework: Repair all repairs every\nrepairable defect, the audio start delay included. Kept for wire compat."
        },
        "resyncDamaged": {
          "type": "boolean",
          "description": "resync_damaged also rewrites, in resync mode, the files whose index is\nfine but which carry mid-file corruption: the ONLY way they become\nplayable, at the price of the damaged bytes. Explicit consent; such\nrepairs always keep their backup."
        }
      }
    },
    "v1ReindexLibrarySeekIndexResponse": {
      "type": "object",
      "properties": {
        "queued": {
          "type": "integer",
          "format": "int32",
          "description": "Deprecated: the candidate count is only known once the background scan\nphase completes - always -1 now. Poll GetSeekIndexJob for done/total."
        }
      }
    },
    "v1ReindexMediaSeekIndexResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean",
          "description": "The repair now runs in the background; per-file details (backup path,\nwarnings, sizes) land in the server log and the job summary."
        },
        "skipped": {
          "type": "boolean",
          "description": "Deprecated pre-async fields, always zero now that the repair is a job."
        },
        "reindexed": {
          "type": "boolean"
        },
        "backupPath": {
          "type": "string"
        },
        "warnings": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "srcBytes": {
          "type": "string",
          "format": "int64"
        },
        "dstBytes": {
          "type": "string",
          "format": "int64"
        }
      }
    },
    "v1ReinterleaveMediaResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        }
      }
    },
    "v1RelinkCollectionsRequest": {
      "type": "object",
      "properties": {
        "limit": {
          "type": "integer",
          "format": "int32",
          "description": "Upper bound on titles examined in this sweep. 0 means the server default."
        }
      }
    },
    "v1RelinkCollectionsResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        }
      }
    },
    "v1RemuxAudioLibraryRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string",
          "description": "Optional: when set, only this library is repaired. Empty = every library."
        }
      }
    },
    "v1RemuxAudioLibraryResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        }
      }
    },
    "v1RemuxAudioMediaResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        }
      }
    },
    "v1RollbackLibrarySeekIndexRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string"
        }
      }
    },
    "v1RollbackLibrarySeekIndexResponse": {
      "type": "object",
      "properties": {
        "queued": {
          "type": "integer",
          "format": "int32",
          "description": "Deprecated: always -1 - poll GetSeekIndexJob for done/total."
        }
      }
    },
    "v1RollbackMediaSeekIndexResponse": {
      "type": "object",
      "properties": {
        "accepted": {
          "type": "boolean"
        },
        "restored": {
          "type": "boolean",
          "description": "Deprecated: the restore now runs in the background - poll GetSeekIndexJob."
        }
      }
    },
    "v1ScanSeekIndexResponse": {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/v1SeekIndexItem"
          }
        },
        "total": {
          "type": "integer",
          "format": "int32",
          "title": "EBML files inspected"
        },
        "missingIndex": {
          "type": "integer",
          "format": "int32",
          "title": "count with has_index == false"
        },
        "withBackup": {
          "type": "integer",
          "format": "int32",
          "title": "count with has_backup == true"
        },
        "scannedAtMs": {
          "type": "string",
          "format": "int64",
          "description": "When the last completed scan for this scope finished (unix ms). 0 = no\nscan has completed yet - items/counters are empty, not \"all clean\"."
        },
        "audioDelayed": {
          "type": "integer",
          "format": "int32",
          "description": "Count with audio_delay_sec above the defect floor (~0.1s)."
        },
        "inplaceAvailable": {
          "type": "boolean",
          "description": "Whether the surgical in-place repair mode is available in this build."
        },
        "truncated": {
          "type": "integer",
          "format": "int32",
          "description": "Count of truncated (incomplete-download) files - these need re-acquiring,\nnot repairing. Count of files with resync-repairable mid-file damage."
        },
        "damaged": {
          "type": "integer",
          "format": "int32"
        },
        "wrongContainer": {
          "type": "integer",
          "format": "int32",
          "description": "Count of .mkv files that are really MP4/MOV - rename/remux, not reindex."
        },
        "trailingJunk": {
          "type": "integer",
          "format": "int32",
          "description": "Count carrying surplus bytes past the Segment end (harmless, a rewrite drops them)."
        },
        "audioShort": {
          "type": "integer",
          "format": "int32",
          "description": "Count whose audio ends before the declared duration (re-acquire the\nsource; playback fills the missing tail with silence meanwhile)."
        },
        "seekCoarse": {
          "type": "integer",
          "format": "int32",
          "description": "Count rebuilt and still too coarse to seek into (see seek_coarse). These\nare NOT part of missing_index: no repair can improve them."
        },
        "volumes": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/v1SeekIndexVolume"
          },
          "description": "Live per-filesystem numbers for the un-indexed candidates, so the client\ncan advise for/against backups (their accumulation persists the SUM of\nthe candidates) and flag files whose rewrite temp copy will not fit."
        },
        "repairable": {
          "type": "integer",
          "format": "int32",
          "description": "The triage the page is built on. repairable is exactly what Repair all\nacts on; informational counts only the remarks still awaiting the\noperator's eyes (acknowledged ones are in acked)."
        },
        "manual": {
          "type": "integer",
          "format": "int32",
          "description": "manual is always 0 since the \"your call\" verdict was removed. Kept only\nso an older client keeps parsing; do not read it."
        },
        "informational": {
          "type": "integer",
          "format": "int32"
        },
        "acked": {
          "type": "integer",
          "format": "int32"
        },
        "containerFixAvailable": {
          "type": "boolean",
          "description": "container_fix_available: the server can rename a mislabelled .mkv to .mp4\nand repoint the catalog (part of Repair all when true)."
        },
        "brokenInterleave": {
          "type": "integer",
          "format": "int32",
          "description": "Count stored as blocks of one track at a time (see interleave_backstep_sec)."
        },
        "reinterleaveAvailable": {
          "type": "boolean",
          "description": "reinterleave_available: the server can re-mux a file stored in blocks\n(part of Repair all when true)."
        },
        "audioPictureOff": {
          "type": "integer",
          "format": "int32",
          "description": "Count whose sound is measured off the picture (see audio_picture_lag_sec)."
        },
        "unreadable": {
          "type": "integer",
          "format": "int32",
          "description": "Count the scan could not diagnose at all (see unreadable)."
        }
      }
    },
    "v1SeekIndexItem": {
      "type": "object",
      "properties": {
        "mediaId": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "path": {
          "type": "string"
        },
        "hasIndex": {
          "type": "boolean",
          "title": "false = muxed without Cues → slow seek/resume"
        },
        "hasBackup": {
          "type": "boolean",
          "title": "a .mkvgobak exists → a prior reindex is rollback-able"
        },
        "sizeBytes": {
          "type": "string",
          "format": "int64"
        },
        "container": {
          "type": "string",
          "title": "e.g. \"mkv\", \"webm\""
        },
        "audioDelaySec": {
          "type": "number",
          "format": "double",
          "description": "Audio starts this far after the video (seconds, max across audio tracks).\nAbove ~0.1s = repack mux defect playing as a constant A/V desync on\ncopied audio (transcoded audio auto-cancels it at playback). 0 = aligned."
        },
        "truncated": {
          "type": "boolean",
          "description": "truncated = the file is an incomplete download whose damage runs to EOF:\nno tool recovers the missing tail, a resync only yields a playable prefix.\nThe remedy is re-acquiring the source, NOT a repair. Only diagnosed scans\nset this (older scans leave it false). Repair the source, not the file."
        },
        "damagedBytes": {
          "type": "string",
          "format": "int64",
          "description": "damaged_bytes \u003e 0 = mid-file corruption a resync repair skips past, losing\nthis many bytes (the file is otherwise repairable in full). 0 = clean."
        },
        "wrongContainer": {
          "type": "boolean",
          "description": "wrong_container = a .mkv whose bytes are really MP4/MOV. It has no Matroska\nseek index to build; the remedy is rename/remux, not reindex. Kept out of\nthe missing-index bucket so \"Repair all\" never wastes a doomed reindex on it."
        },
        "trailingJunk": {
          "type": "boolean",
          "description": "trailing_junk = surplus bytes past the declared Segment end; the media is\ncomplete (leftover junk, or a crashed in-place-repair journal). Harmless -\nany reindex rewrite drops them - so it is NOT damaged_bytes and needs no\nre-download. Surfaced on its own so a whole file is never flagged as corrupt\njust for carrying tail junk."
        },
        "audioShortfallSec": {
          "type": "number",
          "format": "double",
          "description": "audio_shortfall_sec \u003e 0 = an audio track stops this many seconds BEFORE\nthe VIDEO does (mux defect: dead audio over the credits). Measured against\nthe picture's real end, not the container's declared duration - that\nduration is only a muxer's claim, and an over-stated one made aligned\nfiles look like audio defects. The file is structurally healthy, so no\nindex/damage check sees it, but players used to die at the wall (playback\nnow fills the tail with silence). NOT repairable - the audio's tail is\nabsent from the file; the remedy is re-acquiring the source. Only gaps\npast the ~10s defect floor are reported (ordinary muxes leave a couple of\nseconds of slack)."
        },
        "missingTailBytes": {
          "type": "string",
          "format": "int64",
          "description": "missing_tail_bytes quantifies truncated: how many declared bytes are\nabsent from the end of the file. The magnitude routes the decision - a\n9 KB stub is invisible in playback, 41 MB is a missing final scene."
        },
        "seekCoarse": {
          "type": "boolean",
          "description": "seek_coarse = the index was rebuilt and is STILL too coarse to seek into:\nthe encode's own keyframes sit further apart than a seek can tolerate, and\nno index can be denser than the keyframes it names. Reported apart from\nmissing_index precisely so it stays OUT of the repair queue - offering a\nrepair that cannot change anything is what pinned 134 prod files. The\nremedy is a re-encode or a better source, and it is offered as a remark\n(informational verdict), never as an instruction."
        },
        "verdict": {
          "type": "string",
          "description": "verdict is the ONE answer per file, and there are only two non-healthy\nones: \"repairable\" (Evey repairs it here - Repair all acts on exactly\nthese) and \"informational\" (something we measured and mention, possibly\nwrong, never a chore). Informational files can be acknowledged.\nThe former \"manual\" verdict is GONE: a detector that cannot repair\nsomething has not earned the right to hand the operator an instruction."
        },
        "ackedAtMs": {
          "type": "string",
          "format": "int64",
          "description": "acked_at_ms \u003e 0: the operator acknowledged this file's verdict (it stays\nout of the attention list until the file's bytes change)."
        },
        "seekCoarseDetail": {
          "type": "string",
          "description": "seek_coarse_detail is the diagnoser's own account of the remaining hole\n(\"the video cues leave a 44s hole - ...\") - often the final credits alone."
        },
        "audioTracksLagSec": {
          "type": "number",
          "format": "double",
          "description": "audio_tracks_lag_sec is how far this file's OWN audio tracks disagree once\ntheir declared offsets are applied, in seconds, and it is what decides\nwhether audio_delay_sec matters. Two tracks of one film share their music\nand effects bed: they line up when the offsets are RIGHT (a dub aligned to\nits cut - nothing to do) and disagree when a track sits in the wrong place.\n0 with audio_tracks_checked true means PROVEN coherent."
        },
        "audioTracksChecked": {
          "type": "boolean",
          "description": "audio_tracks_checked says the comparison actually ran, so a 0 lag reads as\n\"proven coherent\" rather than \"never looked\". It runs on every file with\ntwo or more audio tracks; false when the file has one audio track, or the\ntwo mixes shared no legible signal."
        },
        "audioTrackZeroed": {
          "type": "boolean",
          "description": "audio_track_zeroed is the fingerprint of a track that was retimed onto the\npicture: one audio track starts at exactly 0 while a sibling keeps its\nsmall priming offset. A muxer treats sibling tracks alike; only a repair\nthat pulled ONE track back by its own declared delay leaves this shape\n(the 2026-07-13 batch). It names the touched file; the amount removed is\ngone from the file and nothing here can restore it. Informational."
        },
        "resyncRequired": {
          "type": "boolean",
          "description": "resync_required = a repair ALREADY ran on this file and the strict rewrite\nrefused it: the source is structurally corrupt somewhere the scan never\nlooked. The scan is head-mostly on purpose (7 000 files must not each cost\na full salvage walk on a NAS), so this class of damage is only ever\ndiscovered BY a repair - and before this field the answer was thrown away\nwith the error, leaving the operator a raw Go string and no button.\nIt means \"only a resync can repair this\", exactly like damaged_bytes \u003e 0,\nwith one difference that must not be blurred: the amount a resync would\ndrop is UNKNOWN here, so it is never folded into the damaged byte total."
        },
        "interleaveBackstepSec": {
          "type": "number",
          "format": "double",
          "description": "interleave_backstep_sec \u003e 0 = the tracks sit in separate blocks of minutes\n(video alone, then audio alone): the largest step BACKWARDS of the cluster\ntimestamp between two sampled positions. No sequential player gets past\nthe first block; only a re-mux by timestamp fixes it. Informational."
        },
        "audioPictureLagSec": {
          "type": "number",
          "format": "double",
          "description": "audio_picture_lag_sec is the measured offset of the worst audio track\nagainst the PICTURE, read off the file's own subtitles (positive = the\nsound must move later); audio_picture_track is that track's ordinal and\naudio_picture_checked says the measure ran and concluded. Informational:\nthe operator applies the value with the preview."
        },
        "audioPictureTrack": {
          "type": "integer",
          "format": "int32"
        },
        "audioPictureChecked": {
          "type": "boolean"
        },
        "unreadable": {
          "type": "boolean",
          "description": "unreadable = the scan could not diagnose this file at all: missing or\nunreadable on disk, or a container the parser refused. Every other flag\nis unset and has_index is true so it never enters a repair queue;\nunreadable_detail is the error in the prober's words. Informational: the\noperator re-acquires the file or fixes the mount. Such a file used to be\ndropped from the scan with a log line - the sickest files of a library\nwere the ones the health page never showed."
        },
        "unreadableDetail": {
          "type": "string"
        }
      },
      "description": "SeekIndexItem is the per-file diagnosis surfaced by ScanSeekIndex."
    },
    "v1SeekIndexJobFailure": {
      "type": "object",
      "properties": {
        "path": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "readOnly": {
          "type": "boolean",
          "description": "The failure came from a read-only mount (:ro media volume) - the user\nmust remount read-write before repairing."
        }
      },
      "description": "SeekIndexJobFailure records one file a job could not process."
    },
    "v1SeekIndexJobSummary": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "title": "\"scan\" | \"reindex\" | \"rollback\""
        },
        "scope": {
          "type": "string",
          "title": "\"\" = all libraries, else the library id"
        },
        "done": {
          "type": "integer",
          "format": "int32"
        },
        "failed": {
          "type": "integer",
          "format": "int32"
        },
        "skipped": {
          "type": "integer",
          "format": "int32",
          "title": "files that already had an index (nothing to do)"
        },
        "total": {
          "type": "integer",
          "format": "int32"
        },
        "finishedAtMs": {
          "type": "string",
          "format": "int64"
        },
        "error": {
          "type": "string",
          "title": "job-level error (e.g. the scan itself failed)"
        },
        "readOnly": {
          "type": "boolean",
          "title": "at least one failure came from a read-only mount"
        },
        "failures": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/v1SeekIndexJobFailure"
          },
          "title": "capped at 50"
        },
        "cancelled": {
          "type": "boolean",
          "description": "cancelled: the job stopped at a file boundary on request (the cancel RPC\nor the server going down); done \u003c total is not a failure here."
        }
      },
      "description": "SeekIndexJobSummary describes the last finished job (since server boot)."
    },
    "v1SeekIndexVolume": {
      "type": "object",
      "properties": {
        "root": {
          "type": "string",
          "title": "shortest observed directory (label)"
        },
        "freeBytes": {
          "type": "string",
          "format": "int64",
          "title": "available right now"
        },
        "missingBytes": {
          "type": "string",
          "format": "int64",
          "title": "sum of un-indexed candidate sizes"
        },
        "largestMissingBytes": {
          "type": "string",
          "format": "int64",
          "title": "floor for one rewrite temp copy"
        },
        "missingFiles": {
          "type": "integer",
          "format": "int32"
        }
      },
      "description": "SeekIndexVolume aggregates the repair candidates of one filesystem."
    },
    "v1StartSeekIndexScanRequest": {
      "type": "object",
      "properties": {
        "libraryId": {
          "type": "string",
          "description": "Optional: when set, only this library is scanned. Empty = every library."
        },
        "pace": {
          "type": "string",
          "description": "How much of the media disks the scan may take:\n  \"gentle\" (default, and what anything unknown means) - one file at a\n    time, then a stand-aside as long as the file just took to read. The\n    disks stay free about half the time, so playback survives the scan.\n  \"fast\" - several files at once, no pause. For a library spread over\n    several disks, where one file at a time leaves the other spindles\n    idle; expect the disks to be busy for the whole run."
        }
      }
    },
    "v1StartSeekIndexScanResponse": {
      "type": "object"
    },
    "v1UndoMediaRepairResponse": {
      "type": "object",
      "properties": {
        "shiftSec": {
          "type": "object",
          "additionalProperties": {
            "type": "number",
            "format": "double"
          },
          "description": "shift_sec is what was applied to undo, per track."
        }
      }
    }
  }
}
