summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--photo-compare.rb186
1 files changed, 0 insertions, 186 deletions
diff --git a/photo-compare.rb b/photo-compare.rb
deleted file mode 100644
index 4f5ec4c..0000000
--- a/photo-compare.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-# photo-compare.rb — Side-by-side before/after photo comparison and selection tool.
-#
-# Shows each original + enhanced pair side by side, filling the window.
-# Press O to move the original to --outdir, E to move the enhanced version,
-# Space/S to skip. Rescans after each action so newly finished photos appear.
-#
-# Usage:
-# ruby photo-compare.rb --indir ~/Downloads/fuji --outdir ~/Downloads/fuji/selected
-#
-# Keyboard shortcuts:
-# O — move original to outdir
-# E — move enhanced to outdir
-# Space/S — skip (leave both, advance to next)
-# Q/Escape — quit
-
-require 'gtk4'
-require 'optparse'
-require 'fileutils'
-
-SUPPORTED_EXTENSIONS = %w[.jpg .jpeg .png .webp].freeze
-
-# ---------------------------------------------------------------------------
-# Helpers
-# ---------------------------------------------------------------------------
-
-def find_pairs(indir)
- Dir.glob(File.join(indir, '*'))
- .select { |f| File.file?(f) && SUPPORTED_EXTENSIONS.include?(File.extname(f).downcase) }
- .reject { |f| File.basename(f, '.*').end_with?('_e') }
- .reject { |f| File.basename(f).include?('.orient.') }
- .filter_map do |orig|
- ext = File.extname(orig).downcase # enhanced files always have lowercase ext
- base = File.basename(orig, File.extname(orig))
- enh = File.join(File.dirname(orig), "#{base}_e#{ext}")
- [orig, enh] if File.exist?(enh)
- end
- .sort
-end
-
-def kb(path)
- (File.size(path) / 1024.0).round
-end
-
-# ---------------------------------------------------------------------------
-# CLI
-# ---------------------------------------------------------------------------
-
-options = { indir: nil, outdir: nil }
-OptionParser.new do |o|
- o.banner = 'Usage: ruby photo-compare.rb --indir DIR --outdir DIR'
- o.on('--indir PATH', 'Directory with original + _e photo pairs') { |v| options[:indir] = v }
- o.on('--outdir PATH', 'Directory to move selected photos into') { |v| options[:outdir] = v }
- o.on('-h', '--help', 'Show this help') { puts o; exit }
-end.parse!
-
-abort '--indir is required' unless options[:indir]
-abort '--outdir is required' unless options[:outdir]
-
-indir = File.expand_path(options[:indir])
-outdir = File.expand_path(options[:outdir])
-FileUtils.mkdir_p(outdir)
-
-state = { pairs: find_pairs(indir), index: 0, indir: indir, outdir: outdir }
-abort "No before/after pairs found in #{indir}" if state[:pairs].empty?
-
-# ---------------------------------------------------------------------------
-# GTK4 UI
-# ---------------------------------------------------------------------------
-
-app = Gtk::Application.new('org.hypr.photo-compare', :default_flags)
-
-app.signal_connect('activate') do |a|
- win = Gtk::ApplicationWindow.new(a)
- win.title = 'Photo Compare'
- win.maximize # fill the screen
-
- root = Gtk::Box.new(:vertical, 4)
- root.margin_top = root.margin_bottom = root.margin_start = root.margin_end = 6
- win.child = root
-
- # Top: progress info
- progress_lbl = Gtk::Label.new
- progress_lbl.xalign = 0
- root.append(progress_lbl)
-
- # Middle: two pictures side by side — Gtk::Picture scales to fill its container
- img_row = Gtk::Box.new(:horizontal, 8)
- img_row.vexpand = true
- root.append(img_row)
-
- left_frame = Gtk::Box.new(:vertical, 2)
- right_frame = Gtk::Box.new(:vertical, 2)
- left_frame.hexpand = right_frame.hexpand = true
- left_frame.vexpand = right_frame.vexpand = true
-
- # Gtk::Picture is GTK4's scaling image widget; content_fit: :contain keeps aspect ratio
- left_pic = Gtk::Picture.new
- right_pic = Gtk::Picture.new
- left_pic.content_fit = :contain
- right_pic.content_fit = :contain
- left_pic.hexpand = left_pic.vexpand = true
- right_pic.hexpand = right_pic.vexpand = true
-
- left_lbl = Gtk::Label.new
- right_lbl = Gtk::Label.new
-
- left_frame.append(left_pic)
- left_frame.append(left_lbl)
- right_frame.append(right_pic)
- right_frame.append(right_lbl)
- img_row.append(left_frame)
- img_row.append(right_frame)
-
- # Bottom: action buttons
- btn_row = Gtk::Box.new(:horizontal, 16)
- btn_row.halign = :center
- orig_btn = Gtk::Button.new(label: '← Original [O]')
- skip_btn = Gtk::Button.new(label: 'Skip [Space]')
- enh_btn = Gtk::Button.new(label: 'Enhanced → [E]')
- btn_row.append(orig_btn)
- btn_row.append(skip_btn)
- btn_row.append(enh_btn)
- root.append(btn_row)
-
- # -----------------------------------------------------------------------
- # Refresh display for current pair
- # -----------------------------------------------------------------------
- refresh = lambda do
- orig, enh = state[:pairs][state[:index]]
- progress_lbl.label = "#{state[:index] + 1} / #{state[:pairs].length} — #{File.basename(orig)}"
- left_pic.set_filename(orig)
- right_pic.set_filename(enh)
- left_lbl.label = "Original (#{kb(orig)} KB)"
- right_lbl.label = "Enhanced (#{kb(enh)} KB)"
- end
-
- # -----------------------------------------------------------------------
- # After moving (or skipping), rescan and show next pair.
- # Moving removes the pair from the list, so index stays put and naturally
- # points at the next pair. Skip increments the index explicitly.
- # -----------------------------------------------------------------------
- advance = lambda do |pick|
- unless pick.nil?
- FileUtils.mv(pick, File.join(state[:outdir], File.basename(pick)))
- else
- state[:index] += 1
- end
-
- state[:pairs] = find_pairs(state[:indir])
-
- if state[:index] >= state[:pairs].length
- progress_lbl.label = 'All pairs reviewed — you can close the window.'
- left_pic.set_filename(nil)
- right_pic.set_filename(nil)
- left_lbl.label = right_lbl.label = ''
- [orig_btn, skip_btn, enh_btn].each { |b| b.sensitive = false }
- else
- refresh.call
- end
- end
-
- orig_btn.signal_connect('clicked') { advance.call(state[:pairs][state[:index]][0]) }
- enh_btn.signal_connect('clicked') { advance.call(state[:pairs][state[:index]][1]) }
- skip_btn.signal_connect('clicked') { advance.call(nil) }
-
- key_ctrl = Gtk::EventControllerKey.new
- key_ctrl.signal_connect('key-pressed') do |_ctrl, keyval, _code, _mod|
- case keyval
- when Gdk::Keyval::KEY_o, Gdk::Keyval::KEY_O then orig_btn.emit('clicked')
- when Gdk::Keyval::KEY_e, Gdk::Keyval::KEY_E then enh_btn.emit('clicked')
- when Gdk::Keyval::KEY_s, Gdk::Keyval::KEY_S,
- Gdk::Keyval::KEY_space then skip_btn.emit('clicked')
- when Gdk::Keyval::KEY_q, Gdk::Keyval::KEY_Escape then a.quit
- end
- false
- end
- win.add_controller(key_ctrl)
-
- refresh.call
- win.show
-end
-
-exit app.run([])