Add uploader scripts and docs
This commit is contained in:
Executable
+180
@@ -0,0 +1,180 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user