diff --git a/pytorrent/services/rtorrent/files.py b/pytorrent/services/rtorrent/files.py index 8635103..8653c97 100644 --- a/pytorrent/services/rtorrent/files.py +++ b/pytorrent/services/rtorrent/files.py @@ -225,6 +225,34 @@ def _media_info_summary(fields: list[dict]) -> dict: } +def _media_info_hachoir_imports(): + # Note: Import is checked before reading the media sample so dependency problems fail fast and clearly. + import sys + + try: + from hachoir.metadata import extractMetadata + from hachoir.parser import createParser + return createParser, extractMetadata + except ModuleNotFoundError as exc: + missing = str(getattr(exc, "name", "") or "hachoir") + if missing.split(".", 1)[0] == "hachoir": + raise RuntimeError( + "Python package 'hachoir' is not importable in the application runtime. " + "Install it inside the pyTorrent virtualenv and restart the service: " + "/opt/pyTorrent/venv/bin/pip install -r /opt/pyTorrent/requirements.txt && systemctl restart pytorrent. " + f"Runtime: {sys.executable}." + ) from exc + raise RuntimeError( + f"hachoir is installed, but one of its Python dependencies is missing: {missing}. " + f"Runtime: {sys.executable}." + ) from exc + except Exception as exc: + raise RuntimeError( + "hachoir was found, but failed during import. " + f"Runtime: {sys.executable}. Details: {exc}" + ) from exc + + def torrent_file_media_info(profile: dict, torrent_hash: str, index: int, max_bytes: int = _MEDIA_INFO_SAMPLE_BYTES) -> dict: # Note: This endpoint is MediaInfo-like and intentionally avoids external binaries such as mediainfo, ffprobe or ffmpeg. selected, remote_path = _torrent_file_remote_path(profile, torrent_hash, index) @@ -246,6 +274,8 @@ def torrent_file_media_info(profile: dict, torrent_hash: str, index: int, max_by result["error"] = "This file extension is not supported by the built-in media info parser." return result + createParser, extractMetadata = _media_info_hachoir_imports() + err = remote_file_readability_error(profile, remote_path) if int(profile.get("is_remote") or 0) else None if err: raise RuntimeError(err) @@ -253,12 +283,6 @@ def torrent_file_media_info(profile: dict, torrent_hash: str, index: int, max_by tmp_path = None try: tmp_path, written = _media_info_temp_sample(profile, remote_path, max(1024 * 1024, int(max_bytes))) - try: - from hachoir.metadata import extractMetadata - from hachoir.parser import createParser - except Exception as exc: - raise RuntimeError("Python package 'hachoir' is required for media info. Install requirements.txt again.") from exc - parser = createParser(tmp_path, real_filename=LocalPath(name).name) if parser is None: result.update({"sample_bytes": written, "error": "hachoir could not detect this media container."})