From 4cc50686ea31cad255f2bee8fc8fca8e001319ac Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 30 Jun 2020 18:16:39 -0400 Subject: [PATCH] Add local fallback mode Fall back to using the local ffmpeg binary (at the same path as the remote system) if we are unable to reach a remote server. Prevents rffmpeg from getting stuck if it can find no valid remote servers. --- README.md | 6 ++++++ rffmpeg.py | 45 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e123841..0b35a3d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ rffmpeg supports setting multiple hosts. It keeps state in `/run/shm/rffmpeg`, o When running rffmpeg manually, *do not* exit it with `Ctrl+C`. Doing so will likely leave the `ffmpeg` process running on the remote machine. Instead, enter `q` and a newline ("Enter") into the rffmpeg process, and this will terminate the entire command cleanly. This is the method that Jellyfin uses to communicate the termination of an `ffmpeg` process. +### Local fallback + +rffmpeg will fall back to a local copy of ffmpeg, at the same location as on remote systems (i.e. as configured in `/etc/rffmpeg/rffmpeg.yml`), should it be unable to find any working remote hosts. This helps prevent situations where rffmpeg cannot be run due to none of the remote host(s) being unavailable. + +If you want the local system to be included in the normal list, for instance if the local system is also a powerful transcode machine, you can add `localhost` to the list of hosts in order to have it be used along with the remote systems; it will SSH to itself but, if the guide below is followed exactly, will work as expected. + ## Full setup guide This example setup is the one I use for `rffmpeg`, involving a media server (`jf1`) and a remote transcode server (`gpu1`). Both systems run Debian GNU/Linux, though the commands below should also work on Ubuntu. Note that Docker is not officially supported with `rffmpeg` due to the complexity of exporting Docker volumes with NFS, the path differences, and the fact that I don't use Docker, but if you do figure it out a PR is welcome. diff --git a/rffmpeg.py b/rffmpeg.py index ee060d8..f8f8f2a 100755 --- a/rffmpeg.py +++ b/rffmpeg.py @@ -95,6 +95,47 @@ current_statefile = config['state_tempdir'] + '/' + config['state_filename'].for logger("Starting rffmpeg {}: {}".format(our_pid, ' '.join(all_args))) +def local_ffmpeg_fallback(): + """ + Fallback call to local ffmpeg + """ + rffmpeg_command = list() + + # Prepare our default stdin/stdout/stderr (normally, stdout to stderr) + stdin = sys.stdin + stdout = sys.stderr + stderr = sys.stderr + + # Verify if we're in ffmpeg or ffprobe mode + if 'ffprobe' in all_args[0]: + rffmpeg_command.append(config['ffprobe_command']) + stdout = sys.stdout + else: + rffmpeg_command.append(config['ffmpeg_command']) + + # Determine if version, encorders, or decoders is an argument; if so, we output stdout to stdout + # Weird workaround for something Jellyfin requires... + if '-version' in cli_ffmpeg_args or '-encoders' in cli_ffmpeg_args or '-decoders' in cli_ffmpeg_args: + stdout = sys.stdout + + # Parse and re-quote any problematic arguments + for arg in cli_ffmpeg_args: + rffmpeg_command.append('{}'.format(arg)) + + p = subprocess.run(rffmpeg_command, + shell=False, + bufsize=0, + universal_newlines=True, + stdin=stdin, + stderr=stderr, + stdout=stdout) + returncode = p.returncode + + os.remove(current_statefile) + + logger("Finished rffmpeg {} (local failover mode) with return code {}".format(our_pid, returncode)) + exit(returncode) + def get_target_host(): """ Determine the optimal target host @@ -147,8 +188,8 @@ def get_target_host(): target_host = host if not target_host: - logger('ERROR: Failed to find a valid target host') - exit(1) + logger('Failed to find a valid target host - using local fallback instead') + local_ffmpeg_fallback() # Write to our state file with open(current_statefile, 'a') as statefile: