|
1 | 1 | #!/usr/bin/env python |
2 | 2 | """ |
3 | | -This file contains connections from Python to FFprobe, useful for extracting |
| 3 | +use Python with FFprobe to extract |
4 | 4 | JSON metadata from any kind of media file that FFprobe can read. |
5 | | -
|
6 | | -Moreover, this file demonstrates asynchronous and synchronous access to |
7 | | -thread-safe subprocesses |
8 | 5 | """ |
9 | 6 | import asyncio |
10 | 7 | import json |
| 8 | +import subprocess |
| 9 | +from typing import Dict, Iterable, AsyncGenerator |
11 | 10 | from pathlib import Path |
12 | 11 | import shutil |
13 | | -import sys |
14 | 12 |
|
15 | 13 | FFPROBE = shutil.which('ffprobe') |
16 | 14 | if not FFPROBE: |
17 | 15 | raise FileNotFoundError('FFPROBE not found') |
18 | 16 |
|
19 | | -TIMEOUT = 5.0 # 2.0 is too short for Windows |
| 17 | + |
| 18 | +async def get_meta(files: Iterable[Path]) -> AsyncGenerator[dict, None]: # mypy bug Dict[str, str] |
| 19 | + |
| 20 | + futures = [ffprobe(file) for file in files] |
| 21 | + for future in asyncio.as_completed(futures): |
| 22 | + try: |
| 23 | + meta = await future |
| 24 | + except asyncio.TimeoutError: |
| 25 | + continue |
| 26 | + |
| 27 | + yield meta |
20 | 28 |
|
21 | 29 |
|
22 | | -# %% Asynchronous FFprobe |
23 | | -async def ffprobe(filein: Path) -> dict: |
| 30 | +async def ffprobe(file: Path) -> Dict[str, str]: |
24 | 31 | """ get media metadata """ |
25 | | - assert isinstance(FFPROBE, str) |
26 | 32 |
|
27 | | - proc = await asyncio.create_subprocess_exec(*[FFPROBE, '-v', 'warning', |
| 33 | + proc = await asyncio.create_subprocess_exec(*[FFPROBE, '-loglevel', 'warning', |
28 | 34 | '-print_format', 'json', |
29 | 35 | '-show_streams', |
30 | 36 | '-show_format', |
31 | | - str(filein)], |
32 | | - stdout=asyncio.subprocess.PIPE, |
33 | | - stderr=asyncio.subprocess.PIPE) |
| 37 | + str(file)], |
| 38 | + stdout=asyncio.subprocess.PIPE) |
34 | 39 |
|
35 | | - stdout, stderr = await proc.communicate() |
| 40 | + stdout, _ = await proc.communicate() |
36 | 41 |
|
37 | 42 | return json.loads(stdout.decode('utf8')) |
38 | 43 |
|
39 | 44 |
|
40 | | -async def get_meta(filein: Path) -> dict: |
41 | | - try: |
42 | | - meta = await asyncio.wait_for(ffprobe(filein), timeout=TIMEOUT) |
43 | | - return meta |
44 | | - except asyncio.TimeoutError: |
45 | | - print('timeout ', filein, file=sys.stderr) |
46 | | - return {} |
| 45 | +def ffprobe_sync(file: Path) -> dict: # mypy bug Dict[str, str] |
| 46 | + """ get media metadata """ |
| 47 | + |
| 48 | + meta = subprocess.check_output([FFPROBE, '-v', 'warning', |
| 49 | + '-print_format', 'json', |
| 50 | + '-show_streams', |
| 51 | + '-show_format', |
| 52 | + str(file)], |
| 53 | + text=True) |
| 54 | + |
| 55 | + return json.loads(meta) |
0 commit comments