feat: initial commit

This commit is contained in:
2026-05-02 20:38:51 +01:00
commit 6bea18e295
14 changed files with 979 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>Photon Uploader</string>
<key>CFBundleDisplayName</key>
<string>Photon Uploader</string>
<key>CFBundleIdentifier</key>
<string>dev.jrdn.PhotonUploader</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>LSArchitecturePriority</key>
<array>
<string>arm64</string>
</array>
<key>CFBundleExecutable</key>
<string>upload</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
</dict>
</plist>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>Photon Uploader</string>
<key>CFBundleDisplayName</key>
<string>Photon Uploader</string>
<key>CFBundleIdentifier</key>
<string>dev.jrdn.PhotonUploaderHelper</string>
<key>CFBundleExecutable</key>
<string>PhotonNotify</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSUIElement</key>
<true/>
<key>LSBackgroundOnly</key>
<true/>
<key>NSUserNotificationAlertStyle</key>
<string>banner</string>
</dict>
</plist>
+43
View File
@@ -0,0 +1,43 @@
import AppKit
import UserNotifications
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let args = ProcessInfo.processInfo.arguments
let subtitle = args.count > 1 ? args[1] : ""
let body = args.count > 2 ? args[2] : ""
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { granted, _ in
guard granted else {
DispatchQueue.main.async { NSApp.terminate(nil) }
return
}
let content = UNMutableNotificationContent()
content.title = "Photon Uploader"
content.subtitle = subtitle
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger
)
center.add(request) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
NSApp.terminate(nil)
}
}
}
}
}
let app = NSApplication.shared
app.setActivationPolicy(.prohibited)
let delegate = AppDelegate()
app.delegate = delegate
app.run()
+71
View File
@@ -0,0 +1,71 @@
import AppKit
class AppDelegate: NSObject, NSApplicationDelegate {
var statusItem: NSStatusItem?
var scriptProcess: Process?
func applicationDidFinishLaunching(_ notification: Notification) {
app.setActivationPolicy(.prohibited)
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
if let button = statusItem?.button {
let bundle = Bundle.main
var icon: NSImage?
if let url = bundle.url(forResource: "MenuBarIcon@2x", withExtension: "png"),
let img = NSImage(contentsOf: url) {
img.isTemplate = true
icon = img
} else if let url = bundle.url(forResource: "MenuBarIcon", withExtension: "png"),
let img = NSImage(contentsOf: url) {
img.isTemplate = true
icon = img
}
if let icon = icon {
button.image = icon
} else {
button.title = ""
}
}
let menu = NSMenu()
let openLog = NSMenuItem(title: "Open Log", action: #selector(openLog), keyEquivalent: "")
openLog.target = self
menu.addItem(openLog)
menu.addItem(NSMenuItem.separator())
let quit = NSMenuItem(title: "Quit Photon Uploader", action: #selector(quitApp), keyEquivalent: "q")
quit.target = self
menu.addItem(quit)
statusItem?.menu = menu
launchScript()
}
func launchScript() {
guard let resourcePath = Bundle.main.resourcePath else { return }
let scriptDir = (resourcePath as NSString).deletingLastPathComponent
let scriptPath = "\(scriptDir)/MacOS/upload.sh"
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/bin/bash")
proc.arguments = [scriptPath, "--background"]
try? proc.run()
scriptProcess = proc
}
@objc func openLog() {
let logPath = (NSHomeDirectory() as NSString)
.appendingPathComponent("Documents/Photon/.photon-upload.log")
NSWorkspace.shared.open(URL(fileURLWithPath: logPath))
}
@objc func quitApp() {
scriptProcess?.terminate()
app.terminate(nil)
}
}
let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate
app.run()
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+639
View File
@@ -0,0 +1,639 @@
#!/bin/bash
set -euo pipefail
# Configuration
SCRIPT_DIR="$HOME/Documents/Photon"
CONFIG_FILE="${SCRIPT_DIR}/photon.conf"
LOCKFILE="${SCRIPT_DIR}/.photon-upload.lock"
LOGFILE="${SCRIPT_DIR}/photon-upload.log"
# When launched as a GUI app there is no TTY — redirect stdout/stderr to the
# log file immediately so nothing tries to write to a closed fd.
mkdir -p "$SCRIPT_DIR"
exec >>"$LOGFILE" 2>&1
# Ensure Homebrew binaries are in PATH when launched as a GUI app
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
# Debug: Log script invocation
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Script invoked with args: $*" >> /tmp/photon-upload-debug.log
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') PID: $$, PPID: $PPID" >> /tmp/photon-upload-debug.log
# Load configuration (mandatory)
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] Config file not found: $CONFIG_FILE" >> "$LOGFILE"
osascript -e "display alert \"Photon Uploader\" message \"Config file not found:\\n$CONFIG_FILE\" as critical buttons {\"OK\"} default button \"OK\""
exit 1
fi
source "$CONFIG_FILE"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] Config loaded: $CONFIG_FILE" >> "$LOGFILE"
# Logging function
log() {
local level="$1"
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >> "$LOGFILE"
}
# Check dependencies
check_dependencies() {
local missing_deps=()
log "INFO" "Checking dependencies..."
for cmd in fswatch openssl ssh scp; do
if ! command -v "$cmd" >/dev/null 2>&1; then
missing_deps+=("$cmd")
fi
done
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log "ERROR" "Missing dependencies: ${missing_deps[*]}"
log "INFO" "Install with: brew install ${missing_deps[*]}"
exit 1
fi
log "INFO" "All dependencies found"
}
# Create necessary directories
setup_directories() {
log "INFO" "Setting up directories..."
for dir in "$DIR_TO_MONITOR" "$DIR_TO_MOVE" "$DIR_ARCHIVE"; do
if [[ ! -d "$dir" ]]; then
log "INFO" "Creating directory: $dir"
mkdir -p "$dir" || {
log "ERROR" "Failed to create directory: $dir"
exit 1
}
fi
done
log "INFO" "Directory setup complete"
}
# Function to generate a random filename
# generate_random_filename() {
# local extension="$1"
# local timestamp=$(date +%s)
# local random_part=$(openssl rand -hex 4)
# echo "${timestamp}_${random_part}.${extension}"
# }
generate_random_filename() {
local extension="$1"
local random_part=$(openssl rand -base64 6 | tr -dc 'A-Za-z' | head -c 8)
echo "${random_part}.${extension}"
}
# Send system notification
send_notification() {
if [[ "$ENABLE_NOTIFICATIONS" != "true" ]]; then return; fi
local NOTIFY_HELPER
NOTIFY_HELPER="$(dirname "$(dirname "$0")")/Helpers/PhotonNotify.app"
if [[ -d "$NOTIFY_HELPER" ]]; then
open -n -jg "$NOTIFY_HELPER" --args "$1" "$2"
elif command -v osascript >/dev/null; then
osascript -e "display notification \"$2\" with title \"Photon Uploader\" subtitle \"$1\""
fi
}
# Check if file extension is supported
is_supported_file() {
local file="$1"
local extension="${file##*.}"
# Convert to lowercase (bash 3.2 compatible)
extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
[[ ",$SUPPORTED_EXTENSIONS," == *",$extension,"* ]]
}
# Validate SSH key authentication setup
validate_ssh_key() {
if [[ ! -f "$SSH_KEY_PATH" ]]; then
log "ERROR" "SSH key not found at: $SSH_KEY_PATH"
log "INFO" "Generate SSH key with: ssh-keygen -t rsa -b 4096 -f $SSH_KEY_PATH -N ''"
log "INFO" "Then copy public key to server: ssh-copy-id -i ${SSH_KEY_PATH}.pub -p $PORT $USER@$SERVER"
return 1
fi
# Check key permissions
local key_perms=$(stat -f "%Lp" "$SSH_KEY_PATH" 2>/dev/null || echo "000")
if [[ "$key_perms" != "600" ]]; then
log "WARN" "Fixing SSH key permissions: $SSH_KEY_PATH"
chmod 600 "$SSH_KEY_PATH"
fi
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] Using SSH key authentication: $SSH_KEY_PATH" >> "$LOGFILE"
return 0
}
# Function to process uploaded files
process_file() {
local file_path="$1"
local file_name=$(basename "$file_path")
# Skip hidden files and directories
if [[ "$file_name" == .* ]] || [[ -d "$file_path" ]]; then
return 0
fi
# Check if file exists and is readable
if [[ ! -f "$file_path" ]] || [[ ! -r "$file_path" ]]; then
log "WARN" "File not accessible: $file_path"
return 1
fi
# Check if file is already being processed
if is_file_being_processed "$file_path"; then
log "INFO" "File already being processed: $file_path"
return 0
fi
# Check if file type is supported
if ! is_supported_file "$file_name"; then
log "INFO" "Unsupported file type: $file_path"
mark_processing_complete "$file_path"
return 0
fi
# Wait for file to be completely written (common with screenshots)
local file_size=0
local new_size=1
while [[ $file_size -ne $new_size ]]; do
file_size=$new_size
sleep 0.5
new_size=$(stat -f%z "$file_path" 2>/dev/null || echo 0)
done
log "INFO" "Processing file: $file_path"
local extension="${file_name##*.}"
local new_filename=$(generate_random_filename "$extension")
local staged_path="$DIR_TO_MOVE/$new_filename"
# Move file to staging area
if ! cp "$file_path" "$staged_path"; then
log "ERROR" "Failed to stage file: $file_path"
mark_processing_complete "$file_path"
return 1
fi
# Upload file
if upload_file "$staged_path" "$new_filename"; then
mark_processing_complete "$file_path"
return 0
else
mark_processing_complete "$file_path"
return 1
fi
}
# Upload file with improved error handling
upload_file() {
local file_path="$1"
local filename="$2"
local url="$BASE_URL/$filename"
log "INFO" "Uploading: $filename"
# Validate SSH key before attempting upload
if ! validate_ssh_key; then
log "ERROR" "SSH key validation failed"
return 1
fi
# Upload using SSH key authentication
if scp -P "$PORT" -i "$SSH_KEY_PATH" -o ConnectTimeout=10 -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$file_path" "$USER@$SERVER:$REMOTE_DIR"; then
upload_success=true
else
upload_success=false
fi
if [[ "$upload_success" == "true" ]]; then
log "INFO" "Upload successful: $filename"
# Copy URL to clipboard
echo "$url" | pbcopy
log "INFO" "URL copied to clipboard: $url"
# Send notification
send_notification "" "$filename uploaded successfully"
generate_thumbnails
# Archive or cleanup
if [[ "$CLEANUP_AFTER_UPLOAD" == "true" ]]; then
if [[ -d "$DIR_ARCHIVE" ]]; then
mv "$file_path" "$DIR_ARCHIVE/"
log "INFO" "File archived: $filename"
else
rm "$file_path"
log "INFO" "File removed: $filename"
fi
fi
return 0
else
log "ERROR" "Upload failed: $filename"
send_notification "Failed" "Upload failed for $filename"
return 1
fi
}
generate_thumbnails() {
log "INFO" "Requesting thumbnail generation..."
if [[ -z "${PHOTON_DOMAIN:-}" ]]; then
log "ERROR" "PHOTON_DOMAIN not configured"
return 1
fi
local response
if response=$(curl -s "https://${PHOTON_DOMAIN}/job/generate_thumbnails" 2>&1); then
log "INFO" "Thumbnail generation response: $response"
# Parse JSON response to check status
if echo "$response" | grep -q '"status":"success"'; then
log "INFO" "Thumbnail generation completed successfully"
return 0
else
log "WARN" "Thumbnail generation may have failed: $response"
return 1
fi
else
log "ERROR" "Failed to connect to thumbnail service: $response"
return 1
fi
}
# Handle concurrent uploads (bash 3.2 compatible)
ACTIVE_UPLOADS_DIR="${SCRIPT_DIR}/.active_uploads"
PROCESSING_DIR="${SCRIPT_DIR}/.processing"
# Function to check if file is already being processed
is_file_being_processed() {
local file_path="$1"
local file_hash=$(echo "$file_path" | shasum -a 256 | cut -d' ' -f1)
local processing_file="$PROCESSING_DIR/$file_hash"
# Check if processing file exists and is recent (less than 60 seconds old)
if [[ -f "$processing_file" ]]; then
local processing_time=$(stat -f%m "$processing_file" 2>/dev/null || echo 0)
local current_time=$(date +%s)
if (( current_time - processing_time < 60 )); then
return 0 # Still being processed
else
rm -f "$processing_file" 2>/dev/null || true # Stale processing file
fi
fi
# Create processing marker atomically to prevent race conditions
mkdir -p "$PROCESSING_DIR"
if (set -C; echo $$ > "$processing_file") 2>/dev/null; then
return 1 # Not being processed, marker created
else
return 0 # Already being processed by another instance
fi
}
# Function to mark file processing as complete
mark_processing_complete() {
local file_path="$1"
local file_hash=$(echo "$file_path" | shasum -a 256 | cut -d' ' -f1)
local processing_file="$PROCESSING_DIR/$file_hash"
rm -f "$processing_file" 2>/dev/null || true
}
# Signal handlers
cleanup() {
log "INFO" "Shutting down..."
[[ -f "$LOCKFILE" ]] && rm -f "$LOCKFILE"
# Kill any fswatch processes monitoring our directory
if command -v pkill >/dev/null 2>&1; then
pkill -f "fswatch.*$DIR_TO_MONITOR" 2>/dev/null || true
fi
# Wait for active uploads to complete
if [[ -d "$ACTIVE_UPLOADS_DIR" ]]; then
for upload_file in "$ACTIVE_UPLOADS_DIR"/*; do
[[ -f "$upload_file" ]] || continue
local pid=$(basename "$upload_file")
if kill -0 "$pid" 2>/dev/null; then
local filename=""
if [[ -r "$upload_file" ]]; then
filename=$(cat "$upload_file" 2>/dev/null || echo "unknown")
else
filename="unknown"
fi
log "INFO" "Waiting for upload to complete: $filename"
wait "$pid" 2>/dev/null || true
fi
rm -f "$upload_file" 2>/dev/null || true
done
rmdir "$ACTIVE_UPLOADS_DIR" 2>/dev/null || true
fi
# Clean up processing directory
if [[ -d "$PROCESSING_DIR" ]]; then
rm -rf "$PROCESSING_DIR" 2>/dev/null || true
fi
exit 0
}
# Main function
main() {
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Entering main function" >> /tmp/photon-upload-debug.log
log "INFO" "Starting screenshot upload service"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Checking for existing lockfile" >> /tmp/photon-upload-debug.log
# Enhanced lock creation with better race condition protection
local max_attempts=5
local attempt=1
local lock_acquired=false
while [[ $attempt -le $max_attempts ]] && [[ "$lock_acquired" == "false" ]]; do
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Lock attempt $attempt" >> /tmp/photon-upload-debug.log
if (set -C; echo $$ > "$LOCKFILE") 2>/dev/null; then
lock_acquired=true
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Lock acquired on attempt $attempt" >> /tmp/photon-upload-debug.log
else
existing_pid=$(cat "$LOCKFILE" 2>/dev/null || echo "")
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Lock exists, checking PID: $existing_pid" >> /tmp/photon-upload-debug.log
if [[ -n "$existing_pid" ]] && kill -0 "$existing_pid" 2>/dev/null; then
log "ERROR" "Another instance is already running (PID: $existing_pid)"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Existing process $existing_pid is alive, exiting" >> /tmp/photon-upload-debug.log
exit 1
else
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Stale lock file, removing" >> /tmp/photon-upload-debug.log
rm -f "$LOCKFILE" 2>/dev/null || true
sleep 0.1
fi
fi
((attempt++))
done
if [[ "$lock_acquired" == "false" ]]; then
log "ERROR" "Unable to acquire lockfile after $max_attempts attempts"
exit 1
fi
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Created lockfile with PID: $$" >> /tmp/photon-upload-debug.log
log "INFO" "Created lockfile with PID: $$"
# Set up signal handlers for this monitoring process only
trap cleanup SIGINT SIGTERM EXIT
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Calling check_dependencies" >> /tmp/photon-upload-debug.log
# Check dependencies and setup
check_dependencies
setup_directories
# Validate SSH key authentication
log "INFO" "Validating SSH key authentication..."
if ! validate_ssh_key; then
log "ERROR" "SSH key authentication setup failed"
exit 1
fi
log "INFO" "SSH key authentication validated successfully"
# Create active uploads tracking directory
mkdir -p "$ACTIVE_UPLOADS_DIR"
log "INFO" "Created tracking directory: $ACTIVE_UPLOADS_DIR"
log "INFO" "Monitoring directory: $DIR_TO_MONITOR"
log "INFO" "Supported file types: $SUPPORTED_EXTENSIONS"
# Test fswatch before starting the main loop
log "INFO" "Testing fswatch compatibility..."
if ! fswatch --version >/dev/null 2>&1; then
log "ERROR" "fswatch not working properly"
exit 1
fi
log "INFO" "fswatch test passed"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') About to start fswatch monitoring" >> /tmp/photon-upload-debug.log
log "INFO" "Starting file monitoring with fswatch..."
# Create named pipe for fswatch to avoid subshell issues
local fswatch_pipe="/tmp/fswatch-$$"
mkfifo "$fswatch_pipe"
# Start fswatch in background writing to pipe
fswatch -0 "$DIR_TO_MONITOR" > "$fswatch_pipe" &
local fswatch_pid=$!
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Started fswatch PID: $fswatch_pid" >> /tmp/photon-upload-debug.log
# Cleanup function for pipe
cleanup_pipe() {
kill -TERM "$fswatch_pid" 2>/dev/null || true
rm -f "$fswatch_pipe"
}
trap cleanup_pipe EXIT
# Read events from pipe without subshell
while read -d "" event; do
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') fswatch event: $event" >> /tmp/photon-upload-debug.log
# Skip directories and hidden files immediately
if [[ -d "$event" ]] || [[ "$(basename "$event")" == .* ]]; then
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Skipping directory/hidden file: $event" >> /tmp/photon-upload-debug.log
continue
fi
# Add small delay to allow multiple rapid events to settle
sleep 0.2
# Limit concurrent uploads
while true; do
local active_count=0
if [[ -d "$ACTIVE_UPLOADS_DIR" ]]; then
for upload_file in "$ACTIVE_UPLOADS_DIR"/*; do
[[ -f "$upload_file" ]] || continue
local pid=$(basename "$upload_file")
if kill -0 "$pid" 2>/dev/null; then
((active_count++))
else
rm -f "$upload_file"
fi
done
fi
[[ $active_count -lt $MAX_CONCURRENT_UPLOADS ]] && break
sleep 0.1
done
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Starting background process for: $event" >> /tmp/photon-upload-debug.log
# Process file in background
(
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Background worker PID $$ processing: $event" >> /tmp/photon-upload-debug.log
process_file "$event"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Background worker PID $$ finished: $event" >> /tmp/photon-upload-debug.log
rm -f "$ACTIVE_UPLOADS_DIR/$$" 2>/dev/null || true
) &
local upload_pid=$!
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Spawned upload worker: $upload_pid for $(basename "$event")" >> /tmp/photon-upload-debug.log
echo "$(basename "$event")" > "$ACTIVE_UPLOADS_DIR/$upload_pid"
done < "$fswatch_pipe"
}
# Help function
show_help() {
cat << EOF
Screenshot Upload Service
Usage: $0 [OPTIONS]
Options:
-h, --help Show this help message
-c, --config Specify config file path
-d, --daemon Run in background
-s, --stop Stop running daemon
-t, --test Test upload with a sample file
--status Show service status and related processes
Configuration:
Edit $CONFIG_FILE to customize settings
EOF
}
# Parse command line arguments
BACKGROUND_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-c|--config)
CONFIG_FILE="$2"
shift 2
;;
-d|--daemon)
# Ensure log directory exists
mkdir -p "$(dirname "$LOGFILE")"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Starting daemon mode" >> /tmp/photon-upload-debug.log
# Start daemon with logging
nohup "$0" --background >> "$LOGFILE" 2>&1 &
daemon_pid=$!
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Spawned background process: $daemon_pid" >> /tmp/photon-upload-debug.log
echo "Started in background with PID: $daemon_pid"
echo "Monitor with: tail -f $LOGFILE"
exit 0
;;
--background)
# Internal flag used by daemon mode - don't document in help
# This prevents infinite daemon loops
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') Background mode activated, breaking from arg parse" >> /tmp/photon-upload-debug.log
BACKGROUND_MODE=true
shift
break
;;
-s|--stop)
if [[ -f "$LOCKFILE" ]]; then
pid=$(cat "$LOCKFILE")
if kill -0 "$pid" 2>/dev/null; then
echo "Stopping service with PID: $pid"
kill -TERM "$pid"
# Also kill any related fswatch processes
if command -v pkill >/dev/null 2>&1; then
pkill -f "fswatch.*$DIR_TO_MONITOR" 2>/dev/null || true
echo "Stopped related fswatch processes"
fi
# Wait a moment for graceful shutdown
sleep 2
# Force kill if still running
if kill -0 "$pid" 2>/dev/null; then
kill -KILL "$pid" 2>/dev/null || true
echo "Force killed process $pid"
fi
echo "Service stopped"
else
echo "Service not running (PID $pid not found)"
rm -f "$LOCKFILE"
fi
else
echo "No lockfile found, checking for orphaned processes..."
# Look for orphaned upload or fswatch processes
if command -v pgrep >/dev/null 2>&1; then
orphaned_pids=$(pgrep -f "upload.*background|fswatch.*$DIR_TO_MONITOR" 2>/dev/null || true)
if [[ -n "$orphaned_pids" ]]; then
echo "Found orphaned processes: $orphaned_pids"
kill -TERM $orphaned_pids 2>/dev/null || true
sleep 2
kill -KILL $orphaned_pids 2>/dev/null || true
echo "Cleaned up orphaned processes"
else
echo "No running processes found"
fi
else
echo "Service may not be running"
fi
fi
exit 0
;;
-t|--test)
echo "Testing upload functionality..."
check_dependencies
# Create a test file
test_file="/tmp/test_$(date +%s).txt"
echo "Test upload $(date)" > "$test_file"
process_file "$test_file"
rm -f "$test_file"
exit 0
;;
--status)
echo "Screenshot Upload Service Status"
echo "================================"
if [[ -f "$LOCKFILE" ]]; then
lock_pid=$(cat "$LOCKFILE")
echo "Lock file: $LOCKFILE (PID: $lock_pid)"
if kill -0 "$lock_pid" 2>/dev/null; then
echo "Main process: RUNNING (PID: $lock_pid)"
else
echo "Main process: DEAD (stale lock file)"
fi
else
echo "Lock file: NOT FOUND"
fi
echo ""
echo "Related processes:"
if ps aux | grep -E "(upload|fswatch.*$DIR_TO_MONITOR)" | grep -v grep | grep -v status; then
true
else
echo "No related processes found"
fi
echo ""
if [[ -d "$ACTIVE_UPLOADS_DIR" ]]; then
upload_count=$(ls -1 "$ACTIVE_UPLOADS_DIR" 2>/dev/null | wc -l | tr -d ' ')
echo "Active uploads: $upload_count"
else
echo "Active uploads: 0"
fi
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Run main function
main