Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ Thumbs.db
.vscode/
.idea/

prompt.md
prompt.md
WARP.md
223 changes: 0 additions & 223 deletions WARP.md

This file was deleted.

15 changes: 0 additions & 15 deletions faststack/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,6 @@
- **Ctrl+0 Zoom Reset:** Added keyboard shortcut to reset zoom and pan to fit window (like Photoshop), with visual feedback.
- **Active Filter Indicator:** Footer now displays active filename filter in yellow bold text for better visibility.
- **Directory Path Display:** Title bar now shows the current working directory path, centered between menu and window controls.
- **WARP.md Documentation:** Created comprehensive development guide for Warp AI assistant with architecture, commands, and development patterns.

### Security
- **Executable Path Validation:** Added comprehensive validation for Photoshop and Helicon Focus executables:
- Validates executable exists and is in safe location (Program Files)
- Checks file type (.exe on Windows)
- Warns about executables outside known safe paths
- Detects directory traversal attempts
- **Subprocess Security Hardening:**
- Replaced unsafe `.split()` with `shlex.split()` for proper argument parsing
- Explicitly set `shell=False` to prevent shell injection attacks
- Added file descriptor management (`stdin`, `stdout`, `stderr` redirection)
- Validates file paths before subprocess execution
- Uses `close_fds=True` to prevent information leakage
- **Input Validation:** Added pre-execution validation for image file paths in both Photoshop and Helicon Focus launchers.

### Fixed
- **Property Name Mismatch:** Corrected `get_stack_summary` to `stackSummary` in UIState to match QML property naming conventions.
Expand Down
2 changes: 1 addition & 1 deletion faststack/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# FastStack

# Version 0.4 - November 2, 2025
# Version 0.7 - November 20, 2025
# By Alan Rockefeller

Ultra-fast, caching JPG viewer designed for culling and selecting RAW files for focus stacking.
Expand Down
12 changes: 10 additions & 2 deletions faststack/faststack.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: faststack
Version: 0.6
Version: 0.7
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Version bump is correct, but PKG-INFO should not be manually edited.

The version bump to 0.7 is consistent with the release. However, PKG-INFO is auto-generated during package builds and this file should not be manually edited or committed to version control.

The faststack.egg-info/ directory is a build artifact that should be excluded from version control. Instead, update the version in your source files (e.g., setup.py, pyproject.toml, or __init__.py) and let the build process regenerate PKG-INFO.

Add to .gitignore:

*.egg-info/

Then remove the directory from the repository:

git rm -r faststack/faststack.egg-info/

Summary: Ultra-fast JPG Viewer for Focus Stacking Selection
Author-email: Alan Rockefeller <alanrockefeller@gmail.com>
Classifier: Programming Language :: Python :: 3
Expand All @@ -21,7 +21,7 @@ Dynamic: license-file

# FastStack

# Version 0.4 - November 2, 2025
# Version 0.7 - November 20, 2025
# By Alan Rockefeller

Ultra-fast, caching JPG viewer designed for culling and selecting RAW files for focus stacking.
Expand All @@ -38,6 +38,11 @@ This tool is optimized for speed, using `libjpeg-turbo` for decoding, aggressive
- **Helicon Focus Integration:** Launch Helicon Focus with your selected RAW files with a single keypress (`Enter`).
- **Sidecar Metadata:** Saves flags, rejections, and stack groupings to a non-destructive `faststack.json` file.
- **Configurable:** Adjust cache sizes, prefetch behavior, and Helicon Focus path via a settings dialog and a persistent `.ini` file.
- **Photoshop Integration:** Edit current image in Photoshop (E key)
- **Clipboard Support:** Copy image path to clipboard (Ctrl+C)
- **Image Filtering:** Filter images by filename
- **Drag & Drop:** Drag images to external applications
- **Theme Support:** Toggle between light and dark themes

## Installation & Usage

Expand All @@ -62,3 +67,6 @@ This tool is optimized for speed, using `libjpeg-turbo` for decoding, aggressive
- `Space`: Toggle Flag
- `X`: Toggle Reject
- `Enter`: Launch Helicon Focus with selected RAWs
- `E`: Edit in Photoshop
- `Ctrl+C`: Copy image path to clipboard
- `C`: Clear all stacks
11 changes: 6 additions & 5 deletions faststack/faststack/imaging/jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

# Attempt to import PyTurboJPEG
try:
from turbojpeg import TurboJPEG, TJFLAG_FASTDCT, TJPF_RGB
from turbojpeg import TurboJPEG, TJPF_RGB
jpeg_decoder = TurboJPEG()
TURBO_AVAILABLE = True
log.info("PyTurboJPEG is available. Using for JPEG decoding.")
Expand All @@ -23,8 +23,9 @@ def decode_jpeg_rgb(jpeg_bytes: bytes) -> Optional[np.ndarray]:
"""Decodes JPEG bytes into an RGB numpy array."""
if TURBO_AVAILABLE and jpeg_decoder:
try:
# The flags prevent upsampling of chroma channels, which is faster.
return jpeg_decoder.decode(jpeg_bytes, pixel_format=TJPF_RGB, flags=TJFLAG_FASTDCT)
# Decode with proper color space handling (no TJFLAG_FASTDCT)
# This ensures proper YCbCr->RGB conversion with correct gamma
return jpeg_decoder.decode(jpeg_bytes, pixel_format=TJPF_RGB, flags=0)
except Exception as e:
log.exception(f"PyTurboJPEG failed to decode image: {e}. Trying Pillow.")
# Fall through to Pillow fallback
Expand Down Expand Up @@ -55,7 +56,7 @@ def decode_jpeg_thumb_rgb(
jpeg_bytes,
scaling_factor=scaling_factor,
pixel_format=TJPF_RGB,
flags=TJFLAG_FASTDCT,
flags=0, # Proper color space handling
)
if decoded.shape[0] > max_dim or decoded.shape[1] > max_dim:
img = Image.fromarray(decoded)
Expand Down Expand Up @@ -115,7 +116,7 @@ def decode_jpeg_resized(
jpeg_bytes,
scaling_factor=scale_factor,
pixel_format=TJPF_RGB,
flags=TJFLAG_FASTDCT
flags=0 # Proper color space handling
)

# Only use Pillow for final resize if needed
Expand Down
6 changes: 3 additions & 3 deletions faststack/faststack/io/executable_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def validate_executable_path(
try:
path = Path(exe_path).resolve()
except (ValueError, OSError) as e:
log.error(f"Invalid path format: {exe_path}: {e}")
log.exception(f"Invalid path format: {exe_path}")
return False, f"Invalid path format: {e}"

# Check if file exists
Expand Down Expand Up @@ -88,8 +88,8 @@ def validate_executable_path(
normalized = os.path.normpath(exe_path)
if ".." in normalized or normalized != str(path):
log.warning(f"Suspicious path detected: {exe_path}")
except Exception as e:
log.error(f"Error normalizing path: {e}")
except (ValueError, OSError) as e:
log.exception("Error normalizing path")
return False, f"Path validation error: {e}"

return True, None
Expand Down
2 changes: 1 addition & 1 deletion faststack/faststack/io/helicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def launch_helicon_focus(raw_files: List[Path]) -> Tuple[bool, Optional[Path]]:
parsed_args = shlex.split(extra_args, posix=(os.name != 'nt'))
args.extend(parsed_args)
except ValueError as e:
log.error(f"Invalid helicon args format: {e}")
log.exception(f"Invalid helicon args format: {e}")
return False, None

log.info(f"Launching Helicon Focus with {len(raw_files)} files.")
Expand Down
Loading