本文へ移動
cccskills
無料GitHub で公開

covers

Work on album artwork — reading an image's dimensions, and embedding a folder's cover file into the tags of its tracks. Use this whenever a task touches `services/artwork.py`, the cover half of `services/tags.py`, or `scripts/embed_covers.py` / `embed_covers_cron.sh`. Covers why Lyrion shows the embedded artwork and never the folder file, how covers are compared, why images are never re-encoded, and what embedding costs in rewritten files.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md5.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Album artwork: measuring it, embedding it

Three cooperating pieces, none of which talk to Lyrion:

  • services/artwork.py — dimensions from an image's header bytes. Dependency-free, so it works on a byte string from anywhere: a file, a tag, a streamed HTTP response.
  • services/tags.py — read_cover / write_cover, next to the lyrics functions. Framework-free, mutagen-based.
  • scripts/embed_covers.py + embed_covers_cron.sh — the batch job, same shape as the lyrics pair.

The fact that motivates all of it

Lyrion serves the artwork embedded in the file's tags, and never the folder.jpg sitting beside it. A library can therefore hold a 3000 px sleeve on disk while every player shows the 500 px one buried in the tags, with nothing in the UI hinting at the gap. albums.artwork in library.db is a coverid pointing at a track, not at a file on disk.

That is what embed_covers.py fixes: it copies the folder's file into the tags so the good image becomes the one Lyrion actually shows.

Measuring: header bytes only

image_size(data) returns (format, width, height) for JPEG, PNG, GIF, BMP and WebP, or None.

from services import artwork

artwork.image_size(data)      # ("jpeg", 1400, 1400)

None is deliberately ambiguous: it means not known from these bytes, covering both "not an image" and "the header hasn't arrived yet". A caller streaming a file can keep reading and ask again. Don't tighten that contract — a JPEG carrying a large colour profile can push its SOF marker hundreds of kilobytes in, so a fixed-size sniff is not enough.

Dimensions are read for reporting and for refusing a file that is not an image at all — not to decide whether to embed. That decision is a byte comparison, see below.

Writing: never re-encode

write_cover(path, data) stores the bytes it is handed, as they are. It drops any existing picture first, so a file ends up with exactly one. Formats: MP3/AIFF/WAVE (APIC), MP4/M4A (covr), FLAC (picture block); anything else raises CoverTagError.

Never re-encode an image to make it fit. Every write in this codebase is a byte copy, which is why the tag and the folder file can be compared byte for byte afterwards — the cheapest possible verification that a batch did what it claimed.

Embedding rewrites the whole audio file: artwork rarely fits the padding a smaller cover left behind. That is why embed_covers.py reports how much the files grow, and why the cron wrapper exists at all — re-tagging an album the day it arrives is free, because a new album is synced and backed up in full anyway; re-tagging it six months later means resyncing a file everyone thought was stable.

The rule: the folder decides

embed_covers.py compares each track's embedded bytes against the folder's file and rewrites the ones that differ. Nothing else enters the decision — not dimensions, not file size, not which looks better.

That makes the folder's file the single source of truth, and it is what lets a cover be replaced by a smaller one: capping an absurd 7000 px sleeve at 2000 px is a legitimate edit, and a rule of "only if bigger" would silently refuse it while leaving the huge image in every track. The protection it offered against a bad cover was accidental, never designed; review happens before the file lands in the folder.

Two properties follow, and both matter when a cron runs this nightly: only the tracks that actually differ are touched, so an interrupted run resumes cheaply; and a second pass writes nothing, so the job is idempotent.

The batch job

python scripts/embed_covers.py /path/to/music --dry-run
python scripts/embed_covers.py "/path/to/music/A*"      # globs, even quoted

An album folder is any folder holding music files, which leaves Scans/ and Artwork/ subfolders alone. Files starting with a dot are skipped: macOS leaves AppleDouble stubs (._track.mp3) that carry the extension but none of the content, and reading one instead of the real track makes a fully tagged album look empty.

Every track is read, since players pick artwork per file and a half-applied album must be finishable.

embed_covers_cron.sh mirrors embed_lyrics_cron.sh — marker stamped at the start, advanced only on success, untouched by --dry-run — with one difference: find -cnewer lists changed files, and it is their folders that get passed on. A replaced folder.jpg therefore queues its album exactly like a new track does.

Rules

  • Read dimensions through services/artwork.py; never add an imaging dependency for a job the header already answers.
  • Never re-encode, never resize: the bytes in the folder are the bytes that go into the tags.
  • Decide on bytes, never on dimensions — the folder's file wins even when it is smaller.
  • Writes go to every track of the album, not just the first.
  • Lyrion is never contacted: the files are the interface, and Lyrion re-scans on its own.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

add-route

無料

Add or modify a Flask endpoint in the Lyrion Dashboard. Use this whenever a task adds a new URL/route, a JSON API endpoint, or a page — anything that registers a handler in `routes/`. Covers the repo's route → service → test layering, input validation, caching headers, security headers, i18n wiring, and the exact test pattern, so a new endpoint matches how `routes/` already works instead of reinventing the structure.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

android

無料

Work on the Android app — the thin WebView wrapper in `android/`. Use this whenever a task touches the Kotlin sources, the Gradle build, the WebView shell, the native settings screen, LMS server auto-discovery, the JS↔native bridge, app strings/i18n, or the Android CI/lint. Covers the "shell only, UI lives on the web" principle, the debug/release signing split, the static versioning F-Droid needs, and the min/target SDKs, so changes fit the wrapper.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

database

無料

Read data from Lyrion's SQLite databases — library stats, stored lyrics, album covers, recent plays. Use this whenever a task queries `library.db` or `persist.db`, adds or changes a statistic, or touches `services/database.py`. Covers the read-only connection model (RO open + `ATTACH persist.db`), the key tables, the `play_counts` view over the Alternative Play Count plugin, the single-flight stats cache, and why the app must never write to these DBs. Reach for it before writing any SQL against the Lyrion databases.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

frontend

無料

Work on the dashboard's frontend — the vanilla JS/CSS page with no framework or bundler. Use this whenever a task touches `static/nowplaying.js`, `static/style.css`, the Jinja templates (`templates/nowplaying.html`, `_icons.html`), the accent-color tinting, the karaoke lyrics sync, the now-playing poll, or the empty-state mosaic / recent-plays pile. Covers how the page is wired, how it reads i18n and data from the server, the ESLint gate, and the Android bridge, so changes match the existing patterns.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

i18n

無料

Add, change, or translate user-facing text in the Lyrion Dashboard, which ships bilingual FR/EN. Use this whenever a task introduces a new visible string (a label, tooltip, aria-label, empty state, stat name), changes wording, or touches the README or a `docs/` page — so every string lands in both languages and the EN/FR documentation pairs stay in sync. Covers `i18n.py`, how templates and JS consume translations, and the parity rule.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

lyrics

無料

Work on lyrics — the web fallback (LRCLIB, Musixmatch, NetEase, Genius), the in-memory cache, verification, and embedding lyrics into audio file tags. Use this whenever a task touches `services/lyrics.py`, `services/tags.py`, the `/lyrics.json` route, or `scripts/embed_lyrics.py` / `embed_lyrics_cron.sh` — adding or fixing a provider, changing caching/verification, or the batch tagger. Covers the provider contract, the synced-vs-plain rule, the read-only cache, and why writes only ever store plain text.

日本語の概要は準備中です。原文の説明を表示しています。

werdeil/lyrion-dashboard32026年10月15日 更新

werdeil のスキルをすべて見る

このスキルの問題を報告する