|
47 | 47 |
|
48 | 48 |
|
49 | 49 | def get_options_dict() -> dict[str, FFMpegOption]:
|
| 50 | + """ |
| 51 | + Load and index FFmpeg options from the cache. |
| 52 | +
|
| 53 | + Returns: |
| 54 | + Dictionary mapping option names to their FFMpegOption definitions |
| 55 | + """ |
50 | 56 | options = load(list[FFMpegOption], "options")
|
51 | 57 | return {option.name: option for option in options}
|
52 | 58 |
|
53 | 59 |
|
54 | 60 | def get_filter_dict() -> dict[str, FFMpegFilter]:
|
| 61 | + """ |
| 62 | + Load and index FFmpeg filters from the cache. |
| 63 | +
|
| 64 | + Returns: |
| 65 | + Dictionary mapping filter names to their FFMpegFilter definitions |
| 66 | + """ |
55 | 67 | filters = load(list[FFMpegFilter], "filters")
|
56 | 68 | return {filter.name: filter for filter in filters}
|
57 | 69 |
|
@@ -99,6 +111,24 @@ def parse_options(tokens: list[str]) -> dict[str, list[str | None | bool]]:
|
99 | 111 | def parse_stream_selector(
|
100 | 112 | selector: str, mapping: Mapping[str, FilterableStream]
|
101 | 113 | ) -> FilterableStream:
|
| 114 | + """ |
| 115 | + Parse a stream selector string and return the corresponding stream. |
| 116 | +
|
| 117 | + This function handles FFmpeg's stream selector syntax: |
| 118 | + - Simple selectors: "[0]" (first input) |
| 119 | + - Type selectors: "[0:v]" (first input, video stream) |
| 120 | + - Index selectors: "[0:v:0]" (first input, first video stream) |
| 121 | +
|
| 122 | + Args: |
| 123 | + selector: Stream selector string to parse |
| 124 | + mapping: Dictionary of available streams |
| 125 | +
|
| 126 | + Returns: |
| 127 | + The selected FilterableStream |
| 128 | +
|
| 129 | + Raises: |
| 130 | + AssertionError: If the stream label is not found in the mapping |
| 131 | + """ |
102 | 132 | selector = selector.strip("[]")
|
103 | 133 |
|
104 | 134 | if ":" in selector:
|
@@ -126,19 +156,48 @@ def parse_stream_selector(
|
126 | 156 | return stream
|
127 | 157 |
|
128 | 158 |
|
| 159 | +def _is_filename(token: str) -> bool: |
| 160 | + """ |
| 161 | + Check if a token is a filename. |
| 162 | +
|
| 163 | + Args: |
| 164 | + token: The token to check |
| 165 | +
|
| 166 | + Returns: |
| 167 | + True if the token is a filename, False otherwise |
| 168 | + """ |
| 169 | + # not start with - and has ext |
| 170 | + return not token.startswith("-") and len(token.split(".")) > 1 |
| 171 | + |
| 172 | + |
129 | 173 | def parse_output(
|
130 | 174 | source: list[str],
|
131 | 175 | in_streams: Mapping[str, FilterableStream],
|
132 | 176 | ffmpeg_options: dict[str, FFMpegOption],
|
133 | 177 | ) -> list[OutputStream]:
|
| 178 | + """ |
| 179 | + Parse output file specifications and their options. |
| 180 | +
|
| 181 | + This function processes the output portion of an FFmpeg command line, |
| 182 | + handling output file paths, stream mapping, and output-specific options. |
| 183 | +
|
| 184 | + Args: |
| 185 | + source: List of command-line tokens for output specifications |
| 186 | + in_streams: Dictionary of available input streams |
| 187 | + ffmpeg_options: Dictionary of valid FFmpeg options |
| 188 | +
|
| 189 | + Returns: |
| 190 | + List of OutputStream objects representing the output specifications |
| 191 | + """ |
134 | 192 | tokens = source.copy()
|
135 | 193 |
|
136 | 194 | export: list[OutputStream] = []
|
137 |
| - |
138 | 195 | buffer: list[str] = []
|
| 196 | + |
139 | 197 | while tokens:
|
140 | 198 | token = tokens.pop(0)
|
141 |
| - if token.startswith("-") or len(buffer) % 2 == 1: |
| 199 | + |
| 200 | + if not _is_filename(token): |
142 | 201 | buffer.append(token)
|
143 | 202 | continue
|
144 | 203 |
|
@@ -183,6 +242,19 @@ def parse_output(
|
183 | 242 | def parse_input(
|
184 | 243 | tokens: list[str], ffmpeg_options: dict[str, FFMpegOption]
|
185 | 244 | ) -> dict[str, FilterableStream]:
|
| 245 | + """ |
| 246 | + Parse input file specifications and their options. |
| 247 | +
|
| 248 | + This function processes the input portion of an FFmpeg command line, |
| 249 | + handling input file paths and input-specific options. |
| 250 | +
|
| 251 | + Args: |
| 252 | + tokens: List of command-line tokens for input specifications |
| 253 | + ffmpeg_options: Dictionary of valid FFmpeg options |
| 254 | +
|
| 255 | + Returns: |
| 256 | + Dictionary mapping input indices to their FilterableStream objects |
| 257 | + """ |
186 | 258 | output: list[AVStream] = []
|
187 | 259 |
|
188 | 260 | while "-i" in tokens:
|
@@ -347,6 +419,29 @@ def parse_global(
|
347 | 419 |
|
348 | 420 |
|
349 | 421 | def parse(cli: str) -> Stream:
|
| 422 | + """ |
| 423 | + Parse a complete FFmpeg command line into a Stream object. |
| 424 | +
|
| 425 | + This function takes a full FFmpeg command line string and converts it into |
| 426 | + a Stream object representing the filter graph. It handles all components: |
| 427 | + - Global options |
| 428 | + - Input files and their options |
| 429 | + - Filter complex |
| 430 | + - Output files and their options |
| 431 | +
|
| 432 | + Args: |
| 433 | + cli: Complete FFmpeg command line string |
| 434 | +
|
| 435 | + Returns: |
| 436 | + Stream object representing the parsed command line |
| 437 | +
|
| 438 | + Example: |
| 439 | + ```python |
| 440 | + stream = parse( |
| 441 | + "ffmpeg -i input.mp4 -filter_complex '[0:v]scale=1280:720[v]' -map '[v]' output.mp4" |
| 442 | + ) |
| 443 | + ``` |
| 444 | + """ |
350 | 445 | # ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
|
351 | 446 | ffmpeg_options = get_options_dict()
|
352 | 447 | ffmpeg_filters = get_filter_dict()
|
|
0 commit comments