diff --git a/flamenco_worker/commands.py b/flamenco_worker/commands.py
index 101779fd8fa145a7c93e827fc912f1c0dde5354a..4e05797f61f0c74e35c565124b10a23f93b9de0b 100644
--- a/flamenco_worker/commands.py
+++ b/flamenco_worker/commands.py
@@ -1070,11 +1070,24 @@ class EXRSequenceToJPEGCommand(BlenderRenderCommand):
         if not self.pyscript.exists():
             raise FileNotFoundError(f'Resource script {self.pyscript} cannot be found')
 
-        exr_directory, err = self._setting(settings, 'exr_directory', True)
+        exr_glob, err = self._setting(settings, 'exr_glob', False)
         if err:
             return err
-        if not exr_directory:
-            return '"exr_directory" may not be empty'
+
+        # Only for backward compatibility. Should not be used.
+        exr_directory, err = self._setting(settings, 'exr_directory', False)
+        if err:
+            return err
+
+        if not exr_glob and not exr_directory:
+            return '"exr_glob" may not be empty'
+        if exr_glob and exr_directory:
+            # Normally I would say 'use either one or the other, not both', but
+            # in this case 'exr_directory' is deprecated and shouldn't be used.
+            return 'Just pass "exr_glob", do not use "exr_directory"'
+
+        if exr_directory:
+            settings['exr_glob'] = str(Path(exr_directory) / '*.exr')
 
         output_pattern, err = self._setting(settings, 'output_pattern', False,
                                             default='preview-######.jpg')
@@ -1089,7 +1102,7 @@ class EXRSequenceToJPEGCommand(BlenderRenderCommand):
             '--python-exit-code', '32',
             '--python', str(self.pyscript),
             '--',
-            '--exr-dir', settings['exr_directory'],
+            '--exr-glob', settings['exr_glob'],
             '--output-pattern', settings['output_pattern'],
         ]
 
diff --git a/flamenco_worker/resources/exr_sequence_to_jpeg.py b/flamenco_worker/resources/exr_sequence_to_jpeg.py
index 36466f9c79c659a77a82663a5c92715b7c708e4d..e8770bbe2e1f9c87e994638decf9da53405271ec 100644
--- a/flamenco_worker/resources/exr_sequence_to_jpeg.py
+++ b/flamenco_worker/resources/exr_sequence_to_jpeg.py
@@ -1,8 +1,11 @@
 # This file is supposed to run inside Blender:
-# blender thefile.blend --python /path/to/exr_sequence_to_jpeg.py -- --exr-dir /path/to/exr/files
+# blender thefile.blend \
+#     --python /path/to/exr_sequence_to_jpeg.py \
+#     -- --exr-pattern /path/to/exr/files/prefix-*.exr
 
 import argparse
 import pathlib
+import re
 import sys
 
 import bpy
@@ -10,15 +13,16 @@ import bpy
 # Find the EXR files to process.
 dashdash_index = sys.argv.index('--')
 parser = argparse.ArgumentParser()
-parser.add_argument('--exr-dir')
+parser.add_argument('--exr-glob')
 parser.add_argument('--output-pattern')
 cli_args, _ = parser.parse_known_args(sys.argv[dashdash_index + 1:])
 
-imgdir = pathlib.Path(cli_args.exr_dir)
-exr_files = list(imgdir.glob('*.exr'))
+exr_glob = pathlib.Path(cli_args.exr_glob)
+imgdir = exr_glob.parent
+exr_files = sorted(imgdir.glob(exr_glob.name))
 
 if not exr_files:
-    raise ValueError(f'No *.exr files found in {cli_args.exr_dir}')
+    raise ValueError(f'No files found for pattern {exr_glob}')
 
 # Create a copy of the scene without data, so we can fill the sequence editor
 # with an image sequence.
@@ -32,11 +36,22 @@ se = scene.sequence_editor_create()
 # This assumes the files are named '000020.exr' etc.
 min_frame = float('inf')
 max_frame = float('-inf')
+
+# Interpret the last continuous string of digits as frame number.
+frame_nr_re = re.compile(r'[0-9]+$')
+print(f'Loading {len(exr_files)} EXR files:')
 for file in exr_files:
-    frame_num = int(file.stem, 10)
+    match = frame_nr_re.search(file.stem)
+    if not match:
+        raise ValueError(f'Unable to find frame number in filename {file.name}')
+    frame_num = int(match.group(), 10)
     min_frame = min(min_frame, frame_num)
     max_frame = max(max_frame, frame_num)
+    print(f'   - {file} -> frame {frame_num}')
     se.sequences.new_image(file.name, str(file), 1, frame_num)
+print(f'Found files for frame range {min_frame}-{max_frame}')
+print()
+sys.stdout.flush()
 
 scene.frame_start = min_frame
 scene.frame_end = max_frame
diff --git a/tests/test_commands_exr_sequence_to_jpeg.py b/tests/test_commands_exr_sequence_to_jpeg.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b55d5d65ba7982bb3f34fc45bbfaa5e26554b93
--- /dev/null
+++ b/tests/test_commands_exr_sequence_to_jpeg.py
@@ -0,0 +1,95 @@
+from pathlib import Path
+import subprocess
+import tempfile
+from unittest import mock
+
+from unittest.mock import patch
+
+from tests.test_runner import AbstractCommandTest
+
+
+class BlenderRenderProgressiveTest(AbstractCommandTest):
+    thisfile = Path(__file__).as_posix()
+
+    def setUp(self):
+        super().setUp()
+
+        from flamenco_worker.commands import EXRSequenceToJPEGCommand
+
+        self.cmd = EXRSequenceToJPEGCommand(
+            worker=self.fworker,
+            task_id='12345',
+            command_idx=0,
+        )
+
+    def test_exr_glob(self):
+        from tests.mock_responses import CoroMock
+
+        filepath = Path(__file__).parent.as_posix()
+        settings = {
+            # Point blender_cmd to this file so that we're sure it exists.
+            'blender_cmd': f'{self.thisfile!r} --with --cli="args for CLI"',
+            'filepath': filepath,
+            'exr_glob': '/some/path/to/files-*.exr',
+            'output_pattern': 'preview-######',
+        }
+
+        cse = CoroMock(...)
+        cse.coro.return_value.wait = CoroMock(return_value=0)
+        cse.coro.return_value.pid = 47
+        with patch('asyncio.create_subprocess_exec', new=cse) as mock_cse:
+            self.loop.run_until_complete(self.cmd.run(settings))
+
+            mock_cse.assert_called_once_with(
+                self.thisfile,
+                '--with',
+                '--cli=args for CLI',
+                '--enable-autoexec',
+                '-noaudio',
+                '--background',
+                filepath,
+                '--python-exit-code', '32',
+                '--python', str(self.cmd.pyscript),
+                '--',
+                '--exr-glob', '/some/path/to/files-*.exr',
+                '--output-pattern', 'preview-######',
+                stdin=subprocess.DEVNULL,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+            )
+
+    def test_exr_directory(self):
+        from tests.mock_responses import CoroMock
+
+        filepath = Path(__file__).parent.as_posix()
+        settings = {
+            # Point blender_cmd to this file so that we're sure it exists.
+            'blender_cmd': f'{self.thisfile!r} --with --cli="args for CLI"',
+            'filepath': filepath,
+            'exr_directory': '/some/path/to/exr',
+            'output_pattern': 'preview-######',
+        }
+
+        cse = CoroMock(...)
+        cse.coro.return_value.wait = CoroMock(return_value=0)
+        cse.coro.return_value.pid = 47
+        with patch('asyncio.create_subprocess_exec', new=cse) as mock_cse:
+            self.loop.run_until_complete(self.cmd.run(settings))
+
+            mock_cse.assert_called_once_with(
+                self.thisfile,
+                '--with',
+                '--cli=args for CLI',
+                '--enable-autoexec',
+                '-noaudio',
+                '--background',
+                filepath,
+                '--python-exit-code', '32',
+                '--python', str(self.cmd.pyscript),
+                '--',
+                '--exr-glob', '/some/path/to/exr/*.exr',
+                '--output-pattern', 'preview-######',
+                stdin=subprocess.DEVNULL,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+            )