diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/light9-vidref-replay-stack.js	Sun May 12 19:02:10 2024 -0700
@@ -0,0 +1,183 @@
+import { LitElement, TemplateResult, html, css } from '/node_modules/lit-element/lit-element.js';
+import debug from '/lib/debug/debug-build-es6.js';
+import _ from '/lib/underscore/underscore-min-es6.js';
+import { rounding }  from '/node_modules/significant-rounding/index.js';
+
+const log = debug('stack');
+
+class Light9VidrefReplayStack extends LitElement {
+    
+    static get properties() {
+        return {
+            songTime: { type: Number, attribute: false }, // from musicState.t but higher res
+            musicState: { type: Object, attribute: false },
+            players: { type: Array, attribute: false },
+            size: { type: String, attribute: true }
+        };
+    }
+
+    constructor() {
+        super();
+        this.musicState = {};
+    }
+
+    setVideoTimesFromSongTime() {
+        this.shadowRoot.querySelectorAll('light9-vidref-replay').forEach(
+            (r) => {
+                r.setVideoTimeFromSongTime(this.songTime, this.musicState.playing);
+            });
+    }
+    nudgeTime(dt) {
+        this.songTime += dt;
+        log('song now', this.songTime);
+    }
+    fineTime() {
+        if (this.musicState.playing) {
+            const sinceLastUpdate = (Date.now() - this.musicState.reportTime) / 1000;
+            this.songTime = sinceLastUpdate + this.musicState.tStart;
+        } else if (this.lastFineTimePlayingState)  {
+            this.songTime = this.musicState.t;
+        }
+        this.lastFineTimePlayingState = this.musicState.playing;
+        requestAnimationFrame(this.fineTime.bind(this));
+    }
+
+    updated(changedProperties) {
+        if (changedProperties.has('songTime')) {
+            this.setVideoTimesFromSongTime();
+        }
+    }
+
+    firstUpdated() {
+        this.songTimeRangeInput = this.shadowRoot.querySelector('#songTime');
+
+        const ws = reconnectingWebSocket('../ascoltami/time/stream',
+                                         this.receivedSongAndTime.bind(this));
+        reconnectingWebSocket('../vidref/time/stream', this.receivedRemoteScrubbedTime.bind(this));
+        // bug: upon connecting, clear this.song
+        this.fineTime();
+    }
+
+    receivedSongAndTime(msg) {
+        this.musicState = msg;
+        this.musicState.reportTime = Date.now();
+        this.musicState.tStart = this.musicState.t;            
+
+        this.songTimeRangeInput.max = this.musicState.duration;
+
+        if (this.musicState.song != this.song) {
+            this.song = this.musicState.song;
+            this.getReplayMapForSong(this.song);
+        }
+    }
+
+    receivedRemoteScrubbedTime(msg) {
+        this.songTime = msg.st;
+
+        // This doesn't work completely since it will keep getting
+        // updates from ascoltami slow updates.
+        if (msg.song != this.song) {
+            this.song = msg.song;
+            this.getReplayMapForSong(this.song);
+        }
+    }
+        
+    getReplayMapForSong(song) {
+        const u = new URL(window.location.href);
+        u.pathname = '/vidref/replayMap'
+        u.searchParams.set('song', song);
+        u.searchParams.set('maxClips', this.size == "small" ? '1' : '3');
+        fetch(u.toString()).then((resp) => {
+            if (resp.ok) {
+                resp.json().then((msg) => {
+                    this.players = msg.map(this.makeClipRow.bind(this));
+                    this.updateComplete.then(this.setupClipRows.bind(this, msg));
+                });
+            }
+        });          
+    }
+    
+    setupClipRows(msg) {
+        const nodes = this.shadowRoot.querySelectorAll('light9-vidref-replay');
+        nodes.forEach((node, i) => {
+            node.uri = msg[i].uri;
+            node.videoUrl = msg[i].videoUrl;
+            node.songToVideo = msg[i].songToVideo;
+        });
+        this.setVideoTimesFromSongTime();
+    }
+    
+    makeClipRow(clip) {
+        return html`<light9-vidref-replay @clips-changed="${this.onClipsChanged}" size="${this.size}"></light9-vidref-replay>`;
+    }
+    
+    onClipsChanged(ev) {
+        this.getReplayMapForSong(this.song);
+    }
+    
+    disconnectedCallback() {
+        log('bye');
+        //close socket
+    }
+
+    userMovedSongTime(ev) {
+        const st = this.songTimeRangeInput.valueAsNumber;
+        this.songTime = st;
+
+        fetch('/ascoltami/seekPlayOrPause', {
+            method: 'POST',
+            body: JSON.stringify({scrub: st}),
+        });
+    }
+
+    static get styles() {
+        return css`
+        :host {
+           display: inline-block;
+        }
+        #songTime {
+            width: 100%;
+        }
+        #clips {
+            display: flex;
+            flex-direction: column;
+        }
+        a {
+            color: rgb(97, 97, 255);
+        }
+        #songTime {
+            font-size: 27px;
+        }
+        light9-vidref-replay {
+            margin: 5px;
+        }
+        `;
+    }
+    
+    render() {
+        const songTimeRange = this.size != "small" ? html`<input id="songTime" type="range" 
+           .value="${this.songTime}" 
+           @input="${this.userMovedSongTime}" 
+           min="0" max="0" step=".001"></div>
+  <div><a href="${this.musicState.song}">${this.musicState.song}</a></div>` : '';
+
+
+        const globalCommands = this.size != 'small' ? html`
+  <div>
+    <button @click="${this.onClipsChanged}">Refresh clips for song</button>
+  </div>
+` : '';
+        return html`
+  <div>
+    ${songTimeRange}
+  <div id="songTime">showing song time ${rounding(this.songTime, 3)}</div>
+  <div>clips:</div>
+  <div id="clips">
+    ${this.players}
+  </div>
+  ${globalCommands}
+`;
+
+    }
+}
+customElements.define('light9-vidref-replay-stack', Light9VidrefReplayStack);