consider file size when caching ffprobe results.

This commit is contained in:
arabcoders 2025-08-23 20:21:16 +03:00
parent c4b8121c3d
commit d9ec7733a1

View file

@ -5,35 +5,19 @@ Python wrapper for ffprobe command line tool. ffprobe must exist in the path.
import asyncio import asyncio
import functools import functools
import json import json
import logging
import operator import operator
import os import os
import subprocess import subprocess # qa: ignore
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING
import anyio import anyio
if TYPE_CHECKING:
from app.library.cache import Cache
# parameter-less decorator LOG: logging.Logger = logging.getLogger(__name__)
def async_lru_cache_decorator(async_function):
@functools.lru_cache
def cached_async_function(*args, **kwargs):
coroutine = async_function(*args, **kwargs)
return asyncio.ensure_future(coroutine)
return cached_async_function
# decorator with options
def async_lru_cache(*lru_cache_args, **lru_cache_kwargs):
def async_lru_cache_decorator(async_function):
@functools.lru_cache(*lru_cache_args, **lru_cache_kwargs)
def cached_async_function(*args, **kwargs):
coroutine = async_function(*args, **kwargs)
return asyncio.ensure_future(coroutine)
return cached_async_function
return async_lru_cache_decorator
class FFProbeError(Exception): class FFProbeError(Exception):
@ -254,7 +238,6 @@ class FFProbeResult:
} }
@async_lru_cache(maxsize=512)
async def ffprobe(file: str) -> FFProbeResult: async def ffprobe(file: str) -> FFProbeResult:
""" """
Run ffprobe on a file and return the parsed data as a dictionary. Run ffprobe on a file and return the parsed data as a dictionary.
@ -266,6 +249,21 @@ async def ffprobe(file: str) -> FFProbeResult:
dict: A dictionary containing the parsed data. dict: A dictionary containing the parsed data.
""" """
from app.library.Services import Services
f = Path(file)
if not f.exists():
msg = f"No such media file '{file}'."
raise OSError(msg)
cache: Cache | None = Services.get_instance().get("cache")
cache_key: str = f"ffprobe:{f!s}:{f.stat().st_size}"
if cache and (cached := cache.get(cache_key)):
LOG.debug(f"ffprobe cache hit for '{cache_key}'")
return cached
try: try:
async with await anyio.open_file(os.devnull, "w") as tempf: async with await anyio.open_file(os.devnull, "w") as tempf:
await asyncio.create_subprocess_exec( await asyncio.create_subprocess_exec(
@ -279,11 +277,7 @@ async def ffprobe(file: str) -> FFProbeResult:
msg = "ffprobe not found." msg = "ffprobe not found."
raise OSError(msg) from e raise OSError(msg) from e
if not Path(file).exists(): args = ["-v", "quiet", "-of", "json", "-show_streams", "-show_format", str(f)]
msg = f"No such media file '{file}'."
raise OSError(msg)
args = ["-v", "quiet", "-of", "json", "-show_streams", "-show_format", file]
p = await asyncio.create_subprocess_exec( p = await asyncio.create_subprocess_exec(
"ffprobe", "ffprobe",
@ -316,4 +310,7 @@ async def ffprobe(file: str) -> FFProbeResult:
elif stream.is_attachment(): elif stream.is_attachment():
result.attachment.append(stream) result.attachment.append(stream)
if cache:
cache.set(cache_key, result, ttl=300)
return result return result