[mobileWallLayout] Add play-on-visibility and ordered loading#704
Conversation
Adds two mobile-first behaviors to the existing layout plugin and rebrands the display name to "scrollFeed": - IntersectionObserver gates <video> play/pause on 10% visibility. Caps concurrent playbacks to ~3, well under iOS Safari's ~20-simultaneous-<video> ceiling that the unchanged Stash wall hits on a 20-card page. - A DOM-ordered load queue cancels the parallel-fetch storm Stash kicks off when all wall cards mount at once. Top clips get uncontested bandwidth first, and a 500ms timeout advance ensures the full page is in-flight within ~5s so degrading reception doesn't strand the bottom of the page with zero bytes. The plugin's internal id stays mobileWallLayout so existing installs upgrade cleanly. The whole file is wrapped in an IIFE to avoid top-level name collisions with other plugins loaded into the same document. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a11e193 to
6753c5b
Compare
The scrollFeed rebrand inadvertently repointed the YAML url: field to a GitHub tree URL and dropped the discourse link from the top of the README. Every other plugin in this repo uses the discourse thread as its url: value (it's what the Stash plugins panel surfaces as the plugin's support link), so restoring the original discourse URL here keeps the convention and keeps existing users linked to the plugin's discussion thread.
|
Apologies — the scrollFeed rebrand silently dropped the discourse URL from two places: the To be upfront: this PR was drafted with AI assistance (Claude Opus 4.7 — noted in the first commit's Just pushed b6ad2e3 restoring both:
Adding a pre-PR check to my own routine so the |
|
This pull request has been mentioned on Stash Forum. There might be relevant details there: |
Summary
Two mobile-first behaviors on top of the existing layout fix, and a
display-name rebrand to
scrollFeedto reflect the broader feature set.IntersectionObserverplays each<video>at10% visibility and pauses it when it leaves the viewport. Concurrent
playback caps at 2–3, which keeps iOS Safari comfortably under its
~20-simultaneous-
<video>decoder ceiling. On a 20-card wall page(Stash's minimum per-page size), the unchanged plugin leaves every
preview autoplay firing at once — this PR fixes that.
for every
<video src>. Over cellular that splits the uplink 20 waysand no video is playable for many seconds. The queue cancels those
fetches via
removeAttribute('src') + video.load()and re-issues themtop-down, 2 at a time. Advances on
canplayor a 500ms fallback — thetop clip is playable fast on cellular, and the whole page is in-flight
within ~5 seconds so moving into a weaker-signal area doesn't leave the
bottom of the page with zero bytes.
scrollFeedwith anupdated description. The plugin ID stays
mobileWallLayoutso existinginstalls upgrade cleanly.
Internals
scope (Stash loads all UI plugins into the same document).
window.location.pathname(exact match on/imagesand
/scenes/markers) rather thanhref.includes.beginLoadingcaptures the current_loadingSetin its closure so any
setTimeoutadvance that fires afterdeactivateVideoBehaviornulls plugin state detects the mismatch andno-ops.
WeakMap/WeakSetso video GC isn'tblocked when React unmounts elements.
Tuning knobs
Constants at the top of the load-queue section:
threshold(IntersectionObserver)0.1_MAX_CONCURRENT_LOADS2_LOAD_ADVANCE_MS500Test plan
cellular: 20-card marker wall plays 2–3 concurrent, top video canplay
in ~1s, whole page in-flight by ~5s, scroll to video renamerOnUpdate: segment config options into different file #18 shows bytes
already accumulating.
entering a not-yet-loaded clip shows a queued state and resumes once
back online.
/scenes/markersand back: observers tear downand re-attach cleanly, no leaked style tag or decoder stampede.
validator/validate.jspasses locally; YAML schema-valid.Internal plugin ID is unchanged (
mobileWallLayout), so users with thecurrent published plugin will transparently upgrade.