From ogg2mp3
Converts QQ Music .ogg/.oggh files on macOS to MP3 with embedded cover art from Resource Fork and id3v2.3 tags. Includes commands for single files, batch conversion, metadata inspection, and environment checks.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ogg2mp3:ogg2mp3The summary Claude sees in its skill listing — used to decide when to auto-load this skill
QQ Music's macOS app stores audio as Vorbis-in-Ogg (`.ogg` or `.oggh`) with the cover image hidden in the **macOS Resource Fork** as an ICNS resource — *not* in the Vorbis stream. `ogg2mp3` reassembles audio + cover + metadata into a standard MP3 with id3v2.3 tags using `ffmpeg`, `derez`, and `icnsutil`.
QQ Music's macOS app stores audio as Vorbis-in-Ogg (.ogg or .oggh) with the cover image hidden in the macOS Resource Fork as an ICNS resource — not in the Vorbis stream. ogg2mp3 reassembles audio + cover + metadata into a standard MP3 with id3v2.3 tags using ffmpeg, derez, and icnsutil.
.ogg or .oggh files from QQ Music's Mac app and wants .mp3..ogg files from other sources (web downloads, Bandcamp, etc.) — conversion will still produce a valid MP3 but cover_embedded will be false because the file has no ICNS in its Resource Fork.ffmpeg directly.ffmpeg directly.~/.local/bin/ogg2mp3. If command -v ogg2mp3 fails, the user has not installed it — instruct them to git clone https://github.com/HerbertGao/ogg2mp3 && bash ogg2mp3/scripts/install.sh.ogg2mp3 doctor --json once per session before any convert / batch call. Parse .ok; if false, stop and surface each failing check's install_hint to the user..binary_version from doctor is 2026.5.0 or higher. If the binary is newer and emits a different JSON shape, trust the binary over this document.convert <input> — convert one fileogg2mp3 convert <input> [-o <output>] [--no-cover] [--no-metadata] [--overwrite] [--yes] [--json]
| Flag | Meaning |
|---|---|
-o <path> | Output path. Default: <input> with extension swapped to .mp3, same directory. |
--no-cover | Skip cover art extraction and embedding. |
--no-metadata | Skip metadata copying. |
--overwrite | Replace existing output without prompting. |
--yes | Required for non-interactive callers. Skips all confirmation prompts. |
--json | Emit a single JSON object to stdout. |
JSON result (with --json):
{
"ok": true,
"input": "/path/to/song.ogg",
"output": "/path/to/song.mp3",
"cover_embedded": true,
"metadata_fields": 3
}
Without --json, stdout is one line: the output path. Logs go to stderr.
Exit codes: 0 success · 1 conversion failed or output exists without --overwrite · 2 bad args · 3 missing dependency or running as root.
Agent rules:
--yes. You are non-interactive; without it, an existing output blocks the run waiting for stdin.--json so you can parse the result.--no-cover unless the user explicitly says they don't want cover art — cover embedding is the whole point of this tool over plain ffmpeg.cover_embedded: false is not an error; it usually means the source had no ICNS in its Resource Fork (i.e. not actually from QQ Music). Surface this to the user as a note, don't retry.batch <dir> — convert a directoryogg2mp3 batch <dir> [-r|--recursive] [--skip-existing | --overwrite] [--no-cover] [--no-metadata] [--dry-run] [--yes] [--json]
| Flag | Meaning |
|---|---|
-r, --recursive | Recurse into subdirectories. Default: top level only. |
--skip-existing | Skip files whose .mp3 already exists. Mutually exclusive with --overwrite. |
--overwrite | Replace existing .mp3 files. |
--dry-run | List planned conversions; do not execute. |
--yes, --json, --no-cover, --no-metadata | Same semantics as convert. |
Output format (--json mode → NDJSON, one object per line):
| Outcome | Schema |
|---|---|
| Converted | {"ok": true, "input": "...", "output": "...", "cover_embedded": bool, "metadata_fields": int} |
| Skipped | {"ok": true, "skipped": true, "input": "...", "output": "..."} |
| Failed | {"ok": false, "input": "...", "output": "...", "error": "..."} |
| Dry-run preview | {"input": "...", "output": "...", "planned_action": "convert"|"skip"|"fail", "reason": null | "..."} |
Plain mode emits OK\t<path> / SKIP\t<path> / FAIL\t<path>\t<reason> per line.
Exit codes: 0 all succeeded (or dry-run) · 1 at least one file failed · 2 bad args (including using --skip-existing and --overwrite together) · 3 missing dep or root.
Default flag combinations Agents should use:
| User intent | Recommended flags |
|---|---|
| "Convert all songs in this folder" | -r --skip-existing --yes --json |
| "Reconvert everything" | -r --overwrite --yes --json |
| "Show me what this will do" | -r --dry-run --json |
If .mp3 already exists and the user did not specify policy, default to --skip-existing and tell the user how many were skipped — do not silently overwrite.
inspect <input> — read-only probeogg2mp3 inspect <input> [--json]
Does not convert. Reports codec, duration, bitrate, all vorbis tags, and whether an ICNS cover is present in the Resource Fork.
JSON schema:
{
"file": "/path/to/song.ogg",
"codec": "vorbis",
"duration_sec": 181.25,
"bitrate": 314482,
"metadata": { "TITLE": "...", "ARTIST": "...", "ALBUM": "..." },
"cover": { "present": true, "size_bytes": 356845 }
}
Exit codes: 0 readable · 1 ffprobe could not parse the file.
Agent rules:
inspect as a precondition for convert — convert already handles missing covers gracefully and is the canonical write path.doctor — environment self-checkogg2mp3 doctor [--json]
JSON schema:
{
"ok": true,
"binary_version": "2026.5.0",
"checks": [
{
"name": "ffmpeg",
"found": true,
"path": "/opt/homebrew/bin/ffmpeg",
"version": "8.1.1",
"install_hint": "brew install ffmpeg"
}
]
}
Checks all of: ffmpeg, ffprobe, derez, icnsutil, jq, xxd, grep, sed.
Exit codes: 0 all green · 3 any missing.
Agent rules:
convert or batch..ok is false, do not call convert or batch. Surface each failing check's install_hint to the user verbatim..binary_version is 2026.5.0 or higher (CalVer; major = year, minor = month). If the binary is newer and emits a JSON shape that disagrees with this document, trust the binary.These exist for human terminal users and will hang or misbehave if an Agent invokes them:
| Don't | Why |
|---|---|
ogg2mp3 with no subcommand | Drops into an interactive menu prompt. Blocks on stdin in a TTY; in non-interactive agent contexts exits 0 or 1 without performing any action. Always pass an explicit subcommand. |
convert or batch without --yes when output exists | Prompts y/N only in a TTY; in non-interactive agent contexts the binary exits 1 immediately rather than blocking. Always pass --yes. |
--skip-existing together with --overwrite | Exit 2 (mutually exclusive). |
Running under sudo / as root | Exit 3 (require_unprivileged guard). The binary refuses on purpose to avoid leaving root-owned files in the user's library. |
--json).→, ✓, warning:, error:).| Symptom | Meaning | What to do |
|---|---|---|
exit 3 from anything | Missing dependency or running as root | Run doctor --json; surface install_hints. Never retry the same call. |
exit 1 from convert | Conversion failed | Read stderr (ffmpeg: lines) for the real cause. Don't silently retry. |
cover_embedded: false on success | No ICNS in Resource Fork | Note it to the user (likely not a QQ Music file). MP3 is still valid. |
metadata_fields: 0 on success | OGG had no Vorbis tags | Note it. MP3 is still valid, just untagged. |
Batch exits 1 but most files are ok: true | Some files failed | Filter NDJSON for ok: false lines and report those to the user. |
Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.
npx claudepluginhub herbertgao/ogg2mp3 --plugin ogg2mp3