1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! `youtube-legend-cli` — a non-interactive Rust CLI for downloading
//! YouTube subtitles through third-party providers, using a native Unix
//! `stdin`/`stdout` interface.
//!
//! # Quickstart
//!
//! The fastest way to drive the crate is to construct a [`Cli`] with
//! `Cli::parse()` (as `main.rs` does) and forward it to [`run`]:
//!
//! ```no_run
//! use clap::Parser;
//! use youtube_legend_cli::{run, Cli};
//!
//! # async fn demo() -> Result<(), Box<dyn std::error::Error>> {
//! let cli = Cli::parse_from(["demo", "https://youtu.be/dQw4w9WgXcQ"]);
//! let exit_code = run(cli).await?;
//! # let _ = exit_code;
//! # Ok(()) }
//! ```
//!
//! # Architecture
//!
//! - [`cli`] — `clap`-derived argument parser and the [`Cli`] struct.
//! - [`commands`] — top-level dispatch between single-URL extract and
//! batch mode.
//! - `provider` — the `Provider` trait and the two concrete
//! implementations, plus the `ProviderChain` that walks
//! them with one-request-per-second throttling.
//! - [`parse`] — SRT text extraction and YouTube URL/video-id parsing.
//! - [`cache`] — TTL-based local file cache keyed on
//! `(video_id, language, format)`.
//! - [`retry`] — exponential backoff helper and in-memory circuit
//! breaker.
//! - [`io`] — `stdin`/`stdout`/TTY helpers.
//! - [`error`] — the [`error::AppError`] enum and the process exit-code
//! table.
//! - [`logging`] — initialiser for the global `tracing` subscriber.
//! - `text` — Unicode NFC normalisation.
//! - [`crypto`] — AES-256-CBC + PBKDF2 used by provider-B's request
//! signing path.
//!
//! # Stream contracts
//!
//! - `stdout` is reserved exclusively for the subtitle body (or the
//! `--json` envelope).
//! - `stderr` is reserved exclusively for logs, progress, and human
//! error messages.
//! - `stdin` accepts a single URL, a batch of one-URL-per-line, or
//! `--batch` flag input.
//!
//! # Cancellation
//!
//! `SIGINT` and `SIGTERM` are wired through `tokio_util::CancellationToken`
//! in `main.rs`. In-flight requests are allowed to complete; the process
//! exits with code 130. The async API exposed by this crate is
//! cancellation-safe at every public await point.
//!
//! # Minimum supported Rust version (MSRV)
//!
//! `1.96.0` — see `rust-toolchain.toml` and `Cargo.toml`'s
//! `rust-version` field.
/// Cache subsystem: TTL-keyed local files for fetched subtitles.
/// Command-line argument parser (clap derive).
/// Top-level command dispatch: extract one URL, or batch many.
/// AES-256-CBC + PBKDF2 token encryption used by provider-B's request
/// signing path. Do not use these primitives for new code outside the
/// provider-B compatibility layer.
/// Error types and process exit-code table.
/// Stdin / stdout / TTY helpers.
/// `tracing` subscriber initialiser.
/// SRT text extraction and YouTube URL / video-id parsing.
/// Provider trait, two concrete implementations, and the provider chain
/// with throttling.
/// Exponential-backoff retry helper and in-memory circuit breaker.
/// Internal constants for provider hosts, paths, cookies, and user
/// agent. Gitignored; never published. Consumed by `provider_a`,
/// `provider_b`, and the `snapshot` binary.
pub
/// Unicode NFC normalisation.
pub
use ExitCode;
pub use ;
pub use ;
/// Top-level entry point that wires the parsed [`Cli`] to the command
/// dispatch. See `main.rs` for the binary that calls this.
///
/// # Errors
///
/// - [`error::AppError::InvalidUsage`] when the CLI was validated and
/// found to have a bad combination of flags.
/// - Any of the provider / network / cache / IO errors described in
/// [`error::AppError`].
///
/// # Cancel safety
///
/// This future is cancel-safe: dropping it before completion does not
/// leak cache writes or partial fetches; the in-flight HTTP request is
/// aborted at the next `await` point.
pub async