From caeee9179bb872a58bf3eeb6d3f4f5706d2bb8d5 Mon Sep 17 00:00:00 2001 From: christian Date: Tue, 27 Oct 2026 11:32:18 -0400 Subject: [PATCH] TRACKS OUT copies to Woonsocket too (ready.locations AB, SC, WS); config synced from NA (Resi event times set on the settings page); tests pin their own Resi times Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- config.json | 477 +++++++++++++++++++------------------ tests/test_status_panel.py | 3 + 3 files changed, 243 insertions(+), 239 deletions(-) diff --git a/README.md b/README.md index 4f6b0e5..ff94755 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ everything on the page at once. | BKGDS | every song has at least one slide with a background | looks again | | LED CROSS | every Resolume column Sunday's playlist calls up has content: each slide's macro (read from the ProPresenter Mac's `Macros` and `.pro` files) sends RossTalk `CC page/row/col` to Companion, that button (and the buttons it presses, e.g. page 17 → page 6 "Arena") sends `/composition/columns/N/connect`; **COL 11 EMPTY** names the column and the items that use it. Also: Resolume on the **Service** deck. | selects the Service deck, then looks again (an empty column is filled in Resolume) | | LINEUP TV | ProdDash's Band Lineup shows this plan, nobody who is not on it or who declined, and everyone on the Band and Communication teams | re-syncs the board from Planning Center | -| TRACKS OUT | every location in `"locations"` (AB, SC) has this weekend's project folder | copies NA's project to the locations that have none (as `261018 AB Project`, without `Backup/`). It is copied under a temporary name and renamed at the end, so a half-finished copy never looks sent. It never overwrites a folder that is already there and never runs by itself. | +| TRACKS OUT | every location in `"locations"` (AB, SC, WS) has this weekend's project folder | copies NA's project to the locations that have none (as `261018 AB Project`, without `Backup/`). It is copied under a temporary name and renamed at the end, so a half-finished copy never looks sent. It never overwrites a folder that is already there and never runs by itself. | | PROP OUT | a `.probundle` is in this weekend's folder of the Chronicles share (`/Volumes/Location Assets/261025`, read on the ProPresenter Mac). **SHARE NOT MOUNTED**: the share itself is not mounted there (2026-10-22: it had dropped off and the key said NOT IN 261025 although the bundle was there). The ProP Mac keeps it mounted by itself: a LaunchAgent (`org.waterschurch.keep-mounted.location-assets`, from `remote-access/mac-keep-mounted.sh`) looks every minute and at login, mounts it again with the Chronicles login saved in the Keychain, and unmounts and remounts it when it hangs after sleep or a network drop (log: `~/Library/Logs/keep-mounted-location-assets.log` on the ProP Mac). | starts that agent now and waits for the share (about 6 s), then looks again. Also an auto-fix. | | RESI VOX | the Resi song (PCO description says RESI, else the last song before the message) goes to the locations with its lead singer's mic: the lead from the description ("Genesis lead"), their VOX from the lineup screen ("[VOX 4] MIX 4"; PCO team positions if the lineup has none), against what the Resi AJA's `5 - Lead Vocal` channel (21) really receives, read from the card with Dante's own query (UDP 4440, read only). **RESI V2 NOT VOX 4** (red). Two leads ("Mike and DJ lead"): the song's Audio note in PCO says whose mic goes out ("V3" or a name); without one it is flagged, never switched: **2 LEADS: MIKE DJ**. Also only flagged: **JOSH NOT ON LINEUP**, **LEAD ON VOX 5: NO BUTTON**. The lineup is what counts: a PCO Audio note naming another VOX is only mentioned in the details. | presses that VOX's AJA button (Companion page 13, row 3, col 1-4: AJA ch 21 <- BOH-AD4Q-A ch n), then reads the AJA back. Also an auto-fix (any time, never while a deck records: the READY page is not looked at then). | | START | every service Planning Center lists this week (this location's Sunday and every special service: First / Together Tuesday, Morning Worship, ...) has its Ableton set. **NO SET SUN 11/1** (red when due): hold START. While it builds the key shows what it is doing (**SAVING JEHOVAH**, **COPYING 40/120**). **CONSOLIDATE THE LORD IS**: the saved set still has clips of that song transposed and not consolidated (read from the set file, so it goes once the set is saved consolidated). After a build, for a day: **NEW SONG GRACE** (not in Finished Songs: build it by hand, page 3 of the workflow PDF). See *START* below. | builds the missing sets (on the Tracks Mac, in the background), then consolidates every transposed clip in Live (step 9 below). Never runs by itself. | diff --git a/config.json b/config.json index 3ecb801..d8ab6fb 100644 --- a/config.json +++ b/config.json @@ -5,13 +5,13 @@ "library": "~/Library/Application Support/companion/modules/bmd-atem-*/node_modules/atem-connection", "node": "/Applications/Companion [0-9]*.app/Contents/Resources/node-runtimes/node22/bin/node", "reference": "atem-reference.xml", - "source_folder": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Production Team/Backup/NA/ATEM", "silence": { "inputs": { "16": "Resi - RX1" }, "seconds": 30 - } + }, + "source_folder": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Production Team/Backup/NA/ATEM" }, "auto_fix": { "after_seconds": 20, @@ -90,6 +90,20 @@ ] }, "fixes": { + "ableset": { + "host": "tracks", + "kind": "ssh", + "label": "start AbleSet on the Tracks Mac if it is not running (minimized, for ProdDash)", + "params": { + "app": "AbleSet", + "host": "tracks", + "minimized": true, + "process": "AbleSet" + }, + "run": "pgrep -xq AbleSet || { open -g -a AbleSet; i=0; while [ $i -lt 20 ]; do sleep 1; i=$((i+1)); n=$(osascript -e 'tell application \"System Events\" to count windows of process \"AbleSet\"' 2>/dev/null); if [ \"${n:-0}\" -gt 0 ]; then osascript -e 'tell application \"System Events\" to tell process \"AbleSet\" to set value of attribute \"AXMinimized\" of every window to true' >/dev/null 2>&1; break; fi; done; }", + "short": "ABLESET", + "template": "mac_start" + }, "ableton": { "host": "tracks", "kind": "ssh", @@ -338,9 +352,15 @@ "process": "ProPresenter", "wait_seconds": 10 }, + "run": "APP=$(for a in /Applications/ProPresenter*.app; do [ -d \"$a\" ] && printf '%s|%s\\n' \"$(defaults read \"$a/Contents/Info\" CFBundleShortVersionString 2>/dev/null)\" \"$a\"; done | sort -t '|' -k1,1V | tail -1 | cut -d '|' -f2-); pkill -TERM -x ProPresenter; i=0; while pgrep -xq ProPresenter && [ $i -lt 20 ]; do sleep 0.5; i=$((i+1)); done; pgrep -xq ProPresenter && pkill -KILL -x ProPresenter; sleep 1; open \"$APP\"", "short": "PROPRES", - "template": "mac_restart", - "run": "APP=$(for a in /Applications/ProPresenter*.app; do [ -d \"$a\" ] && printf '%s|%s\\n' \"$(defaults read \"$a/Contents/Info\" CFBundleShortVersionString 2>/dev/null)\" \"$a\"; done | sort -t '|' -k1,1V | tail -1 | cut -d '|' -f2-); pkill -TERM -x ProPresenter; i=0; while pgrep -xq ProPresenter && [ $i -lt 20 ]; do sleep 0.5; i=$((i+1)); done; pgrep -xq ProPresenter && pkill -KILL -x ProPresenter; sleep 1; open \"$APP\"" + "template": "mac_restart" + }, + "ready_ableton_mix": { + "action": "ableton_mix", + "kind": "ready", + "label": "put Ableton's stems back where the routing template says and their in-ear send (A) to 0 dB", + "short": "MIX" }, "ready_copy_tracks": { "action": "copy_tracks", @@ -355,6 +375,13 @@ "label": "put Resolume back on the Service deck, then look at the columns again", "short": "CROSS" }, + "ready_light_timing": { + "action": "light_timing", + "kind": "ready", + "label": "move the lighting cues that are off onto the drum hit in the open Ableton set (then save the set)", + "never_auto": true, + "short": "CUES" + }, "ready_lineup": { "action": "lineup_sync", "kind": "ready", @@ -368,12 +395,32 @@ "label": "write each song's timecode from the tracks into its LTC note in Planning Center", "short": "LTC" }, + "ready_mount_share": { + "action": "mount_share", + "kind": "ready", + "label": "mount Location Assets again on the ProPresenter Mac (its keep-mounted agent), then look for the bundle again", + "short": "MOUNT" + }, "ready_recheck": { "action": "recheck", "kind": "ready", "label": "look at this weekend's checklist again now", "short": "CHECK" }, + "ready_resi_vox": { + "action": "resi_vox", + "anytime": true, + "kind": "ready", + "label": "send the Resi song's lead singer's mic over Resi: press their VOX AJA button on page 13 (from PCO's lead and the lineup screen)", + "short": "RESI VOX" + }, + "ready_start": { + "action": "start_week", + "kind": "ready", + "label": "build this week's Ableton sets (Sunday and special services) from Planning Center", + "never_auto": true, + "short": "START" + }, "rtpmidi_start": { "host": "lighting", "kind": "ssh", @@ -506,53 +553,6 @@ "run": "tasklist /NH /FI \"IMAGENAME eq Vista.exe\" | find /I \"Vista.exe\" >nul || (schtasks /Run /TN \"\\StatusPanel\\Start Vista\" >nul && ping -n 9 127.0.0.1 >nul && tasklist /NH /FI \"IMAGENAME eq Vista.exe\" | find /I \"Vista.exe\" >nul)", "short": "VISTA", "template": "win_start" - }, - "ready_ableton_mix": { - "action": "ableton_mix", - "kind": "ready", - "label": "put Ableton's stems back where the routing template says and their in-ear send (A) to 0 dB", - "short": "MIX" - }, - "ready_light_timing": { - "action": "light_timing", - "kind": "ready", - "never_auto": true, - "short": "CUES", - "label": "move the lighting cues that are off onto the drum hit in the open Ableton set (then save the set)" - }, - "ready_mount_share": { - "action": "mount_share", - "kind": "ready", - "short": "MOUNT", - "label": "mount Location Assets again on the ProPresenter Mac (its keep-mounted agent), then look for the bundle again" - }, - "ready_start": { - "action": "start_week", - "kind": "ready", - "label": "build this week's Ableton sets (Sunday and special services) from Planning Center", - "never_auto": true, - "short": "START" - }, - "ableset": { - "host": "tracks", - "kind": "ssh", - "label": "start AbleSet on the Tracks Mac if it is not running (minimized, for ProdDash)", - "params": { - "host": "tracks", - "process": "AbleSet", - "app": "AbleSet", - "minimized": true - }, - "run": "pgrep -xq AbleSet || { open -g -a AbleSet; i=0; while [ $i -lt 20 ]; do sleep 1; i=$((i+1)); n=$(osascript -e 'tell application \"System Events\" to count windows of process \"AbleSet\"' 2>/dev/null); if [ \"${n:-0}\" -gt 0 ]; then osascript -e 'tell application \"System Events\" to tell process \"AbleSet\" to set value of attribute \"AXMinimized\" of every window to true' >/dev/null 2>&1; break; fi; done; }", - "short": "ABLESET", - "template": "mac_start" - }, - "ready_resi_vox": { - "action": "resi_vox", - "kind": "ready", - "anytime": true, - "short": "RESI VOX", - "label": "send the Resi song's lead singer's mic over Resi: press their VOX AJA button on page 13 (from PCO's lead and the lineup screen)" } }, "flap": { @@ -912,10 +912,17 @@ "ssh": "studio@10.1.7.39" } }, + "nav_hold_more": [ + { + "ms": 3000, + "page": 98 + } + ], "nav_hold_ms": 2000, "nav_keys": [ "pagenum" ], + "nav_locations_band": true, "nav_tap_page": 1, "network": { "burst_connections": 4, @@ -929,6 +936,7 @@ "page_hold_ms": 2000, "page_name": "19\\nStatus", "ready": { + "ableton_osc": true, "due": [ { "days": [ @@ -952,8 +960,18 @@ "start": "05:00" } ], + "ears_send": 0, + "ears_skip_groups": [ + "RESI TRACKS", + "MIDI GROUP" + ], + "finished_folders": [ + "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Finished Songs", + "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Finished Custom Songs" + ], "folder_format": "{date} {loc} Project", "interval_seconds": 120, + "light_library": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/LIGHTING CUES MIDI", "lighting_track": "LIGHTING CUES", "lineup_skip_positions": [], "lineup_teams": [ @@ -963,7 +981,8 @@ "lineup_url": "http://127.0.0.1:24500/api/modules/band-lineup", "locations": [ "AB", - "SC" + "SC", + "WS" ], "ltc_campuses": [ { @@ -1016,26 +1035,35 @@ "prop_url": "http://10.1.14.225:1025", "remote_timeout": 90, "reset_day": "mon", + "resi_vox": { + "aja": "STUDIO-A-Resi-AJA-SDI", + "buttons": { + "1": [ + 13, + 3, + 1 + ], + "2": [ + 13, + 3, + 2 + ], + "3": [ + 13, + 3, + 3 + ], + "4": [ + 13, + 3, + 4 + ] + }, + "channel": 21, + "mics": "BOH-AD4Q-A" + }, "resolume_deck": "Service", "resolume_url": "http://10.1.10.206:8080", - "site_folder": "NA", - "skip_songs": [], - "tracks_host": "tracks", - "vocal_positions": [ - "Vocalist", - "[Leader]" - ], - "finished_folders": [ - "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Finished Songs", - "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Finished Custom Songs" - ], - "tune_library": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Waves Tune - midi Program Change", - "ableton_osc": true, - "ears_send": 0, - "ears_skip_groups": [ - "RESI TRACKS", - "MIDI GROUP" - ], "routing": { "+GUIDE": [ "Ext. Out", @@ -1230,34 +1258,14 @@ "11/12 VOX" ] }, - "light_library": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/LIGHTING CUES MIDI", - "resi_vox": { - "aja": "STUDIO-A-Resi-AJA-SDI", - "channel": 21, - "mics": "BOH-AD4Q-A", - "buttons": { - "1": [ - 13, - 3, - 1 - ], - "2": [ - 13, - 3, - 2 - ], - "3": [ - 13, - 3, - 3 - ], - "4": [ - 13, - 3, - 4 - ] - } - } + "site_folder": "NA", + "skip_songs": [], + "tracks_host": "tracks", + "tune_library": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Waves Tune - midi Program Change", + "vocal_positions": [ + "Vocalist", + "[Leader]" + ] }, "ready_page": { "back_pos": [ @@ -1479,78 +1487,78 @@ ] }, { + "checks": [ + { + "fix": "ready_light_timing", + "item": "light_timing", + "type": "ready" + } + ], "id": "rd_light_time", "label": "LIGHT TIMING", "pos": [ 1, 5 - ], - "checks": [ - { - "type": "ready", - "item": "light_timing", - "fix": "ready_light_timing" - } ] }, { + "checks": [ + { + "fix": "ready_recheck", + "item": "keys", + "type": "ready" + } + ], "id": "rd_keys", "label": "SONG KEYS", "pos": [ 0, 6 - ], - "checks": [ - { - "type": "ready", - "item": "keys", - "fix": "ready_recheck" - } ] }, { + "checks": [ + { + "fix": "ready_recheck", + "item": "tune", + "type": "ready" + } + ], "id": "rd_tune", "label": "W TUNE", "pos": [ 0, 7 - ], - "checks": [ - { - "type": "ready", - "item": "tune", - "fix": "ready_recheck" - } ] }, { + "checks": [ + { + "fix": "ready_ableton_mix", + "item": "routing", + "type": "ready" + } + ], "id": "rd_routing", "label": "ROUTING", "pos": [ 2, 2 - ], - "checks": [ - { - "type": "ready", - "item": "routing", - "fix": "ready_ableton_mix" - } ] }, { + "checks": [ + { + "fix": "ready_ableton_mix", + "item": "ears", + "type": "ready" + } + ], "id": "rd_ears", "label": "IN EARS", "pos": [ 2, 3 - ], - "checks": [ - { - "type": "ready", - "item": "ears", - "fix": "ready_ableton_mix" - } ] }, { @@ -1563,39 +1571,80 @@ ] }, { + "checks": [ + { + "fix": "ready_start", + "item": "start", + "type": "ready" + } + ], "id": "rd_start", "label": "START", "pos": [ 3, 3 - ], - "checks": [ - { - "type": "ready", - "item": "start", - "fix": "ready_start" - } ] }, { + "checks": [ + { + "fix": "ready_resi_vox", + "item": "resi_vox", + "type": "ready" + } + ], "id": "rd_resi_vox", "label": "RESI VOX", "pos": [ 2, 4 - ], - "checks": [ - { - "type": "ready", - "item": "resi_vox", - "fix": "ready_resi_vox" - } ] } ], "page": 21, "page_name": "21\\nReady" }, + "resi_events": { + "margin_minutes": 10, + "pco_specials": true, + "weekly": [ + { + "days": [ + "mon" + ], + "end": "10:00", + "start": "09:00" + }, + { + "days": [ + "wed" + ], + "end": "22:00", + "start": "16:30" + }, + { + "days": [ + "sun" + ], + "end": "09:45", + "start": "08:00" + }, + { + "days": [ + "sun" + ], + "end": "11:45", + "start": "09:50" + }, + { + "days": [ + "sun" + ], + "end": "13:50", + "start": "11:50" + } + ] + }, "restart_page": { "back_pos": [ 2, @@ -1737,9 +1786,9 @@ ] }, { + "fix": "arena", "id": "rs_arena", "label": "ARENA", - "fix": "arena", "pos": [ 1, 5 @@ -1749,14 +1798,14 @@ ] }, { + "fix": "ableset", "id": "rs_ableset", "label": "ABLESET", - "fix": "ableset", - "toggle": "ableset", "pos": [ 1, 6 - ] + ], + "toggle": "ableset" } ], "page": 20, @@ -1778,6 +1827,12 @@ } ], "site": { + "admin_ips": [ + "10.6.7.38" + ], + "admin_keys": [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPKCGDLncJQfNPpoZUy/5BQWGNtyaU1/xKXwHQWx3ETa christian-laptop@waters" + ], "editing_code": "7381", "id": "na", "ingest_hostname": "na-ingest.local", @@ -1787,13 +1842,7 @@ ], "ingest_name": "NA Ingest", "live_services": [], - "name": "North Attleboro", - "admin_ips": [ - "10.6.7.38" - ], - "admin_keys": [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPKCGDLncJQfNPpoZUy/5BQWGNtyaU1/xKXwHQWx3ETa christian-laptop@waters" - ] + "name": "North Attleboro" }, "spotify": { "name": "Christian EDM 2026", @@ -2214,10 +2263,10 @@ "type": "mdns" }, { + "debounce_seconds": 120, "instance": "NA Tracks", "name": "TRACKS MIDI", - "type": "mdns", - "debounce_seconds": 120 + "type": "mdns" }, { "name": "TRACKS IN", @@ -2232,9 +2281,9 @@ "type": "midi_wired" }, { - "type": "tune_rx", "host": "studiob", - "name": "STUDIO B" + "name": "STUDIO B", + "type": "tune_rx" } ], "id": "midi", @@ -2417,11 +2466,11 @@ "type": "atem" }, { - "type": "atem", - "part": "silence", - "msg": "RESI AUDIO SILENT", "fix": "atem_audio", - "quiet": "resi_events" + "msg": "RESI AUDIO SILENT", + "part": "silence", + "quiet": "resi_events", + "type": "atem" }, { "fix": "atem_camera", @@ -2465,9 +2514,9 @@ "checks": [ { "conn": "RESI_Decoder", + "quiet": "resi_events", "tag": "DECODER", - "type": "conn", - "quiet": "resi_events" + "type": "conn" }, { "host": "10.1.10.203", @@ -2486,16 +2535,16 @@ "url": "http://10.1.10.202/" }, { - "conn": "resi-studio-cues", - "type": "conn", "amber": true, - "quiet": "resi_events" + "conn": "resi-studio-cues", + "quiet": "resi_events", + "type": "conn" }, { "name": "CLOUD", + "quiet": "resi_events", "type": "http", - "url": "https://central.resi.io/", - "quiet": "resi_events" + "url": "https://central.resi.io/" } ], "id": "resi", @@ -2713,41 +2762,41 @@ }, { "id": "autofix", - "label": "AUTO-FIX", "kind": "autofix", + "label": "AUTO-FIX", "pos": [ 3, 7 ] }, { - "id": "ableset", - "label": "ABLESET", - "kind": "bypass", "bypass": { "fixes": [ "ableset" ], - "on_text": "AUTO-OPEN\nOFF", - "off_text": "AUTO-OPEN\nON" - } + "off_text": "AUTO-OPEN\nON", + "on_text": "AUTO-OPEN\nOFF" + }, + "id": "ableset", + "kind": "bypass", + "label": "ABLESET" }, { - "id": "trk_ingest_off", - "label": "TRK INGEST", - "kind": "bypass", "bypass": { - "peers": [ - "NA Ingest" - ], + "auto": "autofix_hours", "hosts": [ "tracks" ], - "auto": "autofix_hours", - "on_text": "OFF IN\nAUTO-FIX HOURS", + "names_only": true, "off_text": "ALLOWED", - "names_only": true - } + "on_text": "OFF IN\nAUTO-FIX HOURS", + "peers": [ + "NA Ingest" + ] + }, + "id": "trk_ingest_off", + "kind": "bypass", + "label": "TRK INGEST" } ], "timing": { @@ -2758,53 +2807,5 @@ "command": "sp_cmd", "heartbeat": "sp_hb", "state": "sp_state" - }, - "nav_locations_band": true, - "nav_hold_more": [ - { - "ms": 3000, - "page": 98 - } - ], - "resi_events": { - "weekly": [ - { - "days": [ - "sun" - ], - "start": "07:30", - "end": "09:15" - }, - { - "days": [ - "sun" - ], - "start": "09:30", - "end": "11:15" - }, - { - "days": [ - "sun" - ], - "start": "11:30", - "end": "13:15" - }, - { - "days": [ - "mon" - ], - "start": "09:00", - "end": "10:00" - }, - { - "days": [ - "wed" - ], - "start": "16:30", - "end": "22:00" - } - ], - "margin_minutes": 10, - "pco_specials": true } } diff --git a/tests/test_status_panel.py b/tests/test_status_panel.py index 16c1bbf..cc6032b 100644 --- a/tests/test_status_panel.py +++ b/tests/test_status_panel.py @@ -36,6 +36,9 @@ CONFIG["auto_fix"]["log_dir"] = tempfile.mkdtemp(prefix="sp-autolog-") # never # ...and their own auto-fix hours, so the location can change its own on the settings page. CONFIG["auto_fix"]["windows"] = [{"days": ["wed"], "start": "16:00", "end": "21:00"}, {"days": ["sun"], "start": "06:30", "end": "13:30"}] WEDNESDAY = datetime(2026, 10, 7, 15, 0).timestamp() +# ...and their own Resi event times (the settings page moves NA's). +CONFIG["resi_events"]["weekly"] = [{"days": ["sun"], "start": s, "end": e} for s, e in (("07:30", "09:15"), ("09:30", "11:15"), ("11:30", "13:15"))] + \ + [{"days": ["mon"], "start": "09:00", "end": "10:00"}, {"days": ["wed"], "start": "16:30", "end": "22:00"}] def healthy_snapshot(now):