# `MishkaCmsCore.Dev.SeedMedia`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/dev/seed_media.ex#L5)

The files the dev seeds put in a site's media library, and where any other seed gets a picture.

A media row used to be written with an invented path — `/uploads/images/3.jpg`, which nothing had
ever written a file to — so every thumbnail in the library fell back to its type glyph, the
builder's media sheet handed pages an `<img src>` that answered 404, and a blog cover was either
nothing or one of the marketing site's own photographs. The set under `priv/dev_seed_media` is
drawn from primitives rather than photographed: nobody's copyright, nothing downloaded, and small
enough (well under 40 KB each) to live in the repository.

A FILE'S NAME SAYS WHAT IT IS FOR. `cover-*` is a wide banner, `card-*` a listing picture,
`avatar-*` a portrait and `doc-*` a document; that prefix is the whole registration, so adding one
is dropping it in the directory. An entry whose prefix is none of those — or that is not a file at
all — is left alone rather than guessed at: `files/0` is what the seed writes, and it only names
what it can describe.

Storing goes through the resource's own upload path — `Media.validate_and_process_file/1` then
`Media.save_file_to_storage/3` — so a seeded row lands exactly where an uploaded one does, under
`/uploads/<site>/media/<category>/<uuid>.<ext>`, and replacing, archiving or deleting it from the
admin works on it like any other file. The copy is staged through a temporary file first: storing
removes the source it is given, and the source here is a file the repository ships.

There are no videos in the set. A real `.mp4` needs an encoder nothing in this toolchain has, and a
row pointing at one we could not write is the 404 this module exists to remove. That costs the dev
database the one `:videos` row the old seed invented, so a seeded library no longer has anything
for the media screen's type filter, its `by_type` read or the film glyph the thumbnail column draws
in place of a picture — ground the resource's own tests and the stress seed still cover, and that a
real `.webm` should take back the day something here can write one.

# `dimensions`

```elixir
@spec dimensions(String.t()) :: map()
```

The pixel size of `path` in the shape the `dimensions` attribute stores, or `%{}` for a file whose
header this cannot read.

Read from the file rather than declared beside it, so a replaced picture cannot leave a row
describing the one before it. Every picture in the set is a lossy WebP, which keeps its canvas
size in the first chunk (`VP8 `) 26 bytes in, as two 14-bit values; that is the one header this
reads. Anything else — a document, or a lossless (`VP8L`) or extended (`VP8X`) WebP somebody adds
later — answers `%{}` rather than a guess, and the suite says which file it was, so the clause for
that chunk gets written the day a file needs it.

# `directory`

```elixir
@spec directory() :: String.t()
```

Where the shipped files live.

Configurable, the way `MishkaCmsCore.Runtime.PageUpload.directory/1` is, and for a reason the
shipped path cannot cover: a file's presence in this directory IS its registration, and under
`:test` the path below resolves through Mix's `priv` symlink to the repository's own source. A
suite proving that a file dropped in between two runs is picked up would be dropping it into the
set the package ships — one killed VM away from a stray picture nothing ignores and `git add -A`
commits. Pointed somewhere else, it writes where it can afford to.

# `files`

```elixir
@spec files() :: [
  %{filename: String.t(), path: String.t(), shape: atom(), title: String.t()}
]
```

Every shipped file as `%{filename: ..., path: ..., shape: ..., title: ...}`, ordered by name.

Answers `[]` when the directory is not there — a package that did not ship it seeds a library with
nothing in it rather than failing the whole run.

# `images`

```elixir
@spec images(String.t()) :: %{required(atom()) =&gt; [String.t()]}
```

The paths of the images `site_id` already has, grouped by shape — `%{cover: [...], card: [...],
avatar: [...]}`.

This is how a seed that needs a picture asks for one: the blog's covers and avatars are rows in
the library rather than strings pointing at whatever happens to be under `priv/static`. A site
that has not been media-seeded answers `%{}`, and every caller has to read that as "no picture",
because that is also what a fresh install looks like.

# `install`

```elixir
@spec install(String.t()) :: [MishkaCmsCore.Runtime.Media.t()]
```

Writes whatever of the set `site_id` does not have yet into its library, one category per shape
the set has, and returns the media rows it wrote.

A file that cannot be stored is reported and skipped: a library short one picture is worth more to
whoever is seeding than a run that stops on it.

RUN AGAIN, THIS ADDS ONLY WHAT IS NEW. Reseeding is how a developer gets the pictures a later
version of this set ships without dropping their database, so a category that is there is reused
and a file that is there is passed over — which is also what makes dropping one more file into
`priv/dev_seed_media` and reseeding the way to add it. It never updates: the row an admin renamed,
re-described or moved to another category is theirs.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
