NEWS.md

tidymedia 0.2.2

The package no longer assumes things about the machine it runs on that are not true everywhere. The fixes answer check failures on the two Fedora flavors CRAN uses, whose FFmpeg has no libx264 and whose which does not answer for a path. A failed run also says now when your FFmpeg build lacks an encoder the run used.

New features

Bug fixes

tidymedia 0.2.1

Documentation fixes for the first CRAN submission. No code changed, and no behavior differs from 0.2.0.

tidymedia 0.2.0

Breaking changes

The package is pre-1.0 and still soaking, so old names are removed rather than deprecated: there are no lifecycle shims, and a call using an old name gets R's usual could not find function or unused-argument error.

| verb | run was at position | run is now at position | |---|---|---| | extract_audio() | 4 | 5 | | format_for_web() | 3 | 7 | | separate_audio_video() | 4 | 10 | | segment_video() | 6 | 12 | | crop_video() | 7 | 13 |

segment_video()'s parallel moves from position 7 to 13. The old slots now hold other arguments, so a positional value lands on one of those instead: extract_audio(video, "audio.aac", "copy", FALSE) reads FALSE as the audio-stream index, and a positional TRUE in crop_video()'s or segment_video()'s old run slot stops with `video_codec` must be a single string or `NULL`.

Naming a track the input does not have remains an FFmpeg error rather than an R one, on every verb.

New features

compare_videos() and picture_in_picture() join them as the two fan-in verbs: a side-by-side or stacked comparison, and an inset overlay with a position, scale and margin. Both drop audio by default; audio_input is the 0-based index of the input whose audio to keep, which counts the verb's inputs rather than one input's audio streams.

The output column is optional on the verbs that can derive a name. Six auto-name one output per input by suffixing its basename — _standardized, _normalized, _anonymized, _stripped, _cropped, _web.mp4. segment_video_batch() and extract_frame_batch() append a zero-padded <basename>_<n> restarting at every input, and sample_frames_batch() writes into a <basename>_frames directory. Every batch verb refuses, before any row runs, a table in which two rows name the same output path.

The eight _batch siblings take it too, after fallback, applying to every row unless jobs carries a numeric quality column, where NA leaves that row's encoder default in place. Each cell is checked against the encoder its own row resolves to, and a wrong cell is refused before any row runs, naming the function and the row.

Digitally silent input measures as -inf loudness, which cannot be normalized. The scalar verb aborts naming silence as the cause; the batch verb normalizes the non-silent rows, marks the silent ones in a logical silent column with success = FALSE, and warns naming them.

It is wired into execution: ffm_run(verify = <named list>) probes the output after a successful run and aborts listing the failed checks, while ffm_batch(verify = ) records the outcome in a logical verified column without aborting. ffm_batch(manifest = TRUE) attaches a per-job provenance record — command, FFmpeg/FFprobe versions, timestamp, output size — read back with ffm_manifest(), and checksums = TRUE adds input and output md5 sums. ffm_batch(progress = TRUE) shows a cli progress bar.

NULL now means the same thing on every codec argument, and NA the same thing in every per-row codec column: emit no -codec:a / -codec:v at all, leaving the encoder to the output container. Three places disagreed — anonymize_video() refused video_codec = NULL while standardize_video() accepted it, extract_audio() refused audio_codec = NULL while its _batch sibling accepted it, and three codec columns rejected NA. All now accept it. No existing command changes; the calls that changed are ones that used to abort and now compile. A scalar NA is still an error everywhere. convert_audio() stays the deliberate exception, where NULL selects -q:a 0.

A reached limit is never silent. The task verbs, ffm_run(), verify_media() and the raw ffmpeg()/ffprobe()/mediainfo() hatches abort, naming the program and the limit. Where one hung file must not discard the rest of the work they warn instead: the metadata readers give an NA row and one warning saying how many files timed out, and the batch verbs mark the row success = FALSE and warn once at the end. Those two lists are not written from memory: a test derives the calls that can start one of these programs from the package's own call graph and drives a timeout through each of them.

with_timeout(expr, seconds) puts the limit on one call and restores whatever the session had, by any exit route, and local_timeout(seconds) is the statement form bounding the rest of the function you call it from. Both refuse a value the underlying limit could not use before expr runs.

The limit says how long tidymedia waits for a program, not how long that program may run. When it is reached R asks the program to stop, insists 20 seconds later, and kills it 20 seconds after that, so a program that answers none of the three is waited for up to 40 seconds longer than you asked. Under a 2-second limit, an FFmpeg blocked reading a pipe nobody writes to returned at 42.0 seconds on Linux, and a shell child that ignores both signals returned at 42.0 seconds on Linux and macOS alike. Plan for it: a 1-second limit across five hung files is three and a half minutes of waiting, not five seconds. How much of the lag you see depends on your FFmpeg, since the same blocked input took 42.0 seconds against FFmpeg 6.1.1 and 2.0 seconds against 9.0.1.

r tryCatch( ffm_run(pipeline), tidymedia_ffmpeg_exit = function(cnd) cnd$tm_status )

The loudnorm analysis pass behind normalize_audio(two_pass = TRUE) raises tidymedia_loudnorm_no_measurement instead, meaning the analysis yielded no usable measurement, and both the scalar and batch verbs raise the same class. The ffm_batch() family records success = FALSE for a failed row rather than raising either class. Batch diagnostics that cannot report one exit status carry the numbers they do have: tm_rows, the 1-indexed offending rows, and tm_row_status, their exit statuses aligned to it.

Bug fixes

Exactly the same calls are refused as before, and no legal value compiles a different command. What moves is which function the error names and, when a call is wrong in more than one way, which error you see: a path typed wrong is reported first, an argument contradiction ahead of a per-row value error, and both ahead of an unavailable hardware encoder, so the diagnosis no longer depends on which FFmpeg build you happen to have. On compare_videos_batch() and picture_in_picture_batch(), where a call can be wrong in both at once: A value error and a contradiction resolve the same way whether the value arrived as an argument or in a jobs column; the contradiction reports first. Some wording changed on calls that aborted before and still abort: `infile` does not exist is now `infile` can't be found or read. One message is missing rather than moved: a refusal that now happens before any row is built no longer carries the In index: line — except where the next entry supplies it.

Performance

Documentation

Requirements

tidymedia 0.1.0

First tagged release, bringing the metadata, builder, and task-verb work of the 0.0.0.900x development series to a documented, release-ready state.

Documentation

Metadata layer

Bug fixes

tidymedia 0.0.0.9002

Batch processing

Task verbs rebuilt on the builder

Pipeline engine

tidymedia 0.0.0.9001

Pipeline engine

Infrastructure

Bug fixes

tidymedia 0.0.0.9000



Try the tidymedia package in your browser

Any scripts or data that you put into this service are public.

tidymedia documentation built on Oct. 11, 2026, 5:08 p.m.