Mercurial > code > home > repos > light9
comparison web/light9-vidref-replay-stack.js @ 2376:4556eebe5d73
topdir reorgs; let pdm have its src/ dir; separate vite area from light9/
author | drewp@bigasterisk.com |
---|---|
date | Sun, 12 May 2024 19:02:10 -0700 |
parents | light9/web/light9-vidref-replay-stack.js@286a34d9ccba |
children |
comparison
equal
deleted
inserted
replaced
2375:623836db99af | 2376:4556eebe5d73 |
---|---|
1 import { LitElement, TemplateResult, html, css } from '/node_modules/lit-element/lit-element.js'; | |
2 import debug from '/lib/debug/debug-build-es6.js'; | |
3 import _ from '/lib/underscore/underscore-min-es6.js'; | |
4 import { rounding } from '/node_modules/significant-rounding/index.js'; | |
5 | |
6 const log = debug('stack'); | |
7 | |
8 class Light9VidrefReplayStack extends LitElement { | |
9 | |
10 static get properties() { | |
11 return { | |
12 songTime: { type: Number, attribute: false }, // from musicState.t but higher res | |
13 musicState: { type: Object, attribute: false }, | |
14 players: { type: Array, attribute: false }, | |
15 size: { type: String, attribute: true } | |
16 }; | |
17 } | |
18 | |
19 constructor() { | |
20 super(); | |
21 this.musicState = {}; | |
22 } | |
23 | |
24 setVideoTimesFromSongTime() { | |
25 this.shadowRoot.querySelectorAll('light9-vidref-replay').forEach( | |
26 (r) => { | |
27 r.setVideoTimeFromSongTime(this.songTime, this.musicState.playing); | |
28 }); | |
29 } | |
30 nudgeTime(dt) { | |
31 this.songTime += dt; | |
32 log('song now', this.songTime); | |
33 } | |
34 fineTime() { | |
35 if (this.musicState.playing) { | |
36 const sinceLastUpdate = (Date.now() - this.musicState.reportTime) / 1000; | |
37 this.songTime = sinceLastUpdate + this.musicState.tStart; | |
38 } else if (this.lastFineTimePlayingState) { | |
39 this.songTime = this.musicState.t; | |
40 } | |
41 this.lastFineTimePlayingState = this.musicState.playing; | |
42 requestAnimationFrame(this.fineTime.bind(this)); | |
43 } | |
44 | |
45 updated(changedProperties) { | |
46 if (changedProperties.has('songTime')) { | |
47 this.setVideoTimesFromSongTime(); | |
48 } | |
49 } | |
50 | |
51 firstUpdated() { | |
52 this.songTimeRangeInput = this.shadowRoot.querySelector('#songTime'); | |
53 | |
54 const ws = reconnectingWebSocket('../ascoltami/time/stream', | |
55 this.receivedSongAndTime.bind(this)); | |
56 reconnectingWebSocket('../vidref/time/stream', this.receivedRemoteScrubbedTime.bind(this)); | |
57 // bug: upon connecting, clear this.song | |
58 this.fineTime(); | |
59 } | |
60 | |
61 receivedSongAndTime(msg) { | |
62 this.musicState = msg; | |
63 this.musicState.reportTime = Date.now(); | |
64 this.musicState.tStart = this.musicState.t; | |
65 | |
66 this.songTimeRangeInput.max = this.musicState.duration; | |
67 | |
68 if (this.musicState.song != this.song) { | |
69 this.song = this.musicState.song; | |
70 this.getReplayMapForSong(this.song); | |
71 } | |
72 } | |
73 | |
74 receivedRemoteScrubbedTime(msg) { | |
75 this.songTime = msg.st; | |
76 | |
77 // This doesn't work completely since it will keep getting | |
78 // updates from ascoltami slow updates. | |
79 if (msg.song != this.song) { | |
80 this.song = msg.song; | |
81 this.getReplayMapForSong(this.song); | |
82 } | |
83 } | |
84 | |
85 getReplayMapForSong(song) { | |
86 const u = new URL(window.location.href); | |
87 u.pathname = '/vidref/replayMap' | |
88 u.searchParams.set('song', song); | |
89 u.searchParams.set('maxClips', this.size == "small" ? '1' : '3'); | |
90 fetch(u.toString()).then((resp) => { | |
91 if (resp.ok) { | |
92 resp.json().then((msg) => { | |
93 this.players = msg.map(this.makeClipRow.bind(this)); | |
94 this.updateComplete.then(this.setupClipRows.bind(this, msg)); | |
95 }); | |
96 } | |
97 }); | |
98 } | |
99 | |
100 setupClipRows(msg) { | |
101 const nodes = this.shadowRoot.querySelectorAll('light9-vidref-replay'); | |
102 nodes.forEach((node, i) => { | |
103 node.uri = msg[i].uri; | |
104 node.videoUrl = msg[i].videoUrl; | |
105 node.songToVideo = msg[i].songToVideo; | |
106 }); | |
107 this.setVideoTimesFromSongTime(); | |
108 } | |
109 | |
110 makeClipRow(clip) { | |
111 return html`<light9-vidref-replay @clips-changed="${this.onClipsChanged}" size="${this.size}"></light9-vidref-replay>`; | |
112 } | |
113 | |
114 onClipsChanged(ev) { | |
115 this.getReplayMapForSong(this.song); | |
116 } | |
117 | |
118 disconnectedCallback() { | |
119 log('bye'); | |
120 //close socket | |
121 } | |
122 | |
123 userMovedSongTime(ev) { | |
124 const st = this.songTimeRangeInput.valueAsNumber; | |
125 this.songTime = st; | |
126 | |
127 fetch('/ascoltami/seekPlayOrPause', { | |
128 method: 'POST', | |
129 body: JSON.stringify({scrub: st}), | |
130 }); | |
131 } | |
132 | |
133 static get styles() { | |
134 return css` | |
135 :host { | |
136 display: inline-block; | |
137 } | |
138 #songTime { | |
139 width: 100%; | |
140 } | |
141 #clips { | |
142 display: flex; | |
143 flex-direction: column; | |
144 } | |
145 a { | |
146 color: rgb(97, 97, 255); | |
147 } | |
148 #songTime { | |
149 font-size: 27px; | |
150 } | |
151 light9-vidref-replay { | |
152 margin: 5px; | |
153 } | |
154 `; | |
155 } | |
156 | |
157 render() { | |
158 const songTimeRange = this.size != "small" ? html`<input id="songTime" type="range" | |
159 .value="${this.songTime}" | |
160 @input="${this.userMovedSongTime}" | |
161 min="0" max="0" step=".001"></div> | |
162 <div><a href="${this.musicState.song}">${this.musicState.song}</a></div>` : ''; | |
163 | |
164 | |
165 const globalCommands = this.size != 'small' ? html` | |
166 <div> | |
167 <button @click="${this.onClipsChanged}">Refresh clips for song</button> | |
168 </div> | |
169 ` : ''; | |
170 return html` | |
171 <div> | |
172 ${songTimeRange} | |
173 <div id="songTime">showing song time ${rounding(this.songTime, 3)}</div> | |
174 <div>clips:</div> | |
175 <div id="clips"> | |
176 ${this.players} | |
177 </div> | |
178 ${globalCommands} | |
179 `; | |
180 | |
181 } | |
182 } | |
183 customElements.define('light9-vidref-replay-stack', Light9VidrefReplayStack); |