199 lines
10 KiB
Markdown
199 lines
10 KiB
Markdown
# Metadata Editor
|
|
|
|
This file defines the requirements for a database-free interactive metadata
|
|
editor command derived from the current file-inspection UI.
|
|
|
|
Feasibility from the current codebase: yes, with a moderate refactor.
|
|
|
|
The strongest reusable pieces already exist:
|
|
|
|
- `ffprobe`-backed media probing through `FileProperties` and `MediaDescriptor`
|
|
- descriptor-level metadata and disposition mutation through `MediaDescriptor`
|
|
and `TrackDescriptor`
|
|
- diff and ffmpeg token generation through `MediaDescriptorChangeSet`
|
|
- stream-copy remux execution through `FfxController` with `VideoEncoder.COPY`
|
|
- reusable tag and track edit dialogs in the Textual UI
|
|
|
|
The main missing pieces are:
|
|
|
|
- a CLI bootstrap path that does not initialize SQLite
|
|
- a probe-only path that does not instantiate database-backed controllers
|
|
- a clean separation between original file state and editable draft state
|
|
- a safe temporary-output and replace workflow for writing changes back to the
|
|
same file path
|
|
|
|
## Scope
|
|
|
|
- One new command: `ffx edit <file>`
|
|
- One-file interactive editing through a Textual screen derived from
|
|
`MediaDetailsScreen`
|
|
- Editing container-level metadata and per-stream metadata already visible in
|
|
the application
|
|
- Editing stream dispositions that are represented as metadata-like output
|
|
state, especially `default` and `forced`
|
|
- Writing the result back to the original file path through a temporary output
|
|
file and replace step
|
|
|
|
## Out Of Scope
|
|
|
|
- SQLite reads, writes, migrations, or pattern matching
|
|
- TMDB lookups, show selection, pattern selection, or shifted-season logic
|
|
- Batch editing multiple files in one command invocation
|
|
- Video or audio transcoding
|
|
- Container changes, filename changes, or rename workflows
|
|
- Stream add, stream delete, stream reorder, or stream substitution from
|
|
external files in the first release
|
|
- Editing technical stream identity such as codec, stream type, source index,
|
|
or audio layout in the first release
|
|
- Chapter editing
|
|
|
|
## Terms
|
|
|
|
- `baseline descriptor`: immutable in-memory representation of the file as last
|
|
probed from disk
|
|
- `draft descriptor`: mutable in-memory representation of the desired output
|
|
state
|
|
- `edit mode`: the database-free TUI mode used by `ffx edit`
|
|
- `planned changes`: user-visible summary of the differences between baseline
|
|
and draft plus any configured cleanup actions
|
|
- `temporary output file`: the write target used before replacing the original
|
|
file path
|
|
|
|
## Rules
|
|
|
|
- `METADATA_EDITOR-0001`: The system shall provide a command `ffx edit <file>`
|
|
that requires exactly one existing media file path and opens an interactive
|
|
Textual editor for that file.
|
|
- `METADATA_EDITOR-0002`: `ffx edit` shall not initialize SQLite, shall not
|
|
open the configured database file, shall not prompt for database migration,
|
|
and shall not instantiate any controller that depends on `context['database']`.
|
|
- `METADATA_EDITOR-0003`: `ffx edit` may still read configuration and logging
|
|
settings from `~/.local/etc/ffx.json`, but any global database option shall
|
|
have no effect on this command's behavior.
|
|
- `METADATA_EDITOR-0004`: Edit mode shall be derived from the current
|
|
`MediaDetailsScreen` behavior and layout where practical, but all DB-only UI
|
|
elements and actions such as show selection, pattern input, and pattern CRUD
|
|
actions shall be hidden, disabled, or replaced.
|
|
- `METADATA_EDITOR-0005`: Edit mode shall keep the baseline descriptor and the
|
|
draft descriptor as separate objects. Editing actions shall mutate only the
|
|
draft descriptor until the operator explicitly applies changes.
|
|
- `METADATA_EDITOR-0006`: The application shall keep raw metadata values
|
|
separate from rendered labels. Rich or Textual markup may be used for
|
|
presentation, but it shall never be stored in descriptor state, reused as
|
|
source data, or written into the media file.
|
|
- `METADATA_EDITOR-0007`: The planned-changes view shall compare the baseline
|
|
descriptor with the draft descriptor using `MediaDescriptorChangeSet` or an
|
|
equivalent descriptor-diff mechanism. It shall no longer mean `file -> db`.
|
|
- `METADATA_EDITOR-0008`: The editor shall support container-tag add, edit, and
|
|
delete operations on the draft descriptor.
|
|
- `METADATA_EDITOR-0009`: The editor shall support per-stream metadata edit
|
|
operations on the draft descriptor, including at least language, title, and
|
|
arbitrary stream tag key-value pairs.
|
|
- `METADATA_EDITOR-0010`: The editor shall support setting and clearing
|
|
`default` and `forced` dispositions in the draft descriptor, while enforcing
|
|
that there is at most one `default` and at most one `forced` stream per track
|
|
type.
|
|
- `METADATA_EDITOR-0011`: The first released editor scope shall treat technical
|
|
stream structure as immutable. A user shall not be able to change stream
|
|
count, output order, codec, track type, audio layout, or source-index
|
|
mapping through `ffx edit`.
|
|
- `METADATA_EDITOR-0012`: The track-edit UI used in edit mode shall therefore
|
|
expose only metadata fields and supported disposition fields. Structural
|
|
fields that are editable in pattern-authoring workflows shall be read-only or
|
|
absent in edit mode.
|
|
- `METADATA_EDITOR-0013`: The command shall write changes through an ffmpeg
|
|
stream-copy remux workflow only. No transcoding shall be performed as part of
|
|
`ffx edit`.
|
|
- `METADATA_EDITOR-0014`: Because ffmpeg cannot rewrite the source file in
|
|
place, `ffx edit` shall write to a temporary output file on the same
|
|
filesystem as the source file and shall replace the original path only after
|
|
ffmpeg reports success.
|
|
- `METADATA_EDITOR-0015`: The temporary output path shall preserve the original
|
|
container type and file extension. The feature shall not silently change the
|
|
container or extension during a metadata-only edit.
|
|
- `METADATA_EDITOR-0016`: If the rewrite step fails, the original file shall
|
|
remain untouched. The system shall not leave the user with a partially
|
|
replaced source file.
|
|
- `METADATA_EDITOR-0017`: After a successful replace, the application shall
|
|
reprobe the rewritten file, refresh the baseline descriptor from disk, reset
|
|
the draft state to that fresh baseline, and clear the dirty state.
|
|
- `METADATA_EDITOR-0018`: Edit mode shall track whether unsaved draft changes
|
|
exist and shall require confirmation before dismissing the screen or quitting
|
|
the app when such changes would be lost.
|
|
- `METADATA_EDITOR-0019`: Edit mode shall not inject conversion-only encoding
|
|
metadata such as encoder quality or preset markers.
|
|
- `METADATA_EDITOR-0020`: Signature-tag behavior shall be explicit for
|
|
metadata-only editing. The default behavior shall not add a misleading
|
|
recoding-style signature to a file that was only remuxed for metadata
|
|
updates.
|
|
- `METADATA_EDITOR-0021`: Configured metadata-removal rules from the local
|
|
configuration shall be surfaced clearly in the UI and in the planned-changes
|
|
view. If those rules are applied during save, the operator shall be able to
|
|
tell that the file will be cleaned in addition to any manual edits.
|
|
- `METADATA_EDITOR-0022`: The command shall provide an invocation-level way to
|
|
disable config-driven cleanup when the operator wants a pure manual metadata
|
|
edit without automatic tag removal.
|
|
- `METADATA_EDITOR-0023`: The existing global `--dry-run` behavior shall apply
|
|
to `ffx edit`. In dry-run mode the command shall not replace the original
|
|
file and shall expose the planned write operation clearly enough for the user
|
|
to understand what would happen.
|
|
|
|
## Acceptance
|
|
|
|
- `ffx edit /path/to/file.mkv` opens successfully on a workstation where the
|
|
configured database is missing, empty, incompatible, or intentionally
|
|
inaccessible.
|
|
- Opening a file in edit mode does not trigger database bootstrap or migration
|
|
prompts.
|
|
- A user can change a container tag, save, and see the rewritten file at the
|
|
same path with the updated metadata.
|
|
- A user can change a stream title or language, save, and see the rewritten
|
|
file at the same path with the updated stream metadata.
|
|
- A user can change `default` or `forced` on a track, save, and see the
|
|
rewritten file at the same path with the updated dispositions.
|
|
- The planned-changes view reflects manual edits relative to the original file
|
|
and, when enabled, any configured cleanup removals.
|
|
- No rendered Rich or Textual color markup appears in the saved file metadata.
|
|
- If ffmpeg fails while saving, the original file remains present and readable
|
|
at the original path.
|
|
- In dry-run mode, the original file remains untouched.
|
|
|
|
## Current Code Fit
|
|
|
|
- Good fit:
|
|
- `FfxController.runJob(...)` already has a `VideoEncoder.COPY` path that
|
|
can remux streams and apply metadata and disposition tokens.
|
|
- `MediaDescriptorChangeSet` already computes container-tag, stream-tag, and
|
|
disposition differences and can generate ffmpeg metadata tokens.
|
|
- `TagDetailsScreen` and `TrackDetailsScreen` already provide reusable edit
|
|
dialogs for draft state.
|
|
- `PatternDetailsScreen` already demonstrates add, edit, and delete flows for
|
|
tags and tracks in a draft-first UI.
|
|
- Refactor required:
|
|
- `ffx` CLI initialization currently creates a database context for all
|
|
non-lightweight commands, so `edit` needs its own DB-free bootstrap path.
|
|
- `FileProperties` currently instantiates `PatternController` eagerly, so
|
|
probing must be split from pattern matching or made lazy.
|
|
- `MediaDetailsScreen` currently assumes `command == 'inspect'` and mixes
|
|
file state with database-backed target-pattern state.
|
|
- `MediaDetailsScreen` currently mutates the probed source descriptor
|
|
directly. Edit mode needs an immutable baseline descriptor and a separate
|
|
mutable draft descriptor.
|
|
- `TrackDetailsScreen` currently exposes structural fields that are valid for
|
|
pattern authoring but too dangerous for metadata-only file editing.
|
|
|
|
## Risks
|
|
|
|
- Container-level metadata support differs across formats, so some requested tag
|
|
changes may not round-trip identically through ffmpeg for every supported
|
|
container.
|
|
- The existing metadata-removal implementation is conversion-oriented and may
|
|
remove tags more aggressively than a user expects from a manual editor unless
|
|
cleanup policy is made explicit.
|
|
- The current codebase lacks a dedicated descriptor clone API, so draft-state
|
|
separation should be implemented deliberately instead of via accidental shared
|
|
references.
|
|
- Replacing a file path with a temporary output changes inode identity, so any
|
|
future requirement around preserving timestamps, hard links, or extended
|
|
attributes would need additional explicit handling.
|