One playback across the tabs of an app, through Web Locks and a BroadcastChannel.
On a desktop, no engine stops the audio of one tab when another tab starts, so two tabs of a podcast app play at once and race to save the position. attach! holds a lock named for the app while the element plays, and takes it from any other tab that holds it, which then pauses its own element:
(def detach!
(attach! audio "my-podcast-app"))
It also tells the other tabs what the element plays, where it is and whether it plays, so that the player of every tab can show the same playback, and it runs the actions that they send while it plays:
The functions take these opts:
One playback across the tabs of an app, through Web Locks and a
BroadcastChannel.
On a desktop, no engine stops the audio of one tab when another tab
starts, so two tabs of a podcast app play at once and race to save the
position. attach! holds a lock named for the app while the element
plays, and takes it from any other tab that holds it, which then pauses
its own element:
(def detach!
(attach! audio "my-podcast-app"))
It also tells the other tabs what the element plays, where it is and
whether it plays, so that the player of every tab can show the same
playback, and it runs the actions that they send while it plays:
- watch! gives a tab the newest playback across the tabs
- position gives where a playback is by now
- press! sends an action to the tab that plays
- share! tells the other tabs of a change of a paused element
- align! moves a paused element to the newest playback
The functions take these opts:
- :window, the window whose navigator holds the locks, and whose
BroadcastChannel reaches the other tabs, instead of the page's, e.g. a
stand-in in a test(align! element)Move the attached media element, which is paused, to the newest
playback of what it plays, when another tab shared one after the last
change here: its position by now, and its rate. Return whether it
moved, e.g. before a skip from where the playback is.
Move the attached media `element`, which is paused, to the newest playback of what it plays, when another tab shared one after the last change here: its position by now, and its rate. Return whether it moved, e.g. before a skip from where the playback is.
(attach! element name)(attach! element name {:keys [handlers align?] :as opts})Hold the Web Lock of name while the media element plays, pause the
element when another tab takes the lock, and tell the other tabs of its
playback, with the opts above and these. Return a function that
detaches it.
The element takes the lock from any holder when it plays, and lets it go when it pauses and at pagehide, since a held lock keeps a page out of the back/forward cache. It tells the other tabs of its playback when it plays and pauses, every heartbeat-ms while it plays, and when another tab asks. A change of a paused element, e.g. a seek, isn't told, since attach! can't tell the listener's seek from the one that loads an episode, so call share! after one. Where the browser has no Web Locks, the element holds no lock, and where it has no BroadcastChannel, the other tabs learn nothing. Where the window fires no events, e.g. Node's global object, which has Web Locks from Node 24.5, the element still takes the lock as it plays and lets go of it as it pauses.
Hold the Web Lock of `name` while the media `element` plays, pause the
element when another tab takes the lock, and tell the other tabs of its
playback, with the `opts` above and these. Return a function that
detaches it.
- :id-fn, a function of no arguments that gives what the element plays,
as data that EDN prints, e.g. the id of an episode, by default its
currentSrc
- :handlers, the handlers of the actions that the other tabs send with
press! while the element plays, a map of action to a function of the
details, as for controls/listen!, e.g. {:pause #(.pause audio)}
- :align?, true to start from the newest playback of what the element
plays, as another tab shared it, when the element plays
The element takes the lock from any holder when it plays, and lets it
go when it pauses and at pagehide, since a held lock keeps a page out of
the back/forward cache. It tells the other tabs of its playback when it
plays and pauses, every heartbeat-ms while it plays, and when another
tab asks. A change of a paused element, e.g. a seek, isn't told, since
attach! can't tell the listener's seek from the one that loads an
episode, so call share! after one. Where the browser has no Web Locks,
the element holds no lock, and where it has no BroadcastChannel, the
other tabs learn nothing. Where the window fires no events, e.g. Node's
global object, which has Web Locks from Node 24.5, the element still
takes the lock as it plays and lets go of it as it pauses.The functions of attach! for each element, by the element: :share!, which tells the other tabs of a change, and :align!, which moves it to the newest playback.
The functions of attach! for each element, by the element: :share!, which tells the other tabs of a change, and :align!, which moves it to the newest playback.
How many milliseconds pass between two messages of a playing element to the other tabs, by which they also notice that its tab has gone.
How many milliseconds pass between two messages of a playing element to the other tabs, by which they also notice that its tab has gone.
(newest states now)The newest of the playback states of the tabs at now, in
milliseconds since the epoch: one that plays, and else the one that
changed last. A playing one that hasn't told where it is for three
heartbeats counts as paused, and :gone.
The newest of the playback `states` of the tabs at `now`, in milliseconds since the epoch: one that plays, and else the one that changed last. A playing one that hasn't told where it is for three heartbeats counts as paused, and :gone.
The shared playback of each name in each window, by the window and then the name, each an atom of its :channel, the :tab id of this page, its :own state, the states of the :others by their tab, the :watchers, the :handlers and playing? of the element of attach!, and how many calls :use it.
The shared playback of each name in each window, by the window and then the name, each an atom of its :channel, the :tab id of this page, its :own state, the states of the :others by their tab, the :watchers, the :handlers and playing? of the element of attach!, and how many calls :use it.
(position {:keys [current-time at paused status playback-rate duration]
:as state}
now)Where the playback state is at now, in milliseconds since the epoch:
its :current-time, moved on by the time since its :at at its rate while
it plays, and no further than its duration.
Where the playback `state` is at `now`, in milliseconds since the epoch: its :current-time, moved on by the time since its :at at its rate while it plays, and no further than its duration.
(press! name details)(press! name details opts)Send the action of details, e.g. {:action :pause}, to the tab whose
element plays the playback of name, with the opts above, which runs
it through the :handlers of its attach!. Where this page's own element
plays, it runs here. It needs an attach! or a watch! of name in this
page, and does nothing where no element plays.
Send the action of `details`, e.g. {:action :pause}, to the tab whose
element plays the playback of `name`, with the `opts` above, which runs
it through the :handlers of its attach!. Where this page's own element
plays, it runs here. It needs an attach! or a watch! of `name` in this
page, and does nothing where no element plays.(share! element)Tell the other tabs of a change that the listener made to the attached
media element while it's paused, e.g. a seek, which makes its
playback the newest.
Tell the other tabs of a change that the listener made to the attached media `element` while it's paused, e.g. a seek, which makes its playback the newest.
The keys of the state of an element that the other tabs learn, from media/state.
The keys of the state of an element that the other tabs learn, from media/state.
(watch! name f)(watch! name f opts)Call f with the newest playback of name across the tabs, now and
whenever it changes, with the opts above, and return a function that
stops it.
The playback is a map of the keys of shared-keys from media/state, with
the :id of what it plays, as the :id-fn of attach! gives it, :at, the
time in milliseconds at which its :current-time was taken, for
position, and :changed, when the listener last changed it. It's :here?
when it's the one of this tab's element, and nil before any tab has
played. Where the window has no BroadcastChannel, f gets nothing.
Call `f` with the newest playback of `name` across the tabs, now and whenever it changes, with the `opts` above, and return a function that stops it. The playback is a map of the keys of shared-keys from media/state, with the :id of what it plays, as the :id-fn of attach! gives it, :at, the time in milliseconds at which its :current-time was taken, for position, and :changed, when the listener last changed it. It's :here? when it's the one of this tab's element, and nil before any tab has played. Where the window has no BroadcastChannel, `f` gets nothing.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |