publ betaReference (MCP guide)
Open publ console

media_subtitle_apply

# media_subtitle_apply

Create or delete one VOD subtitle track for an owned OTT, serial or course video. This is a file workflow, separate from live subtitle packs.

## Inputs and actions

Provide channelCode, pAppCode (`C00002`, `C00006`, `C00007`), postId, action, expectedRevision from [media_subtitles_get](media_subtitles_get.md), requestId and optional dryRun (default false).

- `action:{kind:"create",assetId,language}`: read the same post with this trusted assetId first. Choose an exact available language key returned by the reader, for example `en` rather than an invented locale. The host-staged file must be nonempty UTF-8 SRT/VTT, at most 10 MiB, with recognizable forward cue intervals. This bounded parser is not a complete grammar or playback validation; it preserves file bytes.
- `action:{kind:"delete",subtitleId,confirmation:"DELETE_MEDIA_SUBTITLE"}`: read the post without an asset first. Use the exact observed subtitleId: 1–256 letters/digits/underscores/hyphens, starting with a letter or digit. Only DONE or ERROR tracks are deletable; processing/unknown tracks are rejected.

The channel profile must own the selected VOD post; sample videos are blocked. Creation allows at most ten tracks and one per source language. It derives the source name key and registers `isDefault:false`. Track replacement, language/default editing, automatic object cleanup and deleted-ID restoration are unsupported.

## Effects and observation

Dry run returns `MEDIA_SUBTITLE_PLAN` after fresh target/file validation. Creation uploads the staged file and then registers it with the VOD service. Delete removes the selected track. After a sent mutation, observation rereads the post and requires the same original post/vid identity; a changed target produces `MEDIA_SUBTITLE_TARGET_CHANGED` rather than adopting a replacement.

Creation reconciliation requires a new track ID with the exact issued object URL hash, language, derived name and default state while preserving projected siblings and the media post. Matching language alone is insufficient. A backend URL rewrite can leave an otherwise plausible result unconfirmed. Track conversion state/updatedAt may advance independently; those are not immutable sibling fields. Processing, ready and encoding-failed results are separate, and playback remains unverified. Deletion's matching current state does not prove who performed it.

## Recovery and navigation

Task type: `media-subtitle`. Phases are prepared, upload-sent, uploaded and mutation-sent (deletion skips upload). Status/resume observe only. Identical apply can continue acknowledged uploaded work to its next unsent registration; uncertain sent effects are never replayed. Preserve the requestId.

Prepared/upload-sent/uploaded recovery carries `targetObservation:{source:"saved-checkpoint",currentPostVerified:false}` and no current consoleUrl. The saved post identity is historical. Only a fresh verified read can return `source:"fresh-read",currentPostVerified:true` and a destination for that post's subtitle screen. Do not reconstruct a current URL from a checkpoint.

Trusted host ingress/service injection remains required. Registration in MCP does not prove a staged asset exists, an upload succeeded or the subtitles render correctly.

Read the [media workflow guide](../guides/media-workflows.md) for authorization, source-versus-runtime availability and common recovery rules.

## Input example

Illustrative input only. Replace the channel, installed app, identifiers and revision with values obtained for the selected target.

```json
{
  "channelCode": "selected-channel",
  "pAppCode": "C00002",
  "postId": 1,
  "action": {
    "kind": "create",
    "assetId": "example-asset-001",
    "language": "en"
  },
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "requestId": "example-request-001",
  "dryRun": true
}
```