Notify only once when visible
This commit is contained in:
parent
03889fe6e2
commit
6ba59cbfa7
1 changed files with 51 additions and 9 deletions
|
|
@ -26,6 +26,8 @@ PUSH_CONFIG = Path.home() / ".config" / "mvlog" / "push.json"
|
||||||
STATE_NAME = ".movmaker-state.json"
|
STATE_NAME = ".movmaker-state.json"
|
||||||
LOCK_NAME = ".movmaker-lock"
|
LOCK_NAME = ".movmaker-lock"
|
||||||
ENABLED_NAME = ".movmaker-enabled"
|
ENABLED_NAME = ".movmaker-enabled"
|
||||||
|
HIDDEN_NAME = ".mvlog-hidden"
|
||||||
|
NOTIFIED_CACHE = "cache/push_notifications.json"
|
||||||
|
|
||||||
|
|
||||||
def now() -> str:
|
def now() -> str:
|
||||||
|
|
@ -85,17 +87,33 @@ print(hashlib.sha256(json.dumps(entries, ensure_ascii=False, separators=(',', ':
|
||||||
return ssh(host, cmd).stdout.strip()
|
return ssh(host, cmd).stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
def read_state(host: str, job_dir: str) -> dict:
|
def read_remote_json(host: str, path: str) -> dict:
|
||||||
path = f"{job_dir}/{STATE_NAME}"
|
|
||||||
res = ssh(host, f"cat {remote_q(path)} 2>/dev/null", check=False)
|
res = ssh(host, f"cat {remote_q(path)} 2>/dev/null", check=False)
|
||||||
if res.returncode != 0 or not res.stdout.strip():
|
if res.returncode != 0 or not res.stdout.strip():
|
||||||
return {}
|
return {}
|
||||||
try:
|
try:
|
||||||
return json.loads(res.stdout)
|
data = json.loads(res.stdout)
|
||||||
|
return data if isinstance(data, dict) else {}
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def write_remote_json(host: str, path: str, data: dict) -> None:
|
||||||
|
tmp_path = f"{path}.tmp.$$"
|
||||||
|
payload = json.dumps(data, ensure_ascii=False, indent=2) + "\n"
|
||||||
|
cmd = (
|
||||||
|
f"mkdir -p {remote_q(str(Path(path).parent))} && "
|
||||||
|
f"cat > {remote_q(tmp_path)} && "
|
||||||
|
f"chmod 664 {remote_q(tmp_path)} && "
|
||||||
|
f"mv {remote_q(tmp_path)} {remote_q(path)}"
|
||||||
|
)
|
||||||
|
ssh(host, cmd, input_text=payload)
|
||||||
|
|
||||||
|
|
||||||
|
def read_state(host: str, job_dir: str) -> dict:
|
||||||
|
return read_remote_json(host, f"{job_dir}/{STATE_NAME}")
|
||||||
|
|
||||||
|
|
||||||
def write_state(host: str, job_dir: str, state: dict) -> None:
|
def write_state(host: str, job_dir: str, state: dict) -> None:
|
||||||
path = f"{job_dir}/{STATE_NAME}"
|
path = f"{job_dir}/{STATE_NAME}"
|
||||||
tmp_path = f"{path}.tmp.$$"
|
tmp_path = f"{path}.tmp.$$"
|
||||||
|
|
@ -127,12 +145,12 @@ def copy_job_from_remote(host: str, job_dir: str, local_input: Path) -> None:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def send_push_notification(host: str, remote_root: str, output: str) -> None:
|
def send_push_notification(host: str, remote_root: str, output: str) -> bool:
|
||||||
if not PUSH_SENDER.exists() or not PUSH_CONFIG.exists():
|
if not PUSH_SENDER.exists() or not PUSH_CONFIG.exists():
|
||||||
return
|
return False
|
||||||
res = ssh(host, f"cat {remote_q(remote_root + '/cache/push_subscriptions.json')} 2>/dev/null", check=False)
|
res = ssh(host, f"cat {remote_q(remote_root + '/cache/push_subscriptions.json')} 2>/dev/null", check=False)
|
||||||
if res.returncode != 0 or not res.stdout.strip():
|
if res.returncode != 0 or not res.stdout.strip():
|
||||||
return
|
return False
|
||||||
title = output.rsplit('.', 1)[0].replace('_', ' ').title()
|
title = output.rsplit('.', 1)[0].replace('_', ' ').title()
|
||||||
payload = json.dumps({
|
payload = json.dumps({
|
||||||
"title": "New MVLog video",
|
"title": "New MVLog video",
|
||||||
|
|
@ -145,6 +163,26 @@ def send_push_notification(host: str, remote_root: str, output: str) -> None:
|
||||||
print(proc.stdout.strip())
|
print(proc.stdout.strip())
|
||||||
if proc.stderr.strip():
|
if proc.stderr.strip():
|
||||||
print(proc.stderr.strip(), file=sys.stderr)
|
print(proc.stderr.strip(), file=sys.stderr)
|
||||||
|
return proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_send_first_show_notification(host: str, remote_root: str, job_dir: str, job: str, state: dict | None = None) -> None:
|
||||||
|
if remote_file_exists(host, f"{job_dir}/{HIDDEN_NAME}"):
|
||||||
|
return
|
||||||
|
state = state if state is not None else read_state(host, job_dir)
|
||||||
|
output = state.get("output") if isinstance(state.get("output"), str) else ""
|
||||||
|
if not output or not remote_file_exists(host, f"{remote_root}/out-dir/{output}"):
|
||||||
|
return
|
||||||
|
cache_path = f"{remote_root}/{NOTIFIED_CACHE}"
|
||||||
|
cache = read_remote_json(host, cache_path)
|
||||||
|
sent = cache.get("sent") if isinstance(cache.get("sent"), dict) else {}
|
||||||
|
if output in sent:
|
||||||
|
return
|
||||||
|
if send_push_notification(host, remote_root, output):
|
||||||
|
sent[output] = {"job": job, "sent_at": now()}
|
||||||
|
cache["sent"] = sent
|
||||||
|
write_remote_json(host, cache_path, cache)
|
||||||
|
print(f"notification sent: {job} -> {output}")
|
||||||
|
|
||||||
|
|
||||||
def publish_output(host: str, remote_root: str, local_mp4: Path, old_output: str | None) -> str:
|
def publish_output(host: str, remote_root: str, local_mp4: Path, old_output: str | None) -> str:
|
||||||
|
|
@ -174,6 +212,8 @@ def render_job(local_input: Path, local_out: Path, extra_args: list[str]) -> Pat
|
||||||
|
|
||||||
def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None:
|
def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_args: list[str], force: bool) -> None:
|
||||||
job_dir = f"{remote_root}/in-dir/{job}"
|
job_dir = f"{remote_root}/in-dir/{job}"
|
||||||
|
state = read_state(host, job_dir)
|
||||||
|
maybe_send_first_show_notification(host, remote_root, job_dir, job, state)
|
||||||
if not remote_file_exists(host, f"{job_dir}/{ENABLED_NAME}"):
|
if not remote_file_exists(host, f"{job_dir}/{ENABLED_NAME}"):
|
||||||
print(f"disabled: {job}")
|
print(f"disabled: {job}")
|
||||||
return
|
return
|
||||||
|
|
@ -219,14 +259,16 @@ def process_job(host: str, remote_root: str, job: str, work_dir: Path, extra_arg
|
||||||
return
|
return
|
||||||
|
|
||||||
output = publish_output(host, remote_root, local_mp4, old_output)
|
output = publish_output(host, remote_root, local_mp4, old_output)
|
||||||
send_push_notification(host, remote_root, output)
|
new_state = {
|
||||||
write_state(host, job_dir, {
|
|
||||||
"status": "done",
|
"status": "done",
|
||||||
"fingerprint": fingerprint_after,
|
"fingerprint": fingerprint_after,
|
||||||
"output": output,
|
"output": output,
|
||||||
"generated_at": now(),
|
"generated_at": now(),
|
||||||
})
|
}
|
||||||
|
maybe_send_first_show_notification(host, remote_root, job_dir, job, new_state)
|
||||||
|
write_state(host, job_dir, new_state)
|
||||||
print(f"published: {job} -> {output}")
|
print(f"published: {job} -> {output}")
|
||||||
|
return
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
try:
|
try:
|
||||||
write_state(host, job_dir, {
|
write_state(host, job_dir, {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue