Files
fish/functions/copytime.fish
T
2026-08-14 13:39:41 +02:00

268 lines
8.2 KiB
Fish

function copytime -d 'Copy timestamps between files; for media files also copies the container creation date'
argparse -n copytime 'h/help' 'd/date=' -- $argv
or return 2
if set -q _flag_help
__copytime_usage
return 0
end
if test (count $argv) -lt 1
__copytime_usage
return 2
end
__copytime_colors
if set -q _flag_date
set -l rc 0
for f in $argv
__copytime_setdate "$f" "$_flag_date"
or set rc 1
end
return $rc
end
if test (count $argv) -lt 2
__copytime_usage
return 2
end
set -l src $argv[1]
set -l dsts $argv[2..-1]
if not test -e "$src"
__copytime_error "no such source: '$src'"
return 1
end
switch (uname -s)
case Linux
__copytime_linux "$src" $dsts
case Darwin
__copytime_darwin "$src" $dsts
case '*'
__copytime_error "unsupported platform '(uname -s)'"
return 1
end
end
function __copytime_linux
set -l src $argv[1]
set -l rc 0
set -l mtime (stat -c '%y' -- "$src" | string replace -r '\.[0-9]+' '')
set -l atime (stat -c '%x' -- "$src" | string replace -r '\.[0-9]+' '')
echo "$vrot_c_dim source: $src$vrot_c_reset"
echo "$vrot_c_dim modified: $mtime$vrot_c_reset"
echo "$vrot_c_dim accessed: $atime$vrot_c_reset"
set -l birth (stat -c '%w' -- "$src")
if test -n "$birth"; and string match -q '*[-Unknown]*' "$birth"
set birth ''
end
if test -n "$birth"
set -l btime (string replace -r '\.[0-9]+' '' -- "$birth")
echo "$vrot_c_dim created: $btime (filesystem creation time cannot be set on Linux)$vrot_c_reset"
end
set -l created (__copytime_mediadate "$src")
if test -n "$created"
set -l ctime (string replace 'T' ' ' -- "$created" | string replace -r '\.\d+Z?$' '')
echo "$vrot_c_dim container created: $ctime$vrot_c_reset"
end
for dst in $argv[2..-1]
if not test -e "$dst"
__copytime_error "skipping missing destination: '$dst'"
set rc 1
continue
end
touch -a -m -r "$src" "$dst"
if test $status -ne 0
__copytime_error "failed to copy timestamps to '$dst'"
set rc 1
continue
end
set -l dm (stat -c '%y' -- "$dst" | string replace -r '\.[0-9]+' '')
echo "$vrot_c_ok$dst$vrot_c_dim ← modified $dm$vrot_c_reset"
if test -n "$created"
__copytime_meta "$dst" "$created"
or set rc 1
end
end
return $rc
end
function __copytime_darwin
set -l src $argv[1]
set -l rc 0
set -l mtime (stat -f '%Sm' -t '%Y-%m-%d %H:%M:%S' -- "$src")
set -l btime (stat -f '%SB' -t '%Y-%m-%d %H:%M:%S' -- "$src")
echo "$vrot_c_dim source: $src$vrot_c_reset"
echo "$vrot_c_dim modified: $mtime$vrot_c_reset"
echo "$vrot_c_dim created: $btime$vrot_c_reset"
set -l created (__copytime_mediadate "$src")
if test -n "$created"
set -l ctime (string replace 'T' ' ' -- "$created" | string replace -r '\.\d+Z?$' '')
echo "$vrot_c_dim container created: $ctime$vrot_c_reset"
end
set -l birth (stat -f '%SB' -t '%m/%d/%Y %H:%M:%S' -- "$src")
for dst in $argv[2..-1]
if not test -e "$dst"
__copytime_error "skipping missing destination: '$dst'"
set rc 1
continue
end
touch -a -m -r "$src" "$dst"
if test $status -ne 0
set rc 1
continue
end
if command -q SetFile
SetFile -d "$birth" "$dst"
if test $status -ne 0
__copytime_error "failed to set creation time on '$dst'"
set rc 1
end
else
echo "$vrot_c_warn ⚠ SetFile not found — creation time not copied to $dst$vrot_c_reset" >&2
end
set -l db (stat -f '%SB' -t '%Y-%m-%d %H:%M:%S' -- "$dst")
echo "$vrot_c_ok$dst$vrot_c_dim ← created $db$vrot_c_reset"
if test -n "$created"
__copytime_meta "$dst" "$created"
or set rc 1
end
end
return $rc
end
function __copytime_mediadate
if not command -q ffprobe
return 0
end
if not __copytime_media "$argv[1]"
return 0
end
set -l d (ffprobe -v error -show_entries format_tags=creation_time \
-of default=noprint_wrappers=1:nokey=1 "$argv[1]")[1]
string match -rq '^[0-9]{4}-[0-9]{2}-[0-9]{2}' "$d"; and echo "$d"
end
function __copytime_meta
set -l dst $argv[1]
set -l date $argv[2]
if not command -q ffmpeg
return 0
end
if not __copytime_media "$dst"
return 0
end
set -l tmp (string join '' "$dst" .ct. (random) (path extension "$dst"))
ffmpeg -hide_banner -loglevel error -y -i "$dst" -map 0 -map '-0:d' -map '-0:t' \
-c copy -metadata creation_time="$date" "$tmp"
if test $status -ne 0
rm -f "$tmp"
__copytime_error "failed to copy container creation date to '$dst'"
return 1
end
mv -f -- "$tmp" "$dst"
if test $status -ne 0
rm -f "$tmp"
__copytime_error "failed to replace '$dst'"
return 1
end
set -l ctime (string replace 'T' ' ' -- "$date" | string replace -r '\.\d+Z?$' '')
echo "$vrot_c_dim container created: $ctime$vrot_c_reset"
end
function __copytime_setdate
set -l file $argv[1]
set -l date $argv[2]
if not test -e "$file"
__copytime_error "no such file: '$file'"
return 1
end
if not __copytime_media "$file"
__copytime_error "'$file' is not a supported media file"
return 1
end
if not command -q ffmpeg
__copytime_error "ffmpeg is required to set container dates"
return 1
end
set -l tmp (string join '' "$file" .ct. (random) (path extension "$file"))
ffmpeg -hide_banner -loglevel error -y -i "$file" -map 0 -map '-0:d' -map '-0:t' \
-c copy -metadata creation_time="$date" "$tmp"
if test $status -ne 0
rm -f "$tmp"
__copytime_error "failed to write creation date to '$file'"
return 1
end
mv -f -- "$tmp" "$file"
if test $status -ne 0
rm -f "$tmp"
__copytime_error "failed to replace '$file'"
return 1
end
set -l dsp (string replace 'T' ' ' -- "$date")
echo "$vrot_c_ok$file$vrot_c_dim ← container creation date $dsp$vrot_c_reset"
end
function __copytime_media
set -l ext (string lower (path extension "$argv[1]"))
switch "$ext"
case '.mp4' '.mov' '.m4v' '.mkv' '.webm' '.avi' '.ts' '.mts' '.m2ts' '.flv' '.wmv' '.mpg' '.mpeg' '.3gp' '.vob' '.ogv'
return 0
end
return 1
end
function __copytime_colors
set -q vrot_colors_set; and return 0
set -g vrot_colors_set 1
if isatty stdout
set -g vrot_c_ok (set_color brgreen)
set -g vrot_c_dim (set_color brblack)
set -g vrot_c_warn (set_color bryellow)
set -g vrot_c_err (set_color brred)
set -g vrot_c_reset (set_color normal)
else
for c in ok dim warn err reset
set -g vrot_c_$c ''
end
end
end
function __copytime_error
echo "$vrot_c_err""copytime: "$argv"$vrot_c_reset" >&2
end
function __copytime_usage
echo 'usage: copytime <source> <destination> [<destination> ...]'
echo ' copytime --date <DATE> <video> [<video> ...]'
echo
echo 'Copies the access and modified timestamps from <source> to each'
echo '<destination> (same semantics as `touch -r`). On macOS the filesystem'
echo 'creation time is copied too (requires SetFile).'
echo
echo 'For media files (mp4, mkv, mov, webm, ...), the creation date stored'
echo 'inside the container - the one apps like Plex and photo managers show -'
echo 'is copied from source to destination and printed. Data/timecode tracks'
echo 'are dropped while remuxing (lossless, no re-encode).'
echo
echo 'options:'
echo ' -d, --date <DATE> set the container creation date of the given video(s)'
echo ' directly, e.g. "2020-01-02 03:04:05"'
echo ' -h, --help show this help'
end