Ctrl K
ComunidadGuías…

docs/conventions.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Conventions

Rules that apply to all of the VHS code. The complete reference is
[architecture.md](architecture.md).

## Source data vs local organization

- Metadata, categories and tags coming from the platform stay in
  `platform_metadata` and in the acquired snapshot (`source_metadata_snapshot`).
  **They are not imported automatically** as local `Tag`s.
- Collections created in VHS are `Collection` / `CollectionVideo` (order
  chosen by the user). Platform playlists, with subscriptions
  (post-MVP), will be `SourcePlaylist` / `SourcePlaylistVideo` (source
  order). The two worlds do not overwrite each other: a remote change does not touch
  local choices and does not delete archived files.
- `Tag` and `Collection` are always local VHS data; in the interface tags are
  called "personal tags". No collection is created automatically from a
  channel or a playlist.

## Files and paths

- Every archived video has a main file; its path relative to
  `VHS_MEDIA_ROOT` is in `Video.file_path` and is computed only
  at archive time. Afterwards it is read from the database: do not recompute it from the title,
  channel or template.
- Identity and the API use `(platform, platform_id)` and the video ID, never the
  path, the title or the channel name.
- Tags, collections and other relations neither move nor duplicate files; folders
  represent only provenance.
- Storage never creates `VHS_MEDIA_ROOT` (sole exception: the new library
  created by `library_service.prepare_for_write()`) and does not write without an
  available library: every method that creates or modifies files calls
  `_require_available()`. The `.vhs-library` marker is created only by
  `init_library` or for the new library, never at startup, during reconciliations
  or by health checks. New code that writes to the library goes through storage.
- Before writing, tasks call `prepare_for_write()`; reconciliations
  stop if the library is unavailable; a video is marked `missing` only when
  the library is available (`can_mark_missing()`).
- No library file is deleted automatically to "tidy up":
  missing files, mismatched checksums and orphans are reported; removal
  is an explicit choice. A new path for an already archived video must
  not lose track of the previous file.
- File verification (`verification_service`) is read-only: no
  `save`, no writes to the library, symbolic links never followed. A
  check that could not see everything declares itself incomplete, never "in
  order".
- Sidecars (`.webp`, `.info.json`) have the same basename as the main
  file. The browser copy instead lives in `.browser/`, with the same
  path as the original and a `.mp4` extension: channel folders
  contain only one video file per item.

## Boundaries between layers

| Layer | May use | Must not |
| ------- | --------- | -------- |
| `api/` | services, schemas | contain business logic, call yt-dlp or the filesystem |
| `services/` | ORM, engine, storage | know whether it is invoked by the API, a task, the CLI or a test |
| `tasks/` | services | contain logic: `task(id)` → `Service.method(id)` |
| `engine/` | Python, yt-dlp, ffmpeg | import Django, Ninja, django-q2 or the models (checked by `tests/test_boundaries.py`) |
| `storage/` | filesystem | know about the Django models |

- The API works with IDs (`video_id`), never with filesystem paths.
- Tasks receive only IDs and must be re-runnable without duplicate effects.
- Platform cookies are credentials: never in logs, never returned in plain text.

## Style

- Python: `ruff check` and `ruff format` (configured in `pyproject.toml`).
- Frontend: `npm run lint`, `npm run type-check`.
- UI and API texts are English source texts through i18n (vue-i18n in the SPA, gettext in
  Django): no text written directly in the interface code. Documentation, code comments,
  logs, commits and script messages are in English.