├── .gitignore ├── Makefile ├── COPYING ├── reflac.adoc ├── NEWS.adoc ├── README.adoc └── reflac /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#*\# 3 | .\#* 4 | *.html 5 | *.1 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This can be changed to "asciidoctor -b manpage" if necessary. 2 | ASCIIDOC?=a2x -f manpage 3 | 4 | # Autotools-like prefix, mandir, and DESTDIR variables can be 5 | # overriden to control where files are installed. 6 | prefix?=/usr/local 7 | mandir?=/share/man 8 | target=$(DESTDIR)$(prefix) 9 | 10 | all: man 11 | 12 | man: reflac.1 13 | 14 | reflac.1: reflac.adoc 15 | $(ASCIIDOC) reflac.adoc 16 | 17 | clean: 18 | $(RM) reflac.1 19 | 20 | install: reflac.1 21 | install -Dm 755 reflac "$(target)/bin/reflac" 22 | install -Dm 644 reflac.1 "$(target)$(mandir)/man1/reflac.1" 23 | 24 | uninstall: 25 | $(RM) "$(target)/bin/reflac" 26 | $(RM) "$(target)$(mandir)/man1/reflac.1" 27 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright © 2014-2021 Mike Swanson 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /reflac.adoc: -------------------------------------------------------------------------------- 1 | = reflac(1) 2 | :doctype: manpage 3 | 4 | 5 | == NAME 6 | reflac - recompress FLAC files 7 | 8 | == SYNOPSIS 9 | *reflac* [_OPTIONS_...] [_--_] _DIRECTORY_... 10 | 11 | == DESCRIPTION 12 | reflac recompresses FLAC files while preserving all of their tags, and 13 | one embedded image. It should always be safe to run, it handles cases 14 | of special file names well, allowing odd characters such as *, ?, |, 15 | and so on. To use on a directory that itself begins with a hyphen, 16 | you should use the _--_ argument, it will terminate the options list 17 | and intepret all further arguments as directory names. 18 | 19 | *-h*, *--help*:: 20 | Displays a usage summary and brief help descriptions. 21 | 22 | *-V*, *--version*:: 23 | Displays reflac’s version. 24 | 25 | *-v*, *--verbose*:: 26 | reflac will keep quiet in normal operation, keeping to the Rule of 27 | Silence. Specifying this option once will display the directory names 28 | it enters and each file name processed. Specifying this option twice 29 | will display the entire output of *flac*, including the progress of 30 | each individual file. 31 | 32 | *-r*, *--recursive*:: 33 | Descend into directories recursively. You can easily recompress an 34 | entire tree of FLACs using this option, if, for example, you might 35 | store multiple albums or multiple discs in separate directories, you 36 | can just specify the top-level directory with the recursive option 37 | enabled. 38 | 39 | *-n*, *--no-action*:: 40 | Do not recompress any files. Combined with the *-v* option, reflac 41 | will display a list of files it would otherwise recompress. 42 | 43 | *-s*, *--no-sync*:: 44 | Do not synchronize file data or the rename operation. The script will 45 | return to prompt faster, but introduces a chance to lose files or have 46 | incomplete files after a system crash. 47 | 48 | *-0*, *--fast*:: 49 | Passes *-0* onto the *flac* command. This is the least attainable 50 | compression possible, and it is also the least CPU-intensive for both 51 | encoding and decoding. 52 | 53 | *-1*, *-2*, *-3*, *-4*, *-5*, *-6*, *-7*:: 54 | Passes the selected compression level to FLAC, higher numbers being 55 | more compressed but also more CPU-intensive on encoding and decoding. 56 | The default for reflac is *-5*, the same as *flac* itself. 57 | 58 | *-8*, *--best*:: 59 | Passes *-8* onto the *flac* command. This is the highest attainable 60 | compression possible, and it is also the most CPU-intensive for both 61 | encoding and decoding. Real-time playback may not be possible on some 62 | embedded-type systems, such as Blu-ray players or portable music 63 | players. 64 | 65 | == COPYRIGHT 66 | reflac is licensed under a very permissive ISC license, the same 67 | prefered by OpenBSD. See *COPYING* in the distribution for details. 68 | -------------------------------------------------------------------------------- /NEWS.adoc: -------------------------------------------------------------------------------- 1 | = reflac project news 2 | 3 | HEAD:: 4 | * `Makefile` now has an `uninstall` rule, for those that aren’t 5 | using a package manager to install this. 6 | 7 | 2.0.1 (2021-08-16):: 8 | * Solve a minor edge-case: reflac will now find and recompress 9 | hidden files. 10 | 11 | 2.0.0 (2020-08-07):: 12 | * reflac skips intermediate decompression steps, significantly 13 | speeding up the program and simplifying the code. 14 | * Removed the `--preserve` option as `flac` itself now handles it 15 | without special conditioning. This is a *breaking* change, and by 16 | https://semver.org/[Semantic Versioning] rules, necessitates a 17 | major version bump, thus: reflac 2.0.0. 18 | 19 | 1.5.3 (2020-02-26):: 20 | * AsciiDoc implementation can be altered for manpage generation with 21 | `make ASCIIDOC="command..."`, defaulting to `a2x` as before. Some 22 | of the manpage syntax has been altered for AsciiDoctor 23 | compatibility. 24 | 25 | 1.5.2 (2019-01-26):: 26 | * The non-verbose recursive use case of reflac has been repaired. 27 | 28 | 1.5.1 (2018-01-31):: 29 | * reflac now passes on the `--no-sync` and `--preserve` options to 30 | child processes when `--recursive` is used. 31 | 32 | 1.5 (2017-08-16):: 33 | * reflac has learned to preserve an embedded image in FLAC files. 34 | Limited to just one image for now, but should cover the most 35 | common case. 36 | 37 | 1.4 (2016-12-16):: 38 | * Add an option to preserve the file modification time. 39 | 40 | 1.3 (2016-09-25):: 41 | * Use the rf64 format during processing, eliminating the 4GiB 42 | uncompressed file size limit. This is primarily useful for FLACs 43 | with lots of channels (eg, surround sound) and high sample sizes 44 | and rates, as the 4GiB limit isn’t otherwise likely to be hit. 45 | 46 | 1.2.3 (2016-07-10):: 47 | * Actually bump the script’s own version identifier. 48 | 49 | 1.2.2 (2016-07-10):: 50 | * The first bash in the $PATH is used instead of /bin/bash 51 | * Shift temporary directory creation after getopt. reflac won’t 52 | leave one around anymore after ambiguous options (such as `--v` 53 | expanding to both `--verbose` and `--version`). 54 | 55 | 1.2.1 (2016-04-28):: 56 | * Remove the temporary directory also after using `--version` 57 | 58 | 1.2 (2016-04-28):: 59 | * Sync data by default, add `--no-sync` to disable. The new 60 | versions of FLACs files are written out to prevent 61 | missing/incomplete files in the face of a crash. 62 | 63 | 1.1 (2016-04-25):: 64 | * Add `--no-action`, enabling the view of files that would be 65 | processed. 66 | * Secure against using `--recursive` and directory names beginning 67 | with hyphens. 68 | * Displays usage help if no directories are specified on the command 69 | line. 70 | * Use a temporary staging directory, avoiding the possibility of 71 | losing all existing tag data in the face of errors. 72 | 73 | 1.0 (2016-04-17):: 74 | * Use the so-called “bash strict mode” and enable better processing 75 | of special file names. 76 | 77 | 0.2 (2015-05-18):: 78 | * Added a recursive option, allowing a whole tree to be 79 | recompressed. 80 | 81 | 0.1 (2014-02-26):: 82 | * Initial release, expanded from a ~7 line script the author 83 | initially wrote around 2003. 84 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = reflac 2 | 3 | This is a script that allows you to recompress FLAC files while 4 | preserving their tags, intended for whole directories and for safety 5 | regardless of file name characters and encoding. 6 | 7 | When storing FLACs on your big storage (desktop computer, NAS, etc), 8 | there may be a desire to compress them to the maximum extent possible, 9 | saving disk space and potentially many gigabytes for large 10 | collections. For this, you might use `reflac --best` on them. 11 | 12 | However, slow and old systems might not have the capability to decode 13 | a maximally-compressed (or even the standard compression ratio) quick 14 | enough for real-time playback, and recompressing in a lower setting 15 | might be beneficial as well. This script started life, in a _very_ 16 | rudimentary form for this purpose. The author used Rockbox on an old 17 | black/white display iPod, and found that `flac -3` was the maximum he 18 | could compress FLACs while maintaining uninterrupted playback on the 19 | device. 20 | 21 | == Requirements 22 | 23 | `flac` needs to be in your $PATH. This program is normally packaged 24 | as “flac” by distributions. 25 | 26 | The script both depends on Bash and GNU features of other core 27 | utilities. Versions of `mv`, `getopt`, `sync`, etc from other sources 28 | (such as the *BSD operating systems) might work, but require 29 | compatibility with the same switches found in the GNU versions. 30 | 31 | reflac has only been developed on tested on GNU+Linux, but should work 32 | on any other operating system with the appropriate tools. 33 | 34 | == Safety 35 | 36 | By default, reflac performs a sync on files after two points of its 37 | process: after moving the recompressed file from the temporary 38 | directory to the original location, and after renaming the file from 39 | “*.new” to the original name. This should provide safety in the case 40 | of system crashes or processes being killed. 41 | 42 | Assuming file system syncs have not been disabled, one of two 43 | scenarios should arise in the worst case: 44 | 45 | 1. No new files appear in the directory. A complete FLAC file may 46 | or may not still be available in a temporary staging directory under 47 | /tmp. 48 | 2. There exists an additional “*.new” file in the directory being 49 | processed, which may or may not be a complete FLAC file, the status 50 | of which can be tested with `flac -t`. 51 | 52 | == Running 53 | 54 | .... 55 | Usage: reflac [OPTION]... [--] DIRECTORY... 56 | 57 | -h --help Displays this help text 58 | -0 --fast Use the fastest, but worst, compression possible. 59 | -1..-7 Adjust FLAC compression between these standard ranges. 60 | The default is -5, the same as for flac itself. 61 | -8 --best Use the slowest, but best, compression possible. 62 | -n --no-action Do not recompress. With --verbose, displays a list of 63 | files that would be processed. 64 | -r --recursive Recurse into directories. 65 | -s --no-sync Do not synchronize file data. Will return faster, with 66 | the potential danger to lose your files in a system crash. 67 | -v --verbose Increases the verbosity. Use once to display the FLACs 68 | currently being processed, use twice for the full ‘flac’ 69 | output. 70 | -V --version Displays the version of this program 71 | 72 | DIRECTORY should point ‘reflac’ to somewhere that contains *.flac 73 | files. Optionally terminate the argument list with -- so that any 74 | possible directory names don’t get misinterpreted as arguments. 75 | .... 76 | 77 | == Bugs 78 | 79 | What, bugs? This program is flawless! Joking aside, although the 80 | author tries to resolve in reflac itself, it is at the mercy of bugs 81 | and limitations from flac and metaflac. 82 | 83 | Tag preservation in the light of malformed tags simply does not exist. 84 | Certain release groups use buggy software with the creation of their 85 | files and will trigger some grievances. 86 | 87 | You might see something like this: 88 | .... 89 | $ reflac FLAC 90 | /tmp/reflac.p8OjPn32z8/1-01 The Strange Green Pipe (Medley) [Mikeaudio].tag: ERROR: malformed vorbis comment field "Super Mario 64: Portrait of a Plumber", 91 | field contains no '=' character 92 | .... 93 | 94 | reflac will not continue after the error, resulting in the 95 | untagged-but-recompressed file remaining in the temporary directory, 96 | the path of which should be part of the error message as in the 97 | example. The original file will not have been overwritten, 98 | maintaining the existing compression as well as the existing tags. 99 | 100 | The choice is left to the user for repairing the file manually, such 101 | as by using `metaflac`, or removing the temporary directory 102 | altogether. The author uses, and recommends, 103 | https://picard.musicbrainz.org/[MusicBrainz Picard] to retag files 104 | before using `reflac`. 105 | -------------------------------------------------------------------------------- /reflac: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | shopt -s dotglob 4 | IFS=$'\n\t' 5 | 6 | VERSION=2.0.1 7 | 8 | VERBOSE=0 9 | RECURSIVE=0 10 | NOACTION=0 11 | SYNC=1 12 | FLAC_LEVEL=5 # Default FLAC compression level 13 | 14 | SELF="$(readlink -f "$0")" 15 | OPTS=$(getopt -o 012345678hnrsvV \ 16 | -l best,fast,help,no-action,no-sync,recursive,verbose,version \ 17 | -n reflac -- "$@") 18 | TMPDIR="$(mktemp --tmpdir -d reflac.XXXXXXXXXX)" 19 | 20 | eval set -- "$OPTS" 21 | 22 | trap "exit 1" INT 23 | 24 | cleanup() 25 | { 26 | rmdir "$TMPDIR" 27 | exit "$1" 28 | } 29 | 30 | usage() 31 | { 32 | cat <&2; cleanup 1 ;; 121 | esac 122 | done 123 | 124 | if [ -z "$*" ]; then 125 | usage 126 | fi 127 | 128 | if [ $VERBOSE -eq 0 ] && [ $NOACTION -eq 1 ]; then 129 | cleanup 0 130 | fi 131 | 132 | for dir; do 133 | REFLAC_OPTS="${FLAC_LEVEL}" 134 | 135 | if [ $SYNC -eq 0 ]; then 136 | REFLAC_OPTS="${REFLAC_OPTS}s" 137 | fi 138 | 139 | if [ $RECURSIVE -eq 1 ] && [ $NOACTION -eq 1 ]; then 140 | find "$(readlink -f -- "$dir")" -type d -execdir "$SELF" -nv {} \; 141 | elif [ $RECURSIVE -eq 1 ]; then 142 | if [ $VERBOSE -eq 1 ]; then 143 | find "$(readlink -f -- "$dir")" -type d \ 144 | -execdir "$SELF" -v"$REFLAC_OPTS" {} \; 145 | elif [ $VERBOSE -ge 2 ]; then 146 | find "$(readlink -f -- "$dir")" -type d \ 147 | -execdir "$SELF" -vv"$REFLAC_OPTS" {} \; 148 | else 149 | find "$(readlink -f -- "$dir")" -type d \ 150 | -execdir "$SELF" -"$REFLAC_OPTS" {} \; 151 | fi 152 | else 153 | pushd -- "$dir" >/dev/null || cleanup $? 154 | if [ $VERBOSE -gt 0 ]; then readlink -f .; fi 155 | if [ -n "$(ls -- *.flac 2>/dev/null)" ]; then 156 | recompress 157 | fi 158 | popd >/dev/null 159 | fi 160 | done 161 | 162 | rmdir "$TMPDIR" 163 | --------------------------------------------------------------------------------