Files
downloads_uploader/downloads_uploader_install.sh
T
2026-04-30 09:02:36 +02:00

181 lines
6.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════════
# Downloads Auto-Uploader — macOS launchd installer
# Runs downloads_uploader.py every 5 minutes in the background.
#
# Usage:
# ./downloads_uploader_install.sh install # register & start
# ./downloads_uploader_install.sh uninstall # stop & remove
# ./downloads_uploader_install.sh status # show current state
# ./downloads_uploader_install.sh run # manual one-shot run
# ═══════════════════════════════════════════════════════════════════
set -euo pipefail
LABEL="com.user.downloads-uploader"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCAN_DIR="$(dirname "$SCRIPT_DIR")" # parent of uploader/ = Downloads/
PYTHON_SCRIPT="$SCRIPT_DIR/downloads_uploader.py"
PLIST_DIR="$HOME/Library/LaunchAgents"
PLIST_FILE="$PLIST_DIR/$LABEL.plist"
LOG_OUT="$SCRIPT_DIR/uploader_stdout.log"
LOG_ERR="$SCRIPT_DIR/uploader_stderr.log"
INTERVAL=300 # seconds (5 minutes)
# macOS shows the executable name in permission dialogs, not the script name.
# To display "downloads-uploader" instead of "python3.13", we create a symlink
# named "downloads-uploader" pointing to the real python3 binary, and use that
# symlink in the launchd plist.
SYMLINK_DIR="$HOME/.local/bin"
SYMLINK="$SYMLINK_DIR/downloads-uploader"
# Detect python3
PYTHON=$(command -v python3 || command -v python || echo "")
if [[ -z "$PYTHON" ]]; then
echo "❌ python3 not found. Install it: brew install python"
exit 1
fi
# ───────────────────────────────────────────────
generate_plist() {
cat <<PLIST
<?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>Label</key>
<string>$LABEL</string>
<key>ProgramArguments</key>
<array>
<string>$SYMLINK</string>
<string>$PYTHON_SCRIPT</string>
</array>
<key>StartInterval</key>
<integer>$INTERVAL</integer>
<key>RunAtLoad</key>
<true/>
<!-- Pin the working directory so __file__ resolves correctly -->
<key>WorkingDirectory</key>
<string>$SCRIPT_DIR</string>
<key>StandardOutPath</key>
<string>$LOG_OUT</string>
<key>StandardErrorPath</key>
<string>$LOG_ERR</string>
<key>KeepAlive</key>
<false/>
</dict>
</plist>
PLIST
}
# ───────────────────────────────────────────────
cmd_install() {
echo "📦 Installing Downloads Auto-Uploader..."
if [[ ! -f "$PYTHON_SCRIPT" ]]; then
echo "❌ Script not found: $PYTHON_SCRIPT"
exit 1
fi
# Create named symlink so macOS shows "downloads-uploader" in permission dialogs
mkdir -p "$SYMLINK_DIR"
if [[ -L "$SYMLINK" || -e "$SYMLINK" ]]; then
rm -f "$SYMLINK"
fi
ln -s "$PYTHON" "$SYMLINK"
echo "✅ Symlink created: $SYMLINK$PYTHON"
echo " (macOS permission dialogs will now show 'downloads-uploader')"
# Write the absolute scan_directory into the config so launchd's cwd=/
# never affects path resolution inside the Python script.
"$PYTHON" - "$SCRIPT_DIR" "$SCAN_DIR" <<'PYEOF'
import json, sys
cfg_path = sys.argv[1] + "/downloads_uploader_config.json"
scan_dir = sys.argv[2]
with open(cfg_path) as f:
cfg = json.load(f)
cfg["scan_directory"] = scan_dir
with open(cfg_path, "w") as f:
json.dump(cfg, f, indent=2, ensure_ascii=False)
print(f" scan_directory set to: {scan_dir}")
PYEOF
echo "✅ Config updated with absolute scan_directory"
mkdir -p "$PLIST_DIR"
generate_plist > "$PLIST_FILE"
echo "✅ Plist written: $PLIST_FILE"
# Unload first if already loaded
launchctl unload "$PLIST_FILE" 2>/dev/null || true
launchctl load -w "$PLIST_FILE"
echo "✅ Agent loaded. Script will run every $((INTERVAL / 60)) minutes."
echo " Logs → $LOG_OUT"
}
cmd_uninstall() {
echo "🗑 Uninstalling Downloads Auto-Uploader..."
if [[ -f "$PLIST_FILE" ]]; then
launchctl unload -w "$PLIST_FILE" 2>/dev/null || true
rm -f "$PLIST_FILE"
echo "✅ Removed $PLIST_FILE"
else
echo "️ Plist not found — already uninstalled."
fi
if [[ -L "$SYMLINK" ]]; then
rm -f "$SYMLINK"
echo "✅ Removed symlink $SYMLINK"
fi
}
cmd_status() {
echo "── Status ────────────────────────────────"
if launchctl list | grep -q "$LABEL"; then
echo "✅ Agent is RUNNING (label: $LABEL)"
launchctl list "$LABEL" 2>/dev/null || true
else
echo "⏹ Agent is NOT loaded."
fi
echo ""
echo "── Files ─────────────────────────────────"
echo " Script dir : $SCRIPT_DIR"
echo " Scan dir : $(dirname "$SCRIPT_DIR")"
echo " Config : $SCRIPT_DIR/downloads_uploader_config.json"
echo " State : $SCRIPT_DIR/.uploader_state.json"
echo " App log : $SCRIPT_DIR/uploader_log.txt"
echo " Stdout log : $LOG_OUT"
echo " Stderr log : $LOG_ERR"
echo ""
echo "── Recent activity (uploader_log.txt) ────"
LOG_MAIN="$SCRIPT_DIR/uploader_log.txt"
[[ -f "$LOG_MAIN" ]] && tail -25 "$LOG_MAIN" || echo "(no log yet)"
}
cmd_run() {
echo "▶ Running uploader now (one-shot)..."
"$PYTHON" "$PYTHON_SCRIPT"
}
# ───────────────────────────────────────────────
case "${1:-help}" in
install) cmd_install ;;
uninstall) cmd_uninstall ;;
status) cmd_status ;;
run) cmd_run ;;
*)
echo "Usage: $0 {install|uninstall|status|run}"
echo ""
echo " install Register the launchd agent (runs every 5 min)"
echo " uninstall Remove the launchd agent"
echo " status Show if the agent is running + recent log"
echo " run Run the uploader once right now"
;;
esac