aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
* feat(backup): add original file backupsDennis Fink2026-05-16
| | | | | | | | | | | | Add --backup-dir support so successful encodes can copy the original file to an existing backup directory before replacing it. Preserve each source file's absolute path below the backup directory and avoid overwriting existing backups by appending numeric suffixes when needed. Document the new CLI and config option, and add directory completion for the backup path.
* fix: allow out-of-range nice adjustmentsDennis Fink2026-05-16
| | | | | | | | | | | | Keep nice validation focused on accepting integer values instead of rejecting values outside the standard -20 to 19 range. Avoid failing early for values that nice itself accepts and clamps internally. This gives users behavior that matches the underlying tool instead of turning a non-error into a transcode.sh usage error. Update help text and README wording to describe the option as a nice adjustment, matching the command behavior.
* refactor: improve script structure readabilityDennis Fink2026-05-16
| | | | | | | | | Add a file structure overview and split the script into clearer section headers so related functions are easier to find. Move main execution below all function definitions and add local subsection comments inside encode_one without changing the script behavior.
* refactor(presets): clarify preset variable namesDennis Fink2026-05-16
| | | | | | | | | Rename generic preset path variables to make the preset loading and listing code easier to read. Use explicit names such as preset_file and preset_name so the code better communicates which values refer to files and which refer to display names.
* chore: add release automationDennis Fink2026-05-15
| | | | | | | | | Add a dedicated release helper so version and revision metadata can be prepared consistently before publishing a new release. Use git attributes to keep developer tooling and repository metadata out of generated source archives. Also ignore the dist directory created during the release process.
* docs: polish README formattingDennis Fink2026-05-15
| | | | | | | | Move the preset security note before the preset path so readers see the warning before creating or sourcing preset files. Align the size report schema header with spaces for cleaner rendered documentation.
* refactor(encode): skip excluded codecs before format checksDennis Fink2026-05-14
| | | | | | | | | Move the skip-codec check directly after codec detection, before validating pixel format and frame rate. This lets files with excluded codecs return early without requiring additional probe fields that are irrelevant when the file will not be encoded.
* refactor(probe): read ffprobe fields without sedDennis Fink2026-05-14
| | | | | | | | | | | Replace three separate `printf | sed` pipelines with a single grouped `read` block fed from the existing ffprobe output. This keeps codec, pixel format, and frame-rate parsing together, avoids extra subprocesses, and makes the field order easier to see at the call site. No runtime behavior is intended to change.
* feat(report): improve size summary outputDennis Fink2026-05-14
| | | | | | | | | | | Track the total encoded output size alongside the original and saved byte counts so the batch summary can show original vs. new totals. Improve size report feedback for larger outputs by printing the byte difference as “larger” instead of showing a negative saved value. Rework the final summary format to include colored encoded, skipped, failed, total-size, and average saving lines, with skipped and failed counts broken down by reason.
* style(shell): prefer explicit conditionals for validationDennis Fink2026-05-14
| | | | | | | | | | | | Replace compact `[[ ... ]] || { ... }` guard blocks with explicit `if` statements in config loading, preset loading, CLI argument validation, size report helpers, and encode-file handling. Rename the local preset variable in `load_preset` to `preset_name` for clearer intent, and simplify the nice-value range check and size-report summary default percentage handling. No runtime behavior is intended to change.
* style(comments): reflow and clarify script commentsDennis Fink2026-05-14
| | | | | | | | | Rewrap long comments throughout transcode.sh for more consistent line length and readability. Clarify a few explanatory comments around filesize formatting, preset descriptions, empty input handling, ffprobe probing, temporary output creation, and output verification. No runtime behavior is changed.
* docs: fix option and help text typosDennis Fink2026-05-14
| | | | | | | | Correct the README entry for the long --encode-file=PATH form, which was documented as --encodefile=PATH. Also fix spacing in the generated help text for output_gop_size so the preset variable descriptions remain aligned.
* refactor(output): centralize formatted terminal messagesDennis Fink2026-05-14
| | | | | | | | | | | | | | | Add a shared emit() helper for styled terminal output and route error(), msg(), warn(), verbose(), and debug() through it. This removes duplicated printf formatting across the output helpers while keeping the existing prefixes, colors, quiet/verbose/debug gating, and stderr handling for errors. emit() also joins additional message arguments into a single trailing string, matching the output pattern used by the individual helpers. The output helpers now differ only in their prefix, color, and visibility condition, making future formatting changes easier and less error-prone.
* feat(size-report): replace savings log with status reportsDennis Fink2026-05-14
| | | | | | | | | | | | | | | | | | | | | | | | | | Rename the old savings log feature to size report across the CLI, configuration, documentation, man page, and bash completion. The new `--size-report` option keeps the existing `-s` shorthand, while `--saving`, `--saving-file`, and the `[saving]` config section are replaced by `--size-report`, `--size-report-file`, and `[size_report]`. Extend reporting beyond successful encodes. The TSV log now gains a leading `status` column and records encoded files, codec skips, only-if-larger skips, and failed inputs. Size-report output also prints per-file feedback for encoded and skipped-larger files, including human-readable original/new sizes, bytes saved, and saved percentage. Track batch-level report counters and print an end-of-run summary for multi-file runs with encoded/skipped/failed counts, total bytes saved, and the overall weighted saving percentage across successful encodes. Document the new TSV schema in the README and man page, update examples and config snippets, and adjust bash completion for the renamed options. BREAKING CHANGE: `--saving` is now `--size-report`. `--saving-file` is now `--size-report-file`. `[saving]` in config.toml is now `[size_report]`. The TSV log gains a leading `status` column and may contain skipped and failed rows in addition to successful encodes.
* refactor(script): improve readability of encoding flowDennis Fink2026-05-14
| | | | | | | | | | | | | | Expand compact shell conditionals into explicit if blocks throughout config loading, codec parsing, cleanup, and file-list handling. Rename short local variables in encode_one to more descriptive names, including the input file, temporary output path, command array, and progress counters. Also align preset-list output with the shared msg helper and update colors for preset names. This makes the main transcoding path easier to read and maintain without changing the intended encoding behaviour.
* refactor(core): remove awk and bc dependenciesDennis Fink2026-05-10
| | | | | | | | | | | | | | | Both tools were used for simple arithmetic that bash handles natively. awk: fps rounding (num/den) replaced with round-half-up integer arithmetic: (num + den/2) / den. bc: savings percentage replaced with bash $(( )) arithmetic. bc was a conditional dependency (only required with --saving), which made the dependency story inconsistent. Removing it means the full feature set is available with only ffmpeg, ffprobe, nice, and optionally tomlq. Updated README requirements table, man page --saving description, SEE ALSO section, and exit-code 127 entry accordingly.
* feat(preset): add --list-presets to enumerate available presetsDennis Fink2026-05-10
| | | | | | | | | | | | | | | | | | | | | | Print all *.sh files found in $PRESET_DIR, one per line, with a one-line description extracted from the first line of each file if it matches a `# description: …` comment. Presets without such a comment are listed without a description. Symlinks pointing to another preset inside $PRESET_DIR are shown as "<name> - Symlinked to <target>" rather than duplicating the description. Symlinks pointing outside $PRESET_DIR are followed and treated as regular preset files. Column width for preset names is calculated from the longest name in the directory so descriptions align with minimal padding. Also extracts the repeated readlink/realpath idiom into a dedicated resolve_path() helper and adds a doc comment to filesize(). Exits immediately after printing (same pattern as --version/--help). Honours --quiet/NO_COLOR via the existing msg() and color infrastructure.
* feat(core,cli): add hardware acceleration support via --hwaccelDennis Fink2026-05-09
| | | | | | | | | | | | | | | | | | | | | Add --hwaccel [METHOD] / --no-hwaccel flags to control whether -hwaccel is passed to ffmpeg before the input file. The METHOD argument is optional and defaults to "auto", letting ffmpeg select the best available hardware decoder with automatic software fallback. Hardware acceleration is enabled by default (method: auto). Users on hosts where hwaccel causes compatibility or stability issues can opt out with --no-hwaccel or config.toml [encoding] hwaccel = false. The config key accepts false (disable), true (auto), or a method string such as "vaapi" or "cuda". CLI flags take precedence as usual. Bash completion is extended with a hwaccel_methods word list (auto, cuda, vaapi, dxva2, d3d11va, videotoolbox, qsv) for both the space form (--hwaccel <TAB>) and the equals form (--hwaccel=<TAB>). The completion script is also reformatted from 4-space to 2-space indentation for consistency with transcode.sh itself.
* refactor(core): remove hardcoded PATH resetDennis Fink2026-05-09
| | | | | | | | | | | | | | | PATH='/bin:/usr/bin:/usr/local/bin' was intended as a security measure against PATH injection. For a script invoked manually in the user's own shell the benefit is marginal: if an attacker controls the user's PATH they already have larger problems. The cost is real — tools installed outside these three directories (Homebrew, Nix, ~/.local/bin) silently fail, and the documented workaround of prepending a path at invocation time does not work because the script overwrites PATH immediately on startup. Remove the PATH reset and all associated documentation. The remaining hardening measures (unalias -a, hash -r, strict set -o flags, umask) are retained.
* feat(core): verify output integrity before replacing originalDennis Fink2026-05-09
| | | | | | | | | | | | | | | | | | | | | | | After ffmpeg exits successfully, run ffprobe against the temporary output file to confirm it is a valid, playable media file before replacing the original. If the probe fails the temporary file is removed and the encode is treated as a runtime failure, leaving the original untouched. This guards against cases where ffmpeg exits 0 but has written a truncated or corrupt container (e.g. due to a disk-full condition mid-write, a codec bug, or a preset producing an unreadable stream). The check is enabled by default and can be disabled via --no-verify-output or [encoding] verify_output = false in config.toml for large batch jobs on trusted encoders where the extra ffprobe round-trip is undesirable. The --continue flag is honoured: a failed integrity check counts as a runtime failure for the purposes of deciding whether to proceed to the next file. Docs, man page, bash completion, and README updated accordingly.
* feat(core): expose frame-rate variables to presetsDennis Fink2026-05-08
| | | | | | | | | | | | | | | | Add input_frame_rate, input_fps, and output_gop_size to the set of variables available to preset files at source time. input_frame_rate is the raw ffprobe fraction (e.g. 30000/1001). input_fps rounds that fraction to the nearest integer via awk. output_gop_size is derived as roughly five seconds of video (input_fps * 5), capped at 300 frames. The ffprobe invocation is extended to request r_frame_rate alongside the existing codec_name and pix_fmt entries; a missing frame rate is now treated as a hard error. Dry-run and verbose output are updated to display the new values. Documentation (header comment, --help text, man page, README) is updated accordingly.
* chore(docs): update author email and add SPDX headersDennis Fink2026-05-08
| | | | | | | | | | | The contact address dennis.fink@c3l.lu has been replaced with me+coding@dennisfink.me across all files where it appeared: SPDX-FileCopyrightText, AUTHOR constant, --version output, README footer, LICENSE file, and the man page AUTHOR section. SPDX licence headers were also added to transcode.sh.1 and transcode.sh.bash-completion to bring all files into full REUSE compliance.
* feat(cli)!: add TOML config file supportDennis Fink2026-05-08
| | | | | | | | | | | | | | | | | | | | | | | | | Add a structured config file so common defaults can live in one place instead of being repeated in aliases or shell wrappers. CLI flags keep precedence, which keeps one-off overrides predictable. Use tomlq only when a config file is present, and fail early on missing dependencies or invalid TOML so configuration mistakes do not silently change encode behavior. Move the persistent skip-codec list from the legacy skip.conf file to [skip] codecs in config.toml. Config codecs are merged with --skip-codec so global defaults and per-run skips can be combined. Document the new format in the README and man page, update dependency notes, and extend bash completion for config-file paths and the new negative boolean flags. BREAKING CHANGE: skip.conf is no longer read for codec skips. Migrate its entries to [skip] codecs in config.toml and remove the old file. BREAKING CHANGE: --encodefile and --encodefile= are replaced by --encode-file and --encode-file=. Update scripts, aliases, and completion usage to use the hyphenated option name.
* docs(cli): overhaul help and version output with color and structureDennis Fink2026-02-26
| | | | | | | | | | | | | | | | | | | | Rewrite `print_help` to use the full color palette and group options into labeled sections (Encoding, Input, Savings log, Output, General, and a new Configuration files section). The Configuration files section documents the preset directory and skip.conf path, including the variables available to preset authors and the security note about trusted sources. Default values for `--preset`, `--nice`, and `--saving-file` are now shown inline using `$PRESET_NAME`, `$NICE_VALUE`, and `$SAVING_FILE` rather than being hardcoded, so they always reflect the actual defaults. Rewrite `print_version` to inline the BSD-3-Clause license text directly instead of conditionally catting `LICENSES/BSD-3-Clause.txt`. This makes `--version` self-contained regardless of where the script is invoked from. Apply the same color treatment to the short usage hint printed when no arguments are given.
* chore: bump versionv1.0.1Dennis Fink2026-02-25
|
* revert(preset): return ffargs via nameref instead of global variableDennis Fink2026-02-25
| | | | | | | Reverts commit b10b81fefbe529be7db14ca65ec4b4a293782d39. The nameref approach did not work as intended; reverting to restore correct behaviour.
* chore: update revision datev1.0.0Dennis Fink2026-02-25
|
* fix(skip): join codec keys before passing to verbose()Dennis Fink2026-02-25
| | | | Joining into a single string ensures the entire list renders uniformly.
* feat(core): add warn() and downgrade --saving-file diagnostic to warningDennis Fink2026-02-25
| | | | | | The --saving-file without --saving condition is non-fatal, so using error() was misleading. Introduce warn() (yellow output, respects --quiet) and use it for this diagnostic instead.
* feat(core): add batch progress counter to processing outputDennis Fink2026-02-25
| | | | | | | | | | | encode_one now receives the current index and total file count and includes them in the "Processing" message (e.g. "Processing [2/7]: file.mkv"). Both the CLI and encode-file paths pre-collect their file lists into arrays so the total is known before encoding begins. Also fix a typo in the encode_one step comment ("preserverse" -> "preserves") and add a note on the filename variable explaining the single-extension stripping behaviour for multi-dot filenames.
* refactor(core): combine ffprobe calls and simplify pixel format selectionDennis Fink2026-02-25
| | | | | | | | | | | | | | | Merge the two separate ffprobe invocations for codec and pixel format into one, halving the per-file probe overhead on large batches. Both error messages now include the filename for easier diagnosis in batch runs. Replace the three-way nested if/elif/else for pixel format selection with two independent case statements — one for chroma subsampling, one for bit depth — that compose the output format string from two parts. This eliminates the repetition across the three subsampling branches and makes adding a new bit depth or subsampling variant a single-line change. Also add a verbose fallback warning for exotic formats that don't match any known subsampling pattern.
* refactor(preset): return ffargs via nameref instead of global variableDennis Fink2026-02-25
| | | | | | | | | | | | | | load_preset now accepts the name of the caller's array as a second argument and populates it via a nameref (local -n), replacing the previous implicit global ffargs. The preset is sourced into a local ffargs contained within load_preset's scope and copied to the caller on return. encode_one declares its own local ffargs=() and passes its name to load_preset, making the data flow explicit and eliminating the implicit coupling through the global. The nameref variable is prefixed with an underscore to avoid accidental aliasing of any same-named local in the calling scope.
* fix(cli): improve output consistency and verbose diagnosticsDennis Fink2026-02-25
| | | | | | | | | | | | | | Replace the full help page on no-args with a short usage hint to stderr, reserving the full help for explicit -h/--help invocations. Print the active codec skip list at startup under --verbose so unexpected skips can be diagnosed without resorting to DEBUG mode. Include the resolved ffmpeg command in dry-run --verbose output, which is useful for preset authors verifying their ffargs expansion. Fix the only-if-smaller message to follow the "label: value" convention used throughout the script.
* fix(cli): validate empty option values and improve --saving-file handlingDennis Fink2026-02-25
| | | | | | | | | | | | | | | Reject empty values for --encodefile, --saving-file, and --preset in both the space-separated and equals forms (e.g. --preset= now exits with a clear error instead of failing later with a confusing message from the allowlist check). Track whether --saving-file was set explicitly via SAVING_FILE_EXPLICIT. Warn when --saving-file is used without --saving, but do not exit, to allow the combination in aliases and wrapper scripts. Change the savings log format from space-separated to TSV and write a header line on first creation, making the file easier to parse both by eye and programmatically.
* refactor: simplify several small code patternsDennis Fink2026-02-24
| | | | | | | | | | | | - Drop the UMASK variable; umask is called directly with the literal value - Simplify filesize() to a one-liner using || instead of if/local/printf - Fold token lowercasing into the local declaration in add_skip_codec_token and drop the now-redundant empty-string guard - Replace manual whitespace trimming in the skip.conf reader with read's built-in IFS trimming - Replace the yuvj* case statement with a single parameter substitution - Collapse nested only-if-smaller conditions into one && expression - Move the "Sourcing preset file" debug call to just before source
* chore: update revision date and fix gitmessage leading newlineDennis Fink2026-02-24
|
* fix(core): use mktemp for temporary output file creationDennis Fink2026-02-24
| | | | | Replaces $$.$RANDOM with mktemp, which provides cryptographically random names and atomic file creation.
* fix: improve input validation and error reportingDennis Fink2026-02-24
| | | | | | | | | | | | | Validate the nice value is an integer in [-20, 19] at startup rather than letting nice(1) fail with a cryptic error at encode time. In encode_one, move the "Processing" message after the empty-string guard so it is not printed for blank lines, include the filename in the "Not found" error, and add a readability check so unreadable files are rejected with a clear message before ffprobe is invoked. Validate that the encode file (-f) exists before entering the read loop, exiting with a usage error rather than a silent empty run.
* fix(preset): refuse to source world-writable preset filesDennis Fink2026-02-24
| | | | | | | | | | Preset files are executed as shell code. A world-writable preset file could be modified by any local user to inject arbitrary commands. Symlinks are resolved before the permission check because stat on a symlink returns the permissions of the link itself (always 777 on Linux) rather than those of the target. readlink -f is used with a fallback to realpath for macOS/BSD compatibility.
* refactor: remove expand_aliases shoptDennis Fink2026-02-24
| | | | | | | Aliases are never defined or used in the script, making this option redundant. Removing it reduces the attack surface slightly and avoids unexpected behaviour if the script is ever sourced into a shell with user-defined aliases.
* fix: enable TRACE only when set to 1 instead of any non-zero valueDennis Fink2026-02-24
| | | | | Consistent with the DEBUG variable which is also compared against 1 throughout the script.
* fix(core): pass filename as separate argument to msg/error callsDennis Fink2026-02-24
| | | | | | | Aligns with the two-argument calling convention used elsewhere in the script, where the bold label and the highlighted value are passed as separate arguments. Also moves the preset name out of the error string in load_preset for the same reason.
* docs: document all variables available to presetsDennis Fink2026-02-24
| | | | | | | Previously only output_pixel_format was mentioned. All three variables (input_codec, input_pixel_format, output_pixel_format) are now listed with descriptions in the script header comment, the README, and the man page.
* chore: add BSD-3-Clause license textDennis Fink2026-02-22
|
* docs: document base ffmpeg arguments passed before preset ffargsDennis Fink2026-02-22
| | | | | | | | | | | Both the README and the man page now explain that the script always passes -map 0, -map_metadata 0, -map_chapters 0, and -c copy to ffmpeg before appending the preset's ffargs array. This context is important for preset authors, who only need to override the streams they intend to re-encode. Also fix a minor blockquote rendering issue in the README (missing blank line before the fenced code block in the PATH note).
* feat: initial release of transcode.sh v1.0.0Dennis Fink2026-02-22
Batch transcode helper for media files using ffmpeg. Encoding parameters are supplied by user-defined preset files (shell snippets that set an `ffargs` array) stored under ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/presets/. Key capabilities: - Probes input codec and pixel format via ffprobe; derives an output pixel format that preserves chroma subsampling and bit depth. - Per-user and per-invocation codec skip lists to leave already-encoded files untouched. - Atomic in-place replacement: encodes to a temp file in the same directory, then renames on success. - --only-if-smaller: discard the result if it is larger than the original. - --saving: append filename, original/new sizes and percentage saved to a log file after each successful encode. - --dry-run, --verbose, --quiet, --nice and --continue flags. - Respects the NO_COLOR standard. - Script hardening: strict PATH, safe IFS, errtrace, nounset, pipefail. Also included: - Man page (transcode.sh.1) - Bash completion script (transcode.sh.bash-completion) - README.md - .gitmessage commit message template License: BSD-3-Clause