mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 10:51:36 -04:00
videos: hide the transcoder's temporary output from the file refresh (#731)
ffmpeg wrote `<stem>.transcode.<container>` next to the source. A refresh that overlapped the transcode discovered that file, probed the half-written output (errors in the log), and created a FileGroup and Video for a path the final rename removed moments later. The temporary file is now `.<stem>.transcode.<container>`: every discovery path already skips hidden files, and staying in the same directory keeps the rename over the final path atomic. `transcode_tmp_path` is the single place the name is built. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
86d4bbab98
commit
5dda94492e
2 changed files with 36 additions and 2 deletions
|
|
@ -7,7 +7,7 @@ from unittest import mock
|
|||
import pytest
|
||||
|
||||
from modules.videos.models import Video
|
||||
from modules.videos.transcode import make_ffmpeg_progress_parser, transcode_video_job
|
||||
from modules.videos.transcode import make_ffmpeg_progress_parser, transcode_video_job, transcode_tmp_path
|
||||
from wrolpi import jobs
|
||||
from wrolpi.cmd import CommandResult
|
||||
from wrolpi.files.models import FileGroup
|
||||
|
|
@ -293,3 +293,28 @@ async def test_video_transcode_api_fragmented(async_client, test_session, video_
|
|||
request, response = await async_client.post(f'/api/videos/{video.file_group_id}/transcode',
|
||||
content=json.dumps({'container': 'mkv', 'fragmented': True}))
|
||||
assert response.status == 400
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transcode_temp_file_is_not_discovered_by_refresh(test_session, test_directory, async_client,
|
||||
video_factory, refresh_files):
|
||||
"""A refresh that runs while ffmpeg is still writing the transcoder's output must not model it.
|
||||
|
||||
The temporary file is renamed over the final path moments later; a FileGroup for it would point
|
||||
at a file that no longer exists, and probing a half-written file logs errors.
|
||||
"""
|
||||
video_path = test_directory / 'videos/NO CHANNEL/movie.mp4'
|
||||
video_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
video = video_factory(with_video_file=video_path)
|
||||
test_session.commit()
|
||||
|
||||
tmp_path = transcode_tmp_path(video_path, 'mp4')
|
||||
shutil.copy(PROJECT_DIR / 'test/big_buck_bunny_720p_1mb.mp4', tmp_path)
|
||||
assert tmp_path.is_file()
|
||||
|
||||
await refresh_files()
|
||||
|
||||
primary_paths = {str(i.primary_path) for i in test_session.query(FileGroup)}
|
||||
assert str(video_path) in primary_paths
|
||||
assert str(tmp_path) not in primary_paths, 'the transcoder temporary file was indexed'
|
||||
assert not any(str(tmp_path) in str(p) for p in primary_paths)
|
||||
|
|
|
|||
|
|
@ -149,6 +149,15 @@ async def _acquire_transcode_lock():
|
|||
return lock
|
||||
|
||||
|
||||
def transcode_tmp_path(video_path: pathlib.Path, container: str) -> pathlib.Path:
|
||||
"""Where ffmpeg writes its output before it is renamed over the final path.
|
||||
|
||||
Hidden (dot-prefixed) in the video's own directory: a refresh skips hidden files, so a refresh that
|
||||
overlaps the transcode neither probes the half-written output nor creates a FileGroup for a file
|
||||
that the rename removes moments later; and same-directory keeps the final rename atomic."""
|
||||
return video_path.with_name(f'.{video_path.stem}.transcode.{container}')
|
||||
|
||||
|
||||
async def transcode_video_file(video_path: pathlib.Path,
|
||||
target_vcodec: Optional[str] = None,
|
||||
target_acodec: Optional[str] = None,
|
||||
|
|
@ -363,7 +372,7 @@ async def _transcode_video_file(video_path: pathlib.Path, target_vcodec: Optiona
|
|||
mux_args = ()
|
||||
|
||||
final_path = video_path.with_suffix(f'.{container}')
|
||||
tmp_path = video_path.with_suffix(f'.transcode.{container}')
|
||||
tmp_path = transcode_tmp_path(video_path, container)
|
||||
cmd = (FFMPEG_BIN, '-y',
|
||||
'-i', video_path,
|
||||
*map_args,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue