mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:27:13 +01:00
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018RHwVPyiiQMPh4M3JQwQku
103 lines
3.7 KiB
Bash
Executable File
103 lines
3.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Credit a RunnerUp run the moment its TCX upload lands on this machine.
|
|
#
|
|
# RunnerUp PUTs each export to dufs, which writes it straight into
|
|
# ~/data/cloud/RunnerUp: CREATE fires when the upload starts, CLOSE_WRITE when
|
|
# it ends. So this reacts to close_write/moved_to only -- a systemd .path unit
|
|
# (PathChanged=) fires on CREATE, hands the pass a half-written TCX and drops
|
|
# the close event while that pass runs, which left a 2026-10-07 run uncredited
|
|
# until the next timer tick.
|
|
#
|
|
# Each finished *.tcx starts workout-sync.service (blocking), which ingests it
|
|
# and applies the shutdown credit. systemd serialises that against the 15-min
|
|
# timer run; when one was already in flight it started before this file was
|
|
# complete, so the start merged into it and a second start is needed.
|
|
#
|
|
# Only a FRESH top-level *.tcx counts. endurain-import's adb fallback re-pulls
|
|
# every phone export it does not see at the top level -- including the ones it
|
|
# already moved into processed/ -- and renames each into place, so every
|
|
# 15-min import used to fire ~27 moved_to events and as many workout-sync
|
|
# starts (~370 on 2026-10-09). A name already in processed/ was handled
|
|
# before, and dotfiles are partial transfers; both are skipped with zero forks.
|
|
#
|
|
# Run by runnerup-watch.service (Restart=always). Event-driven: no polling.
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
readonly WATCH_DIR="${RUNNERUP_WATCH_DIR:-$HOME/data/cloud/RunnerUp}"
|
|
readonly SYNC_UNIT="${RUNNERUP_SYNC_UNIT:-workout-sync.service}"
|
|
readonly PROCESSED_DIR="$WATCH_DIR/processed"
|
|
|
|
log() {
|
|
printf '%s\n' "$*" >&2
|
|
}
|
|
|
|
start_sync() {
|
|
if ! systemctl --user start "$SYNC_UNIT"; then
|
|
log "WARNING: $SYNC_UNIT failed after a RunnerUp upload — the run" \
|
|
"is NOT credited yet; see journalctl --user -u $SYNC_UNIT." \
|
|
"The 15-min timer retries it."
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
ingest() {
|
|
local name="$1" state
|
|
state="$(systemctl --user is-active "$SYNC_UNIT" || true)"
|
|
log "RunnerUp upload finished: $name — starting $SYNC_UNIT"
|
|
start_sync || return 0
|
|
if [[ $state == activating ]]; then
|
|
log "$SYNC_UNIT was already running when $name landed — running it again"
|
|
start_sync || return 0
|
|
fi
|
|
}
|
|
|
|
# is_fresh_upload NAME -- true for a new top-level TCX, false for anything
|
|
# endurain-import already handled (its copy is in processed/) or a partial.
|
|
# Builtins only: this runs once per inotify event.
|
|
is_fresh_upload() {
|
|
local name="$1"
|
|
[[ $name == *.tcx && $name != .* ]] || return 1
|
|
[[ ! -e $PROCESSED_DIR/$name ]]
|
|
}
|
|
|
|
validate_requirements() {
|
|
if ! command -v inotifywait >/dev/null 2>&1; then
|
|
log "ERROR: inotifywait missing (pacman -S inotify-tools) — RunnerUp" \
|
|
"uploads are only credited by the 15-min timer"
|
|
exit 1
|
|
fi
|
|
if [[ ! -d $WATCH_DIR ]]; then
|
|
log "ERROR: $WATCH_DIR does not exist — is dufs-cloud set up?" \
|
|
"RunnerUp uploads are only credited by the 15-min timer"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
validate_requirements
|
|
log "Watching $WATCH_DIR for finished RunnerUp TCX uploads"
|
|
local name now last_skip_log=0
|
|
while IFS= read -r name; do
|
|
if ! is_fresh_upload "$name"; then
|
|
[[ $name == *.tcx ]] || continue
|
|
# One line per burst, not per file: the re-pull lands ~27 at once.
|
|
printf -v now '%(%s)T' -1
|
|
if ((now - last_skip_log > 60)); then
|
|
log "Ignoring re-pulled $name (already in processed/; no" \
|
|
"workout-sync) — further re-pulls this minute not logged"
|
|
last_skip_log=$now
|
|
fi
|
|
continue
|
|
fi
|
|
ingest "$name"
|
|
done < <(inotifywait -m -q -e close_write -e moved_to --format '%f' "$WATCH_DIR")
|
|
log "ERROR: inotifywait on $WATCH_DIR exited — restarting via systemd"
|
|
exit 1
|
|
}
|
|
|
|
main "$@"
|