luvcraft/tape.lisp
--------------------------------------------------------------------- Where films live and how yt-dlp is found.
The directory downloaded films are kept in, beside the saved worlds.
The yt-dlp executable: the one Nix names in luv_YT_DLP, else whichever
is on the PATH.
b[ext=mp4][vcodec^=avc1][height<=720]/bv*[ext=mp4][vcodec^=avc1][height<=720]+ba[ext=m4a]/b[ext=mp4]/b
The yt-dlp format selector for a tape: an H.264 MP4 no taller than 720
lines, whole if the site has one, merged from picture and sound if not,
whatever it has otherwise. A whole file first because it is what the
plainest player clients hand out without a fight; see *TAPE-PLAYER-CLIENTS*.
mweb,android,default
Which YouTube player clients yt-dlp asks, in order of precedence.
The default client's segmented formats answer 403 as often as not this season, and yt-dlp does not fall back once a download has begun; the mobile web and Android clients hand out a plain progressive MP4 that arrives. A knob rather than a constant, since this is YouTube's weather, not ours.
Extract the eleven-character video id from text, which may be a bare code
or any of the usual YouTube URLs; NIL when there is none.
--------------------------------------------------------------------- The film: a block kind per video.
One downloaded video as a thing in the world and in the bag.
Every film shares the name :FILM and the reel's tiles; what differs is the video, so a film is its own block kind rather than one entry in the palette, and the world's palette-by-identity storage keeps them apart for free.
Every film this image knows, by video id, so a saved world and a saved bag that name the same video get the same object back.
Return the film for video-id, making it or filling in what is now known.
A short line naming film for a title bar or a bag.
--------------------------------------------------------------------- The download: yt-dlp in the background, read by one thread.
Written by the reader thread, read by the render thread: each is one word or one fresh object, never a structure edited in place.
One yt-dlp run for the tape at X,Y,Z, hung on SESSION as an overlay.
Drawn by no pass: the orb is particles, and there is nothing else.
How much of the whole download is done, 0..1.
yt-dlp fetches sound and picture as two streams for most videos and then merges them, so a stream's own percentage runs to a hundred twice. Until the second one starts we assume there will be two; a single-stream video simply arrives early.
Fold one line of yt-dlp's stdout into download's progress and facts.
A stream's summary line reads "100% of ... in ..." -- that is the stream done, and the next starts at zero.
Run yt-dlp for download and start the thread that listens to it.
--------------------------------------------------------------------- The orb: motes on a sphere over the tape, filled to the progress.
Sow this frame's motes over download's tape: a slowly turning shell,
lit from the bottom up as far as the progress has come, with a faint
fountain of sparks off the top.
Uniform on the sphere: height from the cosine, then a ring.
A few sparks leave the crown, whichever way round they were.
The orb bursts: one last spray of motes going every way.
--------------------------------------------------------------------- The job on the session.
Start fetching video-id for the tape at X,Y,Z and hang the job on session.
The process is over: turn the tape into its film, or leave it a tape.
The tape may have been dug out while it fetched; then the film goes straight into the bag instead of into a hole.
--------------------------------------------------------------------- Focusing a tape asks the question.
Show session the question a tape at X,Y,Z asks -- which YouTube code? --
and return the focus that is asking it, or NIL when no presentation system
can. LUVCRAFT/CLIM supplies the panel; the answer comes back through
begin-tape-download.
A tape already fetching has been asked; the orb is its answer.
--------------------------------------------------------------------- A film beside a wall plays on it.
Put a film down touching a terminal wall and the wall shows it; pick the film up again and the wall goes back to what it was. The film does not sit on the wall's face -- that would cover the screen -- but beside it: on the floor in front, on the wall's edge, anywhere one of its six neighbours is a terminal block with an exposed face.
Return the largest exposed terminal surface touching X,Y,Z, and the face it was found through, or NIL when no wall stands beside the block.
viewer is a point (the player's eye); of two faces of one wall the side
turned toward it wins, so a film set down before a wall lights the side the
player is looking at rather than the one behind.
Every face of that block but the one looking at us -- which we cover -- and the one looking away, which is the wall's back.
The display session already has on surface's wall, if any.
Which display each placed film lit, by (X Y Z): the display, and whether the film opened it -- in which case taking the film away closes it too.
Play film, which stands at X,Y,Z, on the wall beside it, if there is one.
Return the display it plays on, or NIL.
The film at X,Y,Z is gone: stop what it was showing.
--------------------------------------------------------------------- After a restart, the films already standing in the world light their walls again -- once the ground under them has streamed in.
A drawless overlay that shows each of the world's films on its wall as soon as its cell is resident, then takes itself down.
The (X Y Z) of every film written into world's authored edits.
Arrange for session's world's films to play on their walls again.
(session x y z)Show SESSION the question a tape at X,Y,Z asks -- which YouTube code? -- and return the focus that is asking it, or NIL when no presentation system can. LUVCRAFT/CLIM supplies the panel; the answer comes back through BEGIN-TAPE-DOWNLOAD.
(overlay session)Publish any complete pending render state for OVERLAY at a frame boundary.
()The yt-dlp executable: the one Nix names in LUV_YT_DLP, else whichever is on the PATH.
Logical conjunction of tests and raw truth values.
b[ext=mp4][vcodec^=avc1][height<=720]/bv*[ext=mp4][vcodec^=avc1][height<=720]+ba[ext=m4a]/b[ext=mp4]/b
mweb,android,default
(text)Extract the eleven-character video id from TEXT, which may be a bare code or any of the usual YouTube URLs; NIL when there is none.
Logical disjunction of tests and raw truth values.
Test whether two compatible scalars are equal.
Subtraction or unary negation.
Addition over compatible quantities.
One downloaded video as a thing in the world and in the bag. Every film shares the name :FILM and the reel's tiles; what differs is the video, so a film is its own block kind rather than one entry in the palette, and the world's palette-by-identity storage keeps them apart for free.
A standard class whose annotated slots retain quantity declarations. Slot access and instance representation remain ordinary CLOS. The metaclass only makes definition-time meaning inspectable and inheritable. #OXBSAY
(block)Whether BLOCK is a thing a player picks up and carries, rather than a material the palette gives without limit. A film is; stone is not.
Every film this image knows, by video id, so a saved world and a saved bag that name the same video get the same object back.
(video-id &key title uploader duration upload-date pathname)(film)(block)Return the portable value description stored for BLOCK in a world save.
(kind description)Resolve a portable block value with keyword KIND and property DESCRIPTION.
(plist key context &key optional default)One yt-dlp run for the tape at X,Y,Z, hung on SESSION as an overlay.
(overlay)Return where OVERLAY draws: :SCENE, :WORLD-PANEL, :VIEWMODEL, :HUD, or :NONE. Scene overlays inhabit world depth; viewmodels are first-person geometry above the world but below held items and the crosshair. World panels retain world projection and depth while drawing analytic application graphics at native…
(download)How much of the whole download is done, 0..1. yt-dlp fetches sound and picture as two streams for most videos and then merges them, so a stream's own percentage runs to a hundred twice. Until the second one starts we assume there will be two; a single-stream video simply arrives early.
The minimum of compatible quantities.
Division of two represented quantities.
The maximum of compatible quantities.
(download line)(download)(download)How far above the tape's floor the orb's centre floats.
How thickly the orb is sown; each mote lasts a fraction of a second.
(download seconds)Sow this frame's motes over DOWNLOAD's tape: a slowly turning shell, lit from the bottom up as far as the progress has come, with a faint fountain of sparks off the top.
Multiplication and scalar scaling.
(number)The componentwise square root of a raw value.
Test whether one compatible scalar is at most another.
Test whether one compatible scalar is less than another.
(system block x y z &key (velocity-x 0.0) (velocity-y 0.0)
(velocity-z 0.0) (size 0.05)
(lifetime 0.3) (gravity 0.0))Add one weightless BLOCK mote at X,Y,Z to SYSTEM, dropping the oldest fragment when the population is full.
(download)(session x y z)(session x y z video-id)(category control &rest arguments)Write one timestamped line about CATEGORY, formatted from CONTROL. CATEGORY is a keyword naming the subsystem -- :canvas, :watchdog, :vulkan -- so a reader can tell at a glance which machine is talking.
(session overlay)Attach OVERLAY at SESSION's next native frame boundary and return it. ADD consumes a newly offered OVERLAY on both success and terminal rejection. Once SESSION begins stopping, the rejected overlay is released exactly once before APPLICATION-ATTACHMENT-CLOSED is signalled.
(download)(world x y z)Return one site as BLOCK and :RESIDENT, or NIL and :ABSENT. This sparse world-coordinate accessor constructs coordinate descriptors and a chunk lookup key. It is appropriate for inspectors, ray hits, collision probes, and sparse interaction. Algorithms over many cells should select a chunk/domain once and use…
The film reel a player loads with a YouTube code; see luvcraft/tape.lisp. A class of its own rather than a name test, so activating one is a method on the kind and no other block's activation has to know about tapes.
(block world x y z)(inventory block &optional (quantity 1))Add QUANTITY of BLOCK to finite INVENTORY storage and return its entry. Adding to an unlimited entry leaves it unlimited. A previously unavailable block is appended, preserving the stable order of existing number keys.
(session overlay &key (release-p t))Detach OVERLAY at a frame boundary and optionally release it there. The no-release path is intentionally preserved for FUSE-LUVCRAFT-OVERLAY: that path may run inside a frame whose command stream still borrows OVERLAY.
(overlay)Release resources owned by an object attached to luvcraft.
(block session hit)Create and return a focusable interaction for targeted BLOCK, or NIL.
(world x y z &key viewer)Return the largest exposed terminal surface touching X,Y,Z, and the face it was found through, or NIL when no wall stands beside the block. VIEWER is a point (the player's eye); of two faces of one wall the side turned toward it wins, so a film set down before a wall lights the side the player is looking at rather…
(world x y z face
&key (material *terminal-block*)
(absent-neighbor-policy :air))Discover the maximal exposed rectangular terminal surface at X,Y,Z,FACE. Return the surface and :RECTANGLE. A seed which is not terminal, is covered, or belongs to a non-rectangular coplanar component instead returns NIL and a descriptive status. Discovery runs the compiled TERMINAL-SURFACE-DISCOVERY program; this…
Test whether one compatible scalar is greater than another.
(session surface)Which display each placed film lit, by (X Y Z): the display, and whether the film opened it -- in which case taking the film away closes it too.
(session film x y z)Play FILM, which stands at X,Y,Z, on the wall beside it, if there is one. Return the display it plays on, or NIL.
(session x y z face
&key columns rows
(rows-per-block 6)
(fixture (terminal-display-fixture))
(material *terminal-block*)
(class 'terminal-display)
(margin 0.12)
(font-scale 1.0)
(font-pathname *terminal-display-font-pathname*)
(bold-font-pathname *terminal-display-bold-font-pathname*)
(default-foreground *terminal-display-default-foreground*))Attach a deterministic Ghostty terminal to an authored block rectangle. X,Y,Z names any block in the desired exposed FACE. The maximal coplanar terminal-material component must be rectangular. Its native block meshes remain the display body; this overlay contributes only one fitted Slug grid. When COLUMNS or ROWS…
(display pathname &key (hardware :required))Play PATHNAME on DISPLAY's authored wall using the session's video backend. HARDWARE is the decode policy MAKE-VIDEO-SCREEN takes. It defaults to :REQUIRED, which is right for authored films whose codec is known, and wrong for a film that arrived from somewhere: whatever a stranger's phone recorded is not…
(session x y z)(display session mode)Select DISPLAY's focused wall MODE. The built-in modes are the EQL-specialized symbols :SHELL and :FILM. LUVCRAFT/MCCLIM adds an :AFTER method which supplies the film browser, while the display continues to own focus and movie lifetime.
(block session x y z)BLOCK has just been put down at X,Y,Z by the player in SESSION. Most blocks are inert and the default does nothing; a block that does something where it stands -- a film beside a wall -- answers here.
(block session x y z)BLOCK has just been taken away from X,Y,Z by the player in SESSION.
A drawless overlay that shows each of the world's films on its wall as soon as its cell is resident, then takes itself down.
(world)(session)
The tape: a film reel that fetches a YouTube video and becomes a film.
A tape is a placeable block. Focusing one asks for a YouTube code (the question itself is a McCLIM panel supplied by LUVCRAFT/CLIM through
open-tape-prompt); answering starts yt-dlp in the background and hangs a progress orb over the block -- a sphere of bright motes that fills from the bottom as the download comes in. When the file lands the tape is replaced by a FILM: a per-instance block kind carrying the video's metadata and the path of the media, which the player picks up into the inventory by hitting it, and can put down again anywhere.The download job is an overlay that draws nothing: it hangs on the session so
refresh-luvcraft-overlaypolls the process once per frame, on the render thread, which is where the world may be edited and particles emitted.