[READ-ONLY] Mirror of https://github.com/mrgnw/avify.
0

Configure Feed

Select the types of activity you want to include in your feed.

skip re-encoding originals previously kept as smaller

Kept originals get a com.avify.keep xattr; later runs filter them out at
collection instead of re-encoding to rediscover the growth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

+67 -1
+67 -1
src/main.rs
··· 246 246 247 247 #[cfg(test)] 248 248 mod tests { 249 - use super::{fit_one_row, fmt_savings, is_video}; 249 + use super::{fit_one_row, fmt_savings, has_keep_marker, is_video, set_keep_marker}; 250 250 use std::path::Path; 251 + 252 + #[test] 253 + fn keep_marker_round_trip() { 254 + let f = std::env::temp_dir().join("avify_keep_marker_test"); 255 + std::fs::write(&f, b"x").unwrap(); 256 + assert!(!has_keep_marker(&f)); 257 + set_keep_marker(&f); 258 + assert!(has_keep_marker(&f)); 259 + std::fs::remove_file(&f).unwrap(); 260 + } 251 261 252 262 #[test] 253 263 fn savings_line_shows_orig_ratio_final() { ··· 601 611 .unwrap_or(false) 602 612 } 603 613 614 + // Set on originals whose conversion came out bigger, so later runs skip 615 + // them without re-encoding. Local-only: iCloud eviction can strip it, which 616 + // just costs one wasted re-encode. Clear with: xattr -d com.avify.keep <file> 617 + const KEEP_XATTR: &std::ffi::CStr = c"com.avify.keep"; 618 + 619 + fn path_cstring(path: &Path) -> Option<std::ffi::CString> { 620 + use std::os::unix::ffi::OsStrExt; 621 + std::ffi::CString::new(path.as_os_str().as_bytes()).ok() 622 + } 623 + 624 + fn has_keep_marker(path: &Path) -> bool { 625 + let Some(c) = path_cstring(path) else { 626 + return false; 627 + }; 628 + let n = unsafe { 629 + libc::getxattr( 630 + c.as_ptr(), 631 + KEEP_XATTR.as_ptr(), 632 + std::ptr::null_mut(), 633 + 0, 634 + 0, 635 + 0, 636 + ) 637 + }; 638 + n >= 0 639 + } 640 + 641 + fn set_keep_marker(path: &Path) { 642 + let Some(c) = path_cstring(path) else { 643 + return; 644 + }; 645 + unsafe { 646 + libc::setxattr( 647 + c.as_ptr(), 648 + KEEP_XATTR.as_ptr(), 649 + b"1".as_ptr().cast(), 650 + 1, 651 + 0, 652 + 0, 653 + ); 654 + } 655 + } 656 + 604 657 fn video_out_path(path: &Path, outdir: Option<&Path>) -> PathBuf { 605 658 let name = format!( 606 659 "{}.av1.mp4", ··· 791 844 if orig_bytes > 0 && out_bytes as u64 >= orig_bytes { 792 845 fs::remove_file(&out_path) 793 846 .with_context(|| format!("Failed to remove {}", out_path.display()))?; 847 + set_keep_marker(path); 794 848 let mut p = progress.lock().unwrap(); 795 849 p.set( 796 850 idx, ··· 933 987 if args.files.is_empty() { 934 988 anyhow::bail!("No image files found in given paths"); 935 989 } 990 + } 991 + 992 + let before = args.files.len(); 993 + args.files.retain(|p| !has_keep_marker(p)); 994 + let marked = before - args.files.len(); 995 + if marked > 0 { 996 + eprintln!( 997 + "\x1b[33m{marked} file(s) skipped — previously kept as smaller than conversion (xattr -d com.avify.keep to retry)\x1b[0m" 998 + ); 999 + } 1000 + if args.files.is_empty() { 1001 + return Ok(()); 936 1002 } 937 1003 938 1004 if let Some(ref dir) = args.outdir {