{"id":50351197,"url":"https://github.com/marcomc/get-my-domino","last_synced_at":"2026-05-29T21:01:38.581Z","repository":{"id":354764652,"uuid":"1223897045","full_name":"marcomc/get-my-domino","owner":"marcomc","description":"Unofficial CLI for private Rivista Domino article export and local audio","archived":false,"fork":false,"pushed_at":"2026-05-29T15:53:46.000Z","size":381,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T17:18:05.830Z","etag":null,"topics":["accessibility","audiobook","cli","html-export","italian","magazine","offline-reading","personal-use","python","reader","text-to-speech"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcomc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-28T19:02:30.000Z","updated_at":"2026-05-20T13:40:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marcomc/get-my-domino","commit_stats":null,"previous_names":["marcomc/get-my-domino"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/marcomc/get-my-domino","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fget-my-domino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fget-my-domino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fget-my-domino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fget-my-domino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcomc","download_url":"https://codeload.github.com/marcomc/get-my-domino/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fget-my-domino/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33670211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessibility","audiobook","cli","html-export","italian","magazine","offline-reading","personal-use","python","reader","text-to-speech"],"created_at":"2026-05-29T21:01:36.830Z","updated_at":"2026-05-29T21:01:38.564Z","avatar_url":"https://github.com/marcomc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# get-my-domino\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Use and Rights](#use-and-rights)\n- [Features](#features)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Usage](#usage)\n- [Project Layout](#project-layout)\n- [Development](#development)\n- [Release Notes](#release-notes)\n- [License](#license)\n\n## Overview\n\n`get-my-domino` is an independent, unofficial CLI for readers who already have\nlawful access to content on `rivistadomino.it`. It downloads articles for\noffline reading and audio generation, discovers magazine issues and recurring\narticle feeds, lists articles with their section grouping, exports each article\nas clean HTML, UTF-8 text, optional RTF, and lightweight metadata, and can\nturn the text into local `.m4a` or `.mp3` audio files with the macOS `say`\nvoice engine.\n\n## Use and Rights\n\nThis project is intended only for people who already have an active, paid, and\nlawful right to access the relevant Rivista Domino digital content.\n\nThe generated text exports, per-article audio files, and issue-level\naudiobooks are intended for strictly private, personal use, including\naccessibility and reading-support scenarios such as visual impairment or other\nreading difficulties. They are not intended for redistribution, public\nperformance, publication, sharing with non-subscribers, or any other use that\ncould infringe third-party rights.\n\nThis project is not designed, marketed, or maintained as a tool for unlawful\ncopying or publication of copyrighted material. You are responsible for using\nit only in ways permitted by applicable law and by the terms of your\nsubscription.\n\n`get-my-domino` is an independent reader-built tool. It is not affiliated with,\nendorsed by, sponsored by, or operated by Rivista Domino, Dario Fabbri,\nEnrico Mentana, or Edizioni Gommonica S.r.l., which the Rivista Domino site\nidentifies in its service terms and subscription pages as the service provider\nand publishing company for the digital offering.\n\n## Features\n\n- Packaged command-line app exposed as `get-my-domino`\n- `python -m get_my_domino` entry point\n- Issue and article discovery from configurable link patterns\n- Magazine export grouped by issue code, issue title, section, and article\n  date\n- `La settimana di Domino` export grouped in one dated article collection\n- Clean article export to same-name `.html` and `.txt` files, optional `.rtf`,\n  and lightweight `metadata.json`; readable text omits source URLs so audio\n  does not read them aloud, and speech-normalized runs record prompt and\n  source metadata for reuse/invalidation\n- Incremental `sync-magazine` and `sync-feed` commands with local manifests\n- Optional `.m4a` or `.mp3` synthesis through macOS `say` and `afconvert`\n- Optional full-issue `.m4b` audiobook packaging with chapter markers, embedded\n  cover art, and issue metadata when downloading or syncing magazine issues\n- Issue sidecars as `issue.json` with title, issue URL, release date, summary,\n  cover paths, section/order data, and article-folder mapping\n- Repair commands to refresh issue metadata from the live site and repackage\n  issue audiobooks without regenerating chapter audio\n- Default config file at `~/.config/get-my-domino/config.toml`\n- Saved authenticated sessions at `~/.config/get-my-domino/session.json`\n- Separate sync flow for `La settimana di Domino`\n- `uv`-driven sync, lint, test, and install flows\n- `ruff`, `mypy`, `pytest`, `markdownlint`, and `shellcheck` wired in\n- Maintainer notes for the current Domino site structure and parser touchpoints\n  under `docs/`\n\n## Requirements\n\nFor users:\n\n- Python `3.11`\n- `uv`\n- `make`\n- Playwright only when using browser-assisted login\n- macOS `say` and `afconvert` only when generating audio\n- `ffmpeg` only when generating `.mp3` audio\n- `ffmpeg` and `ffprobe` when packaging issue audiobooks as `.m4b`\n\nFor maintainers:\n\n- `markdownlint`\n- `shellcheck`\n\n## Installation\n\nClone the repository and install the standalone runtime:\n\n```bash\ngit clone \u003crepo-url\u003e\ncd get-my-domino\nmake install\n```\n\n`make install`:\n\n- creates a standalone virtual environment in\n  `~/.local/share/get-my-domino/venv`\n- installs the packaged CLI into that standalone runtime\n- links the command to `~/.local/bin/get-my-domino`\n- installs a config template to `~/.config/get-my-domino/config.toml` if it\n  does not exist yet\n- installs the editable speech-normalization prompt to\n  `~/.config/get-my-domino/speech-normalize-codex.txt` if it does not exist yet\n\nIf `~/.local/bin` is not on your `PATH`, `make check-deps` prints the shell\nsnippet to add it.\n\n### Editable Development Install\n\n```bash\nmake install-dev\n```\n\nThis points `~/.local/bin/get-my-domino` at the project-local `.venv` so source\nedits are reflected immediately.\n\n### Browser Login Support\n\nBrowser-assisted login needs the optional browser dependency:\n\n```bash\nuv sync --extra browser\n```\n\nThe command first tries system Chrome. If no supported browser is available,\ninstall Playwright Chromium:\n\n```bash\nuv run playwright install chromium\n```\n\n## Configuration\n\nThe CLI reads optional config from:\n\n- `~/.config/get-my-domino/config.toml`\n- or the file passed with `--config`\n\nStart from the example file in this repository:\n\n- [config.toml.example](config.toml.example)\n- [config.full.toml](config.full.toml)\n- [config.schema.json](config.schema.json)\n\n`config.toml.example` is the recommended user template. It keeps only the\nsettings most readers are likely to change.\n\n`config.full.toml` is a complete reference file with every supported key,\nincluding advanced site-override and maintenance knobs. It is not loaded\nautomatically by the CLI. The runtime still reads one config file at a time,\nnormally `~/.config/get-my-domino/config.toml`.\n\nExample:\n\n```toml\nverbose = false\noutput_parent_dir = \"~/Documents\"\ncollection_dir_name = \"domino\"\n# output_dir = \"~/Documents/domino\"\n# audiobook_output_dir = \"~/Audiobooks/Domino\"\nfeed_folder_name = \"la-settimana-di-domino\"\nexport_formats = [\"html\", \"txt\"]\nsiri_voice = \"\"\naudio_auto = false\naudiobook_auto = false\naudio_format = \"m4a\"\nspeech_normalize_auto = false\nspeech_normalize_command = \"codex\"\nspeech_normalize_model = \"\"\nspeech_normalize_prompt_path = \"~/.config/get-my-domino/speech-normalize-codex.txt\"\nmagazine_title = \"Domino\"\nfilename_separator = \"-\"\naudiobook_name_format = \"{magazine_slug}{sep}{year}{sep}{number}{sep}{title_slug}\"\nauth_username = \"\"\nauth_password = \"\"\nauth_session_path = \"~/.config/get-my-domino/session.json\"\n```\n\nAuthentication can use either a saved session or TOML credentials. Run\n`get-my-domino login --browser` to open a browser, authenticate manually, and\nsave the resulting WordPress/WooCommerce cookies to `auth_session_path`.\nSubsequent commands reuse that session without requiring `auth_username` or\n`auth_password`. If the saved session expires, run browser login again or fill\nthe TOML credentials and run `get-my-domino login`.\n\nDomino currently exposes WordPress session cookies, not a separate refresh\ntoken like the Degoo flow used by `cligoo`. The CLI therefore persists cookies\nand revalidates them against `mio-account`.\n\nAs of May 1, 2026, Domino’s public FAQ still describes the app/reader flow as\nreusing the same site credentials and then remembering them locally, which\nmatches the cookie/session approach above rather than a renewable API token\nflow.\n\nIssue discovery starts from the subscriber `my_domino` page and keeps\n`?sfoglia=1` links so article discovery can reach the private issue contents.\nThe issue index parser also prefers descriptive product-card titles over\ngeneric CTA labels like `Sfoglia`, while keeping the listing synopsis for\ncatalog output.\n\nMost users should never need to touch the advanced site-override keys such as:\n\n- `content_selectors`\n- `issue_link_patterns`\n- `article_link_patterns`\n- `feed_article_link_patterns`\n- `auth_submit_field`\n- `auth_submit_value`\n\nThose exist mainly as recovery knobs if the site markup or login form changes.\n\n## Usage\n\nPrint the focused top-level help:\n\n```bash\nget-my-domino\n```\n\nList available magazine issues:\n\n```bash\nget-my-domino issues\n```\n\nList the articles under one issue:\n\n```bash\nget-my-domino articles \"https://www.rivistadomino.it/prodotto/guaio-persiano/?sfoglia=1\"\n```\n\nThe article listing preserves the section headings from the issue page, such as\n`L'Editoriale`, `La guerra va male`, `L'Iran resiste`, and\n`La guerra altrove`.\n\nList recurring `La settimana di Domino` articles:\n\n```bash\nget-my-domino feed\nget-my-domino feed --pages 3\n```\n\nBrowse the magazine catalog with cleaner, grouped output:\n\n```bash\nget-my-domino catalog\nget-my-domino catalog --issue 2026-04\nget-my-domino catalog --issue \"https://www.rivistadomino.it/prodotto/guaio-persiano/?sfoglia=1\"\nget-my-domino catalog --all\nget-my-domino catalog --all --feed --pages 3\n```\n\n`catalog` lists all available magazine issues by `YYYY-NN` issue code. `--issue`\n expands one issue by issue code or URL, preserving its sections and article\norder. `--all` expands every issue. `--feed` appends the recurring\n`La settimana di Domino` entries. Catalog issue lists are sorted by `YYYY-NN`\nissue code and omit storefront price text.\n\nUse `catalog` for human browsing. The older `issues`, `articles`, and `feed`\ncommands remain as raw URL list commands for scripts and JSON output.\n\nCommand intent:\n\n| Command | Scope | Intended use |\n| --- | --- | --- |\n| `catalog` | Lists issues, issue contents, and feed entries | Human browsing before choosing what to download |\n| `download` | Downloads known targets by URL, one issue article, or one whole issue | Manual, targeted downloads and repairs |\n| `sync-magazine` | Scans every available magazine issue and downloads only missing articles; with `--audio`, also generates missing audio for already synced articles | Periodic archive updates and automation |\n| `sync-feed` | Scans the recurring weekly feed and downloads only missing articles; with `--audio`, also generates missing audio for already synced feed articles | Periodic weekly-feed updates and automation |\n| `refresh-issue-metadata` | Re-reads downloaded issue articles from the live site and refreshes local `metadata.json` plus `issue.json` | Metadata repairs after parser improvements or site changes |\n| `repackage-audiobook` | Runs issue-metadata refresh, then rebuilds the issue `.m4b` from existing chapter audio | Audiobook tag and cover repairs without re-synthesizing audio |\n| `rename-audiobooks` | Renames existing `.m4b` files from embedded tags using the configured filename template | Normalizing an audiobook library after changing naming rules |\n\n`download` is intentionally narrow: it does not scan the whole archive unless\nyou select one issue with `--issue` and `--all`. `sync-magazine` is the\ndatabase/archive maintenance command: it walks all available magazine issues,\nuses the local manifest to skip articles already present, and adds only new\narticles. `sync-feed` does the same for `La settimana di Domino`.\n\nWhen you add `--audiobook` to `download --issue YYYY-NN --all` or to\n`sync-magazine`, the CLI also packages each complete magazine issue as one\nchapterized `.m4b` file under `config.audiobooks_dir`, which defaults to\n`output_dir/audiobooks/` unless you set `audiobook_output_dir`. The package\nreuses the ordered per-article `.m4a` files as audiobook chapters, embeds the\nissue cover image when available, and writes issue-level metadata sidecars as\n`\u003cissue-folder\u003e/issue.json`. If you set `audiobook_auto = true` in the config,\nthe same packaging also happens automatically for `sync-magazine` and for full\nissue downloads via `download --issue YYYY-NN --all`.\n\nVerified audiobook metadata written into the `.m4b` container:\n\n- `title`\n- `album`\n- `artist`\n- `album_artist`\n- `composer` with the unique issue-level contributor list\n- `date`\n- `genre`\n- `comment` using the issue URL\n- `description`\n- `synopsis`\n- chapter titles from the issue article order, expanded to `Title (di Author)`\n  when article author metadata is available\n- embedded cover art as an attached picture stream\n\nThe bundler is backward-compatible with existing archives that already contain\nolder issue audio filenames, as long as the chapter files still preserve their\narticle order prefix such as `01-`, `02-`, and so on.\n\nIssue sidecars also keep contributor metadata per article and as a deduplicated\nissue-level contributor list under `contributors`.\n\nWhen you already have the articles and chapter audio on disk, use the repair\ncommands instead of `download --force`:\n\n```bash\nget-my-domino refresh-issue-metadata --issue 2026-04\nget-my-domino repackage-audiobook --issue 2026-04\n```\n\n`refresh-issue-metadata` updates the per-article `metadata.json` files and the\nissue-level `issue.json` from the live site, but does not regenerate chapter\naudio and does not rebuild the `.m4b`. `repackage-audiobook` depends on that\nrefresh step and then rebuilds the `.m4b` from the existing `.m4a` chapter\nfiles already present inside each article folder.\n\nAudiobook filenames are configurable. By default they are written as:\n\n```text\ndomino-2026-04-guaio-persiano.m4b\n```\n\nThe default template is:\n\n```text\n{magazine_slug}{sep}{year}{sep}{number}{sep}{title_slug}\n```\n\nThe naming model is:\n\n- `magazine_title` is the base text you choose, for example `Domino` or\n  `Rivista Domino`\n- `{magazine}` inserts that text after filename-safe cleanup, preserving case\n  and spaces\n- `{magazine_slug}` is derived from `magazine_title` by lowercasing it and\n  replacing spaces or punctuation with the configured separator\n\nSo:\n\n- `magazine_title = \"Domino\"` with `{magazine}` gives `Domino`\n- `magazine_title = \"Domino\"` with `{magazine_slug}` gives `domino`\n- `magazine_title = \"Rivista Domino\"` with separator `-` and\n  `{magazine_slug}` gives `rivista-domino`\n\nYou can override that per command:\n\n```bash\nget-my-domino repackage-audiobook \\\n  --issue 2026-04 \\\n  --magazine-title \"Rivista Domino\" \\\n  --filename-separator \".\" \\\n  --audiobook-name-format \"{magazine_slug}{sep}anno-{year}{sep}numero-{number}{sep}{title_slug}\"\n```\n\nPreferred config keys:\n\n```toml\nmagazine_title = \"Domino\"\nfilename_separator = \"-\"\naudiobook_name_format = \"{magazine}{sep}{year}{sep}{number}{sep}{title_slug}\"\n```\n\nOlder keys such as `audiobook_filename_magazine_title`,\n`audiobook_filename_separator`, and `audiobook_filename_format` are still\naccepted for backward compatibility, but the names above are now the preferred\nones.\n\nIf your `.m4b` library lives elsewhere, set for example:\n\n```toml\naudiobook_output_dir = \"~/Audiobooks/Domino\"\n```\n\nThat redirects only the packaged audiobook destination. Article exports,\nissue metadata, and per-article audio still remain under `output_dir/library`.\n\nAvailable filename fields:\n\n| Field | Meaning | Example |\n| --- | --- | --- |\n| `{magazine}` | Magazine title after filename-safe cleanup, preserving case and spaces | `Rivista Domino` |\n| `{magazine_slug}` | Lowercase slug derived from `magazine_title`, using the configured separator between words | `rivista-domino` |\n| `{sep}` | The configured separator | `-` |\n| `{year}` | Issue year from `YYYY-NN` | `2026` |\n| `{number}` | Issue number within that year from `YYYY-NN` | `04` |\n| `{issue}` | Year and issue number joined with the configured separator | `2026-04` |\n| `{issue_compact}` | Year and issue number without a separator | `202604` |\n| `{title}` | Issue title after filename-safe cleanup, preserving case and spaces | `Guaio persiano` |\n| `{title_slug}` | Lowercase slug form of the issue title | `guaio-persiano` |\n\nAllowed separator characters are intentionally narrow so the result is safe on\nmacOS and Linux:\n\n- `-`\n- `_`\n- `.`\n\nExamples:\n\n- `{magazine_slug}{sep}{year}{sep}{number}{sep}{title_slug}` -\u003e `domino-2026-04-guaio-persiano`\n- `{magazine}{sep}{year}{sep}{number}{sep}{title_slug}` -\u003e `Domino-2026-04-guaio-persiano`\n- `{magazine_slug}{sep}anno-{year}{sep}numero-{number}{sep}{title_slug}` -\u003e `rivista-domino-anno-2026-numero-04-guaio-persiano`\n- `{number}{sep}{year}{sep}{magazine_slug}{sep}{title_slug}` with `sep = \".\"` -\u003e `04.2026.rivista-domino.guaio-persiano`\n\nUse `rename-audiobooks` after changing the template to normalize files already\npresent in your library. It reads the embedded `.m4b` tags and rebuilds the\nfilename from those tags, so it does not need the original article folders:\n\n```bash\nget-my-domino rename-audiobooks --dry-run\nget-my-domino rename-audiobooks --library-dir ~/Audiobooks/Domino\nget-my-domino rename-audiobooks ~/Audiobooks/Domino/legacy-file.m4b\n```\n\nDownload specific articles:\n\n```bash\nget-my-domino download \\\n  \"https://rivistadomino.it/articolo/example/\"\n```\n\nYou can also download one magazine article by issue code and article order\nfrom `catalog --issue`:\n\n```bash\nget-my-domino download --issue 2026-04 --article 1\n```\n\nDownload every article from one magazine issue:\n\n```bash\nget-my-domino download --issue 2026-04 --all\n```\n\nExplicit downloads reuse existing article folders from the manifest. If only\naudio is missing, the CLI generates audio from the existing UTF-8\n`.txt` export. If audio already exists, it is reused and not regenerated. If\nany configured export file is missing, it refetches the article and fills the\nmissing export set.\n\nTo regenerate one audio file, delete only that `.m4a` or `.mp3` file and rerun\nthe same `download` command. This is the narrowest repair path when the audio\nis corrupt or when you changed the configured system voice and want to rebuild\nonly one article. Use `--force` when you want to refetch, rewrite exports, and\nregenerate audio for every selected article:\n\n```bash\nget-my-domino download --issue 2026-04 --article 1 --force\n```\n\nLong operations show friendly status messages. In an interactive terminal, the\ncurrent step uses an animated indeterminate progress bar and then turns into a\ncheck mark when complete; in logs or redirected output, the CLI prints plain\nstart and done lines. Download results are summarized as one compact row per\narticle with export status, audio status, and total elapsed time:\n\n```text\narticle                                                    export     audio      time\n✓ Cosa fare a Teheran quando sei morto                     reused     reused     00:00\n✓ E la Casa Bianca restò sola                              written    generated  01:05\n```\n\nWhen terminal colors are available, status labels use restrained color for\nquick scanning. Use `--verbose` to also print the article export directory\nafter each row. The article summary includes one total elapsed time.\nmacOS `say` does not expose true synthesis percentage, so audio generation\ndoes not show a percentage or ETA. By default, long article text is split into\nsmall paragraph-aware chunks. The CLI synthesizes up to\n`audio_chunk_concurrency = 4` chunk AIFF files at a time, retries failed chunks\nwith `audio_chunk_retries`, joins the AIFF chunks into one temporary AIFF, and\nthen performs one final `afconvert` or `ffmpeg` conversion. This avoids long\nsingle `say` runs that can silently truncate Siri/neural output.\n\n`audio_timeout` stops a stuck `say`, `afconvert`, or `ffmpeg` command after\nthe configured number of seconds. `audio_stall_timeout` retries a chunk when\nthe temporary AIFF file stops growing. These values can be overridden per run:\n\n```bash\nget-my-domino download --issue 2026-04 --article 1 --audio-timeout 1200\nget-my-domino download --issue 2026-04 --article 1 --audio-jobs 4\nget-my-domino download --issue 2026-04 --article 1 --no-audio-chunks\n```\n\nPress `Ctrl-C` to stop a run cleanly; the CLI stops active speech synthesis,\nremoves the temporary `.aiff` file, and prints `interrupted` without a Python\ntraceback. The chunked synthesis step uses a user-level lock across concurrent\n`get-my-domino` processes so two separate CLI runs do not overlap large Siri\nspeech jobs. If one article audio conversion fails during a multi-article\ndownload or sync, remaining articles continue; the command prints an audio\nfailure summary at the end and exits non-zero.\n\nSet `export_formats` in the config, or repeat `--format` on `download`,\n`sync-magazine`, or `sync-feed`, to choose article export formats. The default\nis `[\"html\", \"txt\"]`; `rtf` is available but not generated unless requested.\n\n```bash\nget-my-domino download --issue 2026-04 --article 1 --format txt --format rtf\n```\n\nScan all issues, skip articles already in the manifest, and download new text:\n\n```bash\nget-my-domino sync-magazine\nget-my-domino sync-magazine --audio\nget-my-domino sync-magazine --audio --force\n```\n\nMagazine articles are saved under:\n\n```text\noutput_dir/\n├── audiobooks/\n└── library/\n    └── rivista/\n        └── 2026-04-guaio-persiano/\n            ├── metadata.json\n            ├── issue.json\n            ├── 01-l-editoriale/\n            │   └── 01-cosa-fare-a-teheran-quando-sei-morto/\n            │       ├── 01-cosa-fare-a-teheran-quando-sei-morto.html\n            │       ├── 01-cosa-fare-a-teheran-quando-sei-morto.txt\n            │       ├── 01-cosa-fare-a-teheran-quando-sei-morto.m4a\n            │       └── metadata.json\n            └── 02-la-guerra-va-male/\n                └── 02-e-la-casa-bianca-rest-sola/\n```\n\nIf `audiobook_output_dir` is configured, replace the top-level `audiobooks/`\nentry above with that external path.\n\nSingle-article audio now lives beside the article exports and metadata, so the\nold top-level `output_dir/audio/` tree is no longer the canonical layout.\n\nScan `La settimana di Domino` and save it under\n`output_dir/library/feed_folder_name`:\n\n```bash\nget-my-domino sync-feed\nget-my-domino sync-feed --audio --pages 3\nget-my-domino sync-feed --audio --force\n```\n\n`sync-feed --audio` and `sync-magazine --audio` also inspect articles already\npresent in the local manifest and generate missing audio from local exports.\nUse `--force` on sync commands only when you want to refetch/rewrite existing\narticle exports and regenerate their audio.\n\nFeed articles are saved with date-first names, for example:\n\n```text\noutput_dir/\n└── library/\n    └── la-settimana-di-domino/\n        └── 2026-04-24-usa-e-globalizzazione-guerra-in-medio-oriente/\n            ├── 2026-04-24-usa-e-globalizzazione-guerra-in-medio-oriente.html\n            ├── 2026-04-24-usa-e-globalizzazione-guerra-in-medio-oriente.txt\n            ├── 2026-04-24-usa-e-globalizzazione-guerra-in-medio-oriente.m4a\n            └── metadata.json\n```\n\nCreate audio from downloaded text files:\n\n```bash\nget-my-domino speak\nget-my-domino speak --audio-format mp3\nget-my-domino sync-magazine --audio --audio-format m4a\nget-my-domino download --issue 2026-04 --article 1 --audio-timeout 1200\nget-my-domino download --issue 2026-04 --all --audio-jobs 3\n```\n\nOptionally create speech-ready text before audio synthesis:\n\n```bash\nget-my-domino speech-normalize /path/to/article-dir --diff\nget-my-domino download --issue 2026-04 --article 1 --audio --speech-normalize\nget-my-domino sync-magazine --audio --speech-normalize\n```\n\nSpeech normalization is disabled by default because it invokes an external AI\nagent and may send article text to the service configured for that agent. The\ncurrent implemented backend is `speech_normalize_agent = \"codex\"`, which calls\nthe local `codex exec` CLI through `speech_normalize_command`. The config is\nstructured for future backends (`codex-cloud`, `github-cli`, `github-copilot`,\nand `jelly`), but those agents currently fail with a clear “not implemented”\nmessage. When enabled, the CLI writes `\u003carticle-basename\u003e.speech.txt` beside\nthe original `.txt` export and sends that speech-ready file to macOS `say`.\nThe original `.txt` remains unchanged. The prompt also allows conservative\nItalian punctuation fixes for TTS prosody, for example closing clear incisi or\nadding a comma at a real clause boundary, but forbids punctuation changes that\nwould alter syntax or meaning.\n\nAfter a successful normalization run, the article `metadata.json` gains a\n`speech_normalization` block with the prompt path, prompt version, prompt hash,\nnormalizer command, model, source-text hash, output path, and normalization\ntimestamp. Existing `.speech.txt` files are reused only when that metadata\nstill matches the current source text and prompt template, so changing the\nconfigured prompt file invalidates stale speech sidecars automatically.\n\nThe default prompt is installed as a user-editable file at\n`~/.config/get-my-domino/speech-normalize-codex.txt`. Set\n`speech_normalize_prompt_path` or pass `--speech-normalize-prompt` to use a\ndifferent prompt template. The template must keep the placeholders\n`{output_path}`, `{source_text_path}`, and `{normalized_text}`.\n\nModel selection for Codex speech normalization already works at all three\nlayers:\n\n- config file with `speech_normalize_model = \"gpt-5.3-codex-spark\"`\n- command line with `--speech-normalize-model gpt-5.3-codex-spark`\n- direct Codex invocation, because the CLI passes `-m \u003cmodel\u003e` through to\n  `codex exec`\n\nExamples:\n\n```bash\nget-my-domino speak /path/to/article-dir \\\n  --speech-normalize \\\n  --speech-normalize-model gpt-5.3-codex-spark\n\nget-my-domino sync-magazine \\\n  --audio \\\n  --speech-normalize \\\n  --speech-normalize-model gpt-5.3-codex-spark\n```\n\nIf you want Spark as your default for speech normalization, set:\n\n```toml\nspeech_normalize_model = \"gpt-5.3-codex-spark\"\n```\n\nCurrent OpenAI model availability for this setting, verified against official\nOpenAI docs on April 29, 2026:\n\n- Recommended default here, when your account has access:\n  `gpt-5.3-codex-spark`\n- General Codex model examples documented by OpenAI today:\n  `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`,\n  `gpt-5.3-codex-spark`\n- General API model IDs documented by OpenAI today:\n  `gpt-5.5`, `gpt-5.5-pro`, `gpt-5.4`, `gpt-5.4-pro`, `gpt-5.4-mini`,\n  `gpt-5.4-nano`, `gpt-5.3-codex`\n\nOpenAI currently documents the following plan-level availability:\n\n- ChatGPT subscriptions:\n  all users have GPT-5.3 by default; GPT-5.5 is rolling out in ChatGPT to\n  Plus, Pro, Business, and Enterprise; GPT-5.5 Pro is for Pro, Business, and\n  Enterprise; GPT-5.4 Thinking remains the earlier paid-tier reasoning model;\n  GPT-5.4 mini is available to Free and Go users through Thinking and as a\n  fallback for paid GPT-5.4 Thinking users\n- Codex subscriptions:\n  GPT-5.5 is available in Codex for Plus, Pro, Business, Enterprise, Edu, and\n  Go plans; the Codex rate card also currently lists GPT-5.4, GPT-5.4-Mini,\n  GPT-5.3-Codex, and GPT-5.3-Codex-Spark as a research preview\n- API:\n  the current primary GPT family listed by OpenAI is GPT-5.5, GPT-5.5 Pro,\n  GPT-5.4, GPT-5.4 Pro, GPT-5.4 mini, GPT-5.4 nano, plus GPT-5.3-Codex for\n  coding workflows\n\nImportant availability note: `gpt-5.3-codex-spark` launched on February 12,\n2026 as a research preview in Codex for ChatGPT Pro users, and OpenAI’s Codex\nrate card still describes it as a research preview with non-final credit rates.\nOpenAI also says Codex-Spark is in the API only for a small set of design\npartners, so you should not assume general API access to Spark.\n\nFor `get-my-domino`, the practical rule is:\n\n- if you want the cheapest Codex option and your Codex account exposes it, set\n  `speech_normalize_model = \"gpt-5.3-codex-spark\"`\n- if Spark is not available on your account, fall back to\n  `gpt-5.4-mini` or `gpt-5.3-codex` depending on whether you prefer lower cost\n  or stronger coding-oriented behavior\n\nSpeech normalization config:\n\n| Key | Meaning |\n| --- | --- |\n| `speech_normalize_auto` | Run AI speech normalization automatically before every audio generation. Keep `false` to enable it only with `--speech-normalize`. |\n| `speech_normalize_agent` | AI backend name. Only `codex` is implemented; other configured backend names are reserved for future support. |\n| `speech_normalize_command` | Executable used for the selected agent, usually `codex`; set a full path if the command is not on `PATH`. |\n| `speech_normalize_model` | Optional model name passed to the agent. Empty means the agent CLI uses its default model. |\n| `speech_normalize_timeout` | Maximum seconds allowed for one article normalization run before stopping it. |\n| `speech_normalize_force` | Regenerate `.speech.txt` even when a reusable speech file already exists. |\n| `speech_normalize_fallback` | If normalization fails, continue with the original `.txt` instead of stopping before audio. |\n| `speech_normalize_prompt_path` | User-editable prompt template used by the normalizer. |\n\nList the exact voice names that macOS `say` accepts:\n\n```bash\nget-my-domino voices\nget-my-domino voices --all\n```\n\nSet `audio_auto = true` in the config to generate audio automatically whenever\n`download`, `sync-magazine`, or `sync-feed` saves new articles. Use\n`--no-audio` for one run without synthesis. `audio_format = \"mp4a\"` is accepted\nas an alias for `m4a`.\n\nSet `audiobook_auto = true` in the config to generate issue-level `.m4b`\naudiobooks automatically for `sync-magazine` and `download --issue YYYY-NN --all`.\nThis does not affect `sync-feed`, which only produces article-level audio.\n\nLeave `siri_voice` empty to use the current macOS system voice. This is the\nonly supported way to use Siri/neural voices from this CLI: the app calls `say`\nwithout `-v`, matching plain `say \"ciao\"` behavior, and macOS delegates speech\nto the configured system voice.\n\nOn macOS, open System Settings \u003e Accessibility \u003e Spoken Content, set Language\nto `Italian (Italy)` or your preferred language, choose System Voice, click the\ninformation button next to the voice selector, search for the Siri voice you\nwant, select it, and close Settings.\n\nSet `siri_voice` only when you want to force an exact voice name accepted by\n`say -v '?'`. Those are the older `NSSpeechSynthesizer` voices, such as\n`Alice`; Siri/neural voices are not reliably selectable through `-v`. Article\ntext used for synthesis starts with the issue title when known, then the\narticle title, then `di ...` when an author is detected; the source URL is kept\nonly in `metadata.json`. If `siri_voice` names a voice that `say` would\nsilently ignore, the CLI now stops with an explicit error instead of creating\naudio with a fallback voice.\n\nLegacy aliases remain available: `sync` for `sync-magazine`, `weekly` for\n`feed`, and `sync-weekly` for `sync-feed`.\n\nInspect the resolved configuration:\n\n```bash\nget-my-domino info\nget-my-domino --config ./config.toml info --json\n```\n\n## Project Layout\n\n```text\n.\n├── AGENTS.md\n├── CHANGELOG.md\n├── Makefile\n├── README.md\n├── TODO.md\n├── config.full.toml\n├── config.toml.example\n├── pyproject.toml\n├── scripts/\n│   └── install.sh\n├── src/\n│   └── get_my_domino/\n│       ├── __init__.py\n│       ├── __main__.py\n│       ├── audio.py\n│       ├── browser_auth.py\n│       ├── cli.py\n│       ├── config.py\n│       ├── extract.py\n│       ├── models.py\n│       ├── session_store.py\n│       ├── storage.py\n│       └── web.py\n└── tests/\n    └── test_cli.py\n```\n\n## Development\n\nSync the environment and run the default quality gate:\n\n```bash\nmake check\n```\n\nCommon commands:\n\n```bash\nmake sync\nmake test\nmake lint\nmake run\n```\n\nWhen the Domino site layout changes, start with the maintainer note at\n[docs/domino-site-structure.md](docs/domino-site-structure.md).\nIt documents the current crawl entry points, issue/feed selectors, article\nextraction assumptions, and which config keys usually let you patch the scraper\nwithout changing code.\n\n## Release Notes\n\nBefore tagging a release:\n\n1. update the version in `pyproject.toml`\n2. update `src/get_my_domino/__init__.py`\n3. add release notes to `CHANGELOG.md`\n4. verify `make check`\n\n## License\n\nThis project is released under the MIT License. See [LICENSE](LICENSE).\nRoot storage is configurable in two ways:\n\n- set `output_dir` directly when you want one explicit absolute path\n- or set `output_parent_dir` plus `collection_dir_name`\n\nWhen `output_dir` is omitted, the default root folder becomes:\n\n```text\noutput_parent_dir/collection_dir_name\n```\n\n`collection_dir_name` is intended to be a filesystem slug. By default it is\nderived from `magazine_title` in lowercase with underscores, for example\n`\"Rivista Domino\"` -\u003e `rivista_domino`.\n\nAudiobooks use a separate destination resolver:\n\n- by default: `output_dir/audiobooks`\n- if set explicitly: `audiobook_output_dir`\n\nThe HTTP `User-Agent` header is versioned automatically from the installed app.\nIt is no longer a config key because users should not need to update it by hand\nwhen upgrading the CLI.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomc%2Fget-my-domino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcomc%2Fget-my-domino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomc%2Fget-my-domino/lists"}