diff --git a/bazarr/config.py b/bazarr/config.py index 843e92823..c037b1e74 100644 --- a/bazarr/config.py +++ b/bazarr/config.py @@ -200,7 +200,8 @@ defaults = { 'embeddedsubtitles': { 'include_ass': 'True', 'include_srt': 'True', - 'hi_fallback': 'False' + 'hi_fallback': 'False', + 'mergerfs_mode': 'False' }, 'subsync': { 'use_subsync': 'False', diff --git a/bazarr/get_providers.py b/bazarr/get_providers.py index ad909cf4f..354c610f9 100644 --- a/bazarr/get_providers.py +++ b/bazarr/get_providers.py @@ -211,6 +211,7 @@ def get_providers_auth(): 'include_ass': settings.embeddedsubtitles.getboolean('include_ass'), 'include_srt': settings.embeddedsubtitles.getboolean('include_srt'), 'hi_fallback': settings.embeddedsubtitles.getboolean('hi_fallback'), + 'mergerfs_mode': settings.embeddedsubtitles.getboolean('mergerfs_mode'), 'cache_dir': os.path.join(args.config_dir, "cache"), 'ffprobe_path': _FFPROBE_BINARY, 'ffmpeg_path': _FFMPEG_BINARY, diff --git a/frontend/src/pages/Settings/Providers/list.ts b/frontend/src/pages/Settings/Providers/list.ts index aac5ce6bb..163ae0ff8 100644 --- a/frontend/src/pages/Settings/Providers/list.ts +++ b/frontend/src/pages/Settings/Providers/list.ts @@ -64,6 +64,7 @@ export const ProviderList: Readonly = [ include_srt: true, include_ass: true, hi_fallback: false, + mergerfs_mode: false, }, message: "Warning for cloud users: this provider needs to read the entire file in order to extract subtitles.", @@ -72,6 +73,8 @@ export const ProviderList: Readonly = [ include_ass: "Include ASS (will be converted to SRT)", hi_fallback: "Use HI subtitles as a fallback (don't enable it if you have a HI language profile)", + mergerfs_mode: + "[EXPERIMENTAL] Ignore cloud video files from rclone/mergerfs", }, }, { diff --git a/libs/subliminal_patch/providers/embeddedsubtitles.py b/libs/subliminal_patch/providers/embeddedsubtitles.py index 497a76ecd..3785eab16 100644 --- a/libs/subliminal_patch/providers/embeddedsubtitles.py +++ b/libs/subliminal_patch/providers/embeddedsubtitles.py @@ -78,6 +78,7 @@ class EmbeddedSubtitlesProvider(Provider): ffprobe_path=None, ffmpeg_path=None, hi_fallback=False, + mergerfs_mode=False, ): self._include_ass = include_ass self._include_srt = include_srt @@ -86,6 +87,7 @@ class EmbeddedSubtitlesProvider(Provider): ) self._hi_fallback = hi_fallback self._cached_paths = {} + self._mergerfs_mode = mergerfs_mode fese.FFPROBE_PATH = ffprobe_path or fese.FFPROBE_PATH fese.FFMPEG_PATH = ffmpeg_path or fese.FFMPEG_PATH @@ -163,8 +165,8 @@ class EmbeddedSubtitlesProvider(Provider): ] def list_subtitles(self, video, languages): - if not os.path.isfile(video.original_path): - logger.debug("Ignoring inexistent file: %s", video.original_path) + if not self._is_path_valid(video.original_path): + logger.debug("Ignoring video: %s", video) return [] return self.query( @@ -207,6 +209,21 @@ class EmbeddedSubtitlesProvider(Provider): return new_subtitle_path + def _is_path_valid(self, path): + if path in self._blacklist: + logger.debug("Blacklisted path: %s", path) + return False + + if not os.path.isfile(path): + logger.debug("Inexistent file: %s", path) + return False + + if self._mergerfs_mode and _is_fuse_rclone_mount(path): + logger.debug("Potential cloud file: %s", path) + return False + + return True + class _MemoizedFFprobeVideoContainer(FFprobeVideoContainer): @functools.lru_cache @@ -240,3 +257,17 @@ def _check_hi_fallback(streams, languages): else: logger.debug("HI fallback not needed: %s", compatible_streams) + + +def _is_fuse_rclone_mount(path: str): + # Experimental! + + # This function only makes sense if you are combining a rclone mount with a local mount + # with mergerfs or similar tools. Don't use it otherwise. + + # It tries to guess whether a file is a cloud mount by the length + # of the inode number. See the following links for reference. + + # https://forum.rclone.org/t/fuse-inode-number-aufs/215/5 + # https://pkg.go.dev/bazil.org/fuse/fs?utm_source=godoc#GenerateDynamicInode + return len(str(os.stat(path).st_ino)) > 18