audiobook_forge/cli/
commands.rs

1//! CLI commands and arguments
2
3use clap::{Parser, Subcommand, Args};
4use std::path::PathBuf;
5
6use crate::VERSION;
7
8/// Audiobook Forge - Convert audiobook directories to M4B format
9#[derive(Parser)]
10#[command(name = "audiobook-forge")]
11#[command(version = VERSION)]
12#[command(about = "Convert audiobook directories to M4B format with chapters and metadata")]
13#[command(long_about = "
14Audiobook Forge is a CLI tool that converts audiobook directories containing
15MP3 files into high-quality M4B audiobook files with proper chapters and metadata.
16
17Features:
18• Automatic quality detection and preservation
19• Smart chapter generation from multiple sources
20• Parallel batch processing
21• Metadata extraction and enhancement
22• Cover art embedding
23")]
24pub struct Cli {
25    #[command(subcommand)]
26    pub command: Commands,
27
28    /// Enable verbose output
29    #[arg(global = true, short, long)]
30    pub verbose: bool,
31}
32
33#[derive(Subcommand)]
34pub enum Commands {
35    /// Process audiobooks and convert to M4B
36    Build(BuildArgs),
37
38    /// Organize audiobooks into M4B and To_Convert folders
39    Organize(OrganizeArgs),
40
41    /// Manage configuration
42    #[command(subcommand)]
43    Config(ConfigCommands),
44
45    /// Fetch and manage Audible metadata
46    #[command(subcommand)]
47    Metadata(MetadataCommands),
48
49    /// Interactive metadata matching for M4B files
50    Match(MatchArgs),
51
52    /// Check system dependencies
53    Check,
54
55    /// Show version information
56    Version,
57}
58
59#[derive(Args)]
60pub struct BuildArgs {
61    /// Root directory containing audiobook folders
62    #[arg(short, long)]
63    pub root: Option<PathBuf>,
64
65    /// Output directory (defaults to same as root)
66    #[arg(short, long)]
67    pub out: Option<PathBuf>,
68
69    /// Number of parallel workers (1-8)
70    #[arg(short = 'j', long, value_parser = clap::value_parser!(u8).range(1..=8))]
71    pub parallel: Option<u8>,
72
73    /// Skip folders with existing M4B files
74    #[arg(long)]
75    pub skip_existing: Option<bool>,
76
77    /// Force reprocessing (overwrite existing)
78    #[arg(long)]
79    pub force: bool,
80
81    /// Merge multiple M4B files even without detected naming pattern
82    #[arg(long)]
83    pub merge_m4b: bool,
84
85    /// Normalize existing M4B files (fix metadata)
86    #[arg(long)]
87    pub normalize: bool,
88
89    /// Dry run (analyze without creating files)
90    #[arg(long)]
91    pub dry_run: bool,
92
93    /// Prefer stereo over mono
94    #[arg(long)]
95    pub prefer_stereo: Option<bool>,
96
97    /// Chapter source priority
98    #[arg(long, value_parser = ["auto", "files", "cue", "id3", "none"])]
99    pub chapter_source: Option<String>,
100
101    /// Cover art filenames (comma-separated)
102    #[arg(long)]
103    pub cover_names: Option<String>,
104
105    /// Default language for metadata
106    #[arg(long)]
107    pub language: Option<String>,
108
109    /// Keep temporary files for debugging
110    #[arg(long)]
111    pub keep_temp: bool,
112
113    /// Delete original files after conversion
114    #[arg(long)]
115    pub delete_originals: bool,
116
117    /// Quality preset for output audio
118    #[arg(long, value_parser = ["low", "medium", "high", "ultra", "maximum", "source"])]
119    pub quality: Option<String>,
120
121    /// AAC encoder to use (auto, aac_at, libfdk_aac, aac)
122    #[arg(long)]
123    pub aac_encoder: Option<String>,
124
125    /// DEPRECATED: Use --aac-encoder instead
126    #[arg(long, hide = true)]
127    pub use_apple_silicon_encoder: Option<bool>,
128
129    /// Fetch metadata from Audible during build
130    #[arg(long)]
131    pub fetch_audible: bool,
132
133    /// Audible region (us, uk, ca, au, fr, de, jp, it, in, es)
134    #[arg(long)]
135    pub audible_region: Option<String>,
136
137    /// Auto-match books with Audible by folder name
138    #[arg(long)]
139    pub audible_auto_match: bool,
140
141    /// Configuration file path
142    #[arg(long)]
143    pub config: Option<PathBuf>,
144}
145
146#[derive(Args)]
147pub struct OrganizeArgs {
148    /// Root directory to organize
149    #[arg(short, long)]
150    pub root: Option<PathBuf>,
151
152    /// Dry run (show what would be done)
153    #[arg(long)]
154    pub dry_run: bool,
155
156    /// Configuration file path
157    #[arg(long)]
158    pub config: Option<PathBuf>,
159}
160
161#[derive(Subcommand)]
162pub enum ConfigCommands {
163    /// Initialize config file with defaults
164    Init {
165        /// Overwrite existing config file
166        #[arg(long)]
167        force: bool,
168    },
169
170    /// Show current configuration
171    Show {
172        /// Configuration file path
173        #[arg(long)]
174        config: Option<PathBuf>,
175    },
176
177    /// Validate configuration file
178    Validate {
179        /// Configuration file path
180        #[arg(long)]
181        config: Option<PathBuf>,
182    },
183
184    /// Show config file path
185    Path,
186
187    /// Edit config file in default editor
188    Edit,
189}
190
191#[derive(Subcommand)]
192pub enum MetadataCommands {
193    /// Fetch metadata from Audible
194    Fetch {
195        /// Audible ASIN (B002V5D7RU format)
196        #[arg(long)]
197        asin: Option<String>,
198
199        /// Search by title
200        #[arg(long)]
201        title: Option<String>,
202
203        /// Search by author
204        #[arg(long)]
205        author: Option<String>,
206
207        /// Audible region (us, uk, ca, au, fr, de, jp, it, in, es)
208        #[arg(long, default_value = "us")]
209        region: String,
210
211        /// Save metadata to JSON file
212        #[arg(long)]
213        output: Option<PathBuf>,
214    },
215
216    /// Enrich M4B file with Audible metadata
217    Enrich {
218        /// M4B file to enrich
219        #[arg(long)]
220        file: PathBuf,
221
222        /// Audible ASIN
223        #[arg(long)]
224        asin: Option<String>,
225
226        /// Auto-detect ASIN from filename
227        #[arg(long)]
228        auto_detect: bool,
229
230        /// Audible region
231        #[arg(long, default_value = "us")]
232        region: String,
233
234        /// Update chapters from file (text/EPUB)
235        #[arg(long)]
236        chapters: Option<PathBuf>,
237
238        /// Fetch chapters from Audnex API by ASIN
239        #[arg(long, conflicts_with = "chapters")]
240        chapters_asin: Option<String>,
241
242        /// Only update chapters, skip metadata enrichment
243        #[arg(long)]
244        update_chapters_only: bool,
245
246        /// Chapter merge strategy (keep-timestamps, replace-all, skip-on-mismatch, interactive)
247        #[arg(long, default_value = "interactive")]
248        merge_strategy: String,
249    },
250}
251
252/// Arguments for the match command
253#[derive(Args)]
254pub struct MatchArgs {
255    /// M4B file to match
256    #[arg(long, short = 'f', conflicts_with = "dir")]
257    pub file: Option<PathBuf>,
258
259    /// Directory of M4B files
260    #[arg(long, short = 'd', conflicts_with = "file")]
261    pub dir: Option<PathBuf>,
262
263    /// Manual title override
264    #[arg(long)]
265    pub title: Option<String>,
266
267    /// Manual author override
268    #[arg(long)]
269    pub author: Option<String>,
270
271    /// Auto mode (non-interactive, select best match)
272    #[arg(long)]
273    pub auto: bool,
274
275    /// Audible region
276    #[arg(long, default_value = "us")]
277    pub region: String,
278
279    /// Keep existing cover art instead of downloading
280    #[arg(long)]
281    pub keep_cover: bool,
282
283    /// Dry run (show matches but don't apply)
284    #[arg(long)]
285    pub dry_run: bool,
286}