Mercurial > code > home > repos > light9
comparison web/light9-vidref-replay.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.js@b64a4db527e2 |
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('replay'); | |
7 | |
8 class Light9VidrefReplay extends LitElement { | |
9 | |
10 static get properties() { | |
11 return { | |
12 uri: { type: String }, | |
13 videoUrl: { type: String }, | |
14 songToVideo: { type: Object }, | |
15 videoTime: { type: Number }, | |
16 outVideoCurrentTime: { type: Number }, | |
17 timeErr: { type: Number }, | |
18 playRate: { type: Number }, | |
19 size: { type: String, attribute: true } | |
20 }; | |
21 } | |
22 | |
23 estimateRate() { | |
24 const n = this.songToVideo.length; | |
25 const x0 = Math.round(n * .3); | |
26 const x1 = Math.round(n * .6); | |
27 const pt0 = this.songToVideo[x0]; | |
28 const pt1 = this.songToVideo[x1]; | |
29 return (pt1[1] - pt0[1]) / (pt1[0] - pt0[0]); | |
30 } | |
31 | |
32 setVideoTimeFromSongTime(songTime, isPlaying) { | |
33 if (!this.songToVideo || !this.outVideo || this.outVideo.readyState < 1) { | |
34 return; | |
35 } | |
36 const i = _.sortedIndex(this.songToVideo, [songTime], | |
37 (row) => { return row[0]; }); | |
38 if (i == 0 || i > this.songToVideo.length - 1) { | |
39 isPlaying = false; | |
40 } | |
41 | |
42 this.videoTime = this.songToVideo[Math.max(0, i - 1)][1]; | |
43 | |
44 this.outVideoCurrentTime = this.outVideo.currentTime; | |
45 | |
46 if (isPlaying) { | |
47 if (this.outVideo.paused) { | |
48 this.outVideo.play(); | |
49 this.setRate(this.estimateRate()); | |
50 } | |
51 const err = this.outVideo.currentTime - this.videoTime; | |
52 this.timeErr = err; | |
53 | |
54 if (Math.abs(err) > window.thresh) { | |
55 this.outVideo.currentTime = this.videoTime; | |
56 const p = window.p; | |
57 if (err > 0) { | |
58 this.setRate(this.playRate - err * p); | |
59 } else { | |
60 this.setRate(this.playRate - err * p); | |
61 } | |
62 } | |
63 } else { | |
64 this.outVideo.pause(); | |
65 this.outVideoCurrentTime = this.outVideo.currentTime = this.videoTime; | |
66 this.timeErr = 0; | |
67 } | |
68 } | |
69 | |
70 setRate(r) { | |
71 this.playRate = Math.max(.1, Math.min(4, r)); | |
72 this.outVideo.playbackRate = this.playRate; | |
73 } | |
74 | |
75 firstUpdated() { | |
76 this.outVideo = this.shadowRoot.querySelector('#replay'); | |
77 this.playRate = this.outVideo.playbackRate = 1.0; | |
78 } | |
79 | |
80 onDelete() { | |
81 const u = new URL(window.location.href); | |
82 u.pathname = '/vidref/clips' | |
83 u.searchParams.set('uri', this.uri); | |
84 fetch(u.toString(), {method: 'DELETE'}).then((resp) => { | |
85 let event = new CustomEvent('clips-changed', {detail: {}}); | |
86 this.dispatchEvent(event); | |
87 }); | |
88 } | |
89 | |
90 static get styles() { | |
91 return css` | |
92 :host { | |
93 border: 2px solid #46a79f; | |
94 display: flex; | |
95 flex-direction: column; | |
96 } | |
97 div { | |
98 padding: 5px; | |
99 } | |
100 .num { | |
101 display: inline-block; | |
102 width: 4em; | |
103 color: #29ffa0; | |
104 } | |
105 a { | |
106 color: rgb(97, 97, 255); | |
107 } | |
108 video { | |
109 width: 100%; | |
110 } | |
111 `; | |
112 } | |
113 | |
114 render() { | |
115 let details = ''; | |
116 if (this.size != 'small') { | |
117 details = html` | |
118 <div> | |
119 take is <a href="${this.uri}">${this.uri}</a> | |
120 (${Object.keys(this.songToVideo).length} frames) | |
121 <button @click="${this.onDelete}">Delete</button> | |
122 </div> | |
123 <!-- here, put a little canvas showing what coverage we have with the | |
124 actual/goal time cursors --> | |
125 <div> | |
126 video time should be <span class="num">${this.videoTime} </span> | |
127 actual = <span class="num">${rounding(this.outVideoCurrentTime, 3, 3, true)}</span>, | |
128 err = <span class="num">${rounding(this.timeErr, 3, 4, true)}</span> | |
129 rate = <span class="num">${rounding(this.playRate, 3, 3, true)}</span> | |
130 </div> | |
131 `; | |
132 } | |
133 return html` | |
134 <video id="replay" class="size-${this.size}" src="${this.videoUrl}"></video> | |
135 ${details} | |
136 `; | |
137 | |
138 } | |
139 } | |
140 customElements.define('light9-vidref-replay', Light9VidrefReplay); | |
141 window.thresh=.3 | |
142 window.p=.3 |