straight_to_video 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/app/assets/javascripts/straight-to-video.js +38 -7
- data/index.html +8 -5
- data/index.js +37 -6
- data/lib/straight_to_video/version.rb +1 -1
- data/package-lock.json +2 -2
- data/package.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57371915cd38c1ecac814d16a43244275216661f31b639a80022685672984e22
|
|
4
|
+
data.tar.gz: bb04ab7274edbb61031a270c8f650872b0b2338a7a1fc93a54948c8f3a3fcd04
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92b3e2d474ba435ac6679c26755a9d62bb5388cd3fbb984fc6b31506287b4ced1be3a7c3621efdaf1812a9feae7f60287caa9e91e3601731c60c0a450e4d8824
|
|
7
|
+
data.tar.gz: 550b54b8fb004e7b6fea54282f00053fe47fd4bdefd20a5e87817c91495756adfe38c40d72888de883b0fbc049ad9615eb3f4f1783b05d01d92fc418356645cb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// straight-to-video@0.0.
|
|
1
|
+
// straight-to-video@0.0.6 vendored by the straight_to_video gem
|
|
2
2
|
// straight-to-video - https://github.com/searlsco/straight-to-video
|
|
3
3
|
|
|
4
4
|
// ----- External imports -----
|
|
@@ -149,6 +149,20 @@ async function waitForFrameReady (video, budgetMs) {
|
|
|
149
149
|
})
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
async function seekOnce (video, time) {
|
|
153
|
+
if (!video) return
|
|
154
|
+
const t = Number.isFinite(time) ? time : 0
|
|
155
|
+
if (Math.abs(video.currentTime - t) < 1e-6) return
|
|
156
|
+
await new Promise((resolve) => {
|
|
157
|
+
const onSeeked = () => {
|
|
158
|
+
video.removeEventListener('seeked', onSeeked)
|
|
159
|
+
resolve()
|
|
160
|
+
}
|
|
161
|
+
video.addEventListener('seeked', onSeeked, { once: true })
|
|
162
|
+
video.currentTime = t
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
152
166
|
async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
153
167
|
const w = srcMeta.w
|
|
154
168
|
const h = srcMeta.h
|
|
@@ -205,8 +219,22 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
205
219
|
const url = URL.createObjectURL(file)
|
|
206
220
|
const v = document.createElement('video')
|
|
207
221
|
v.muted = true; v.preload = 'auto'; v.playsInline = true
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
await new Promise((resolve, reject) => {
|
|
223
|
+
const onLoaded = () => {
|
|
224
|
+
v.removeEventListener('loadedmetadata', onLoaded)
|
|
225
|
+
v.removeEventListener('error', onError)
|
|
226
|
+
resolve()
|
|
227
|
+
}
|
|
228
|
+
const onError = () => {
|
|
229
|
+
v.removeEventListener('loadedmetadata', onLoaded)
|
|
230
|
+
v.removeEventListener('error', onError)
|
|
231
|
+
reject(new Error('video load failed'))
|
|
232
|
+
}
|
|
233
|
+
v.addEventListener('loadedmetadata', onLoaded)
|
|
234
|
+
v.addEventListener('error', onError)
|
|
235
|
+
v.src = url
|
|
236
|
+
try { v.load() } catch (_) {}
|
|
237
|
+
})
|
|
210
238
|
const canvas = document.createElement('canvas'); canvas.width = targetWidth; canvas.height = targetHeight
|
|
211
239
|
const ctx = canvas.getContext('2d', { alpha: false })
|
|
212
240
|
|
|
@@ -216,13 +244,15 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
216
244
|
const drawTime = i === 0
|
|
217
245
|
? Math.min(Math.max(0, t + (step * 0.5)), Math.max(0.000001, durationCfr - 0.000001))
|
|
218
246
|
: targetTime
|
|
219
|
-
|
|
220
|
-
await
|
|
247
|
+
if (i === 0) {}
|
|
248
|
+
await seekOnce(v, drawTime)
|
|
249
|
+
if (i === 0) {}
|
|
221
250
|
const budgetMs = Math.min(34, Math.max(17, Math.round(step * 1000)))
|
|
222
251
|
const presented = await waitForFrameReady(v, budgetMs)
|
|
223
252
|
if (!presented && i === 0) {
|
|
224
253
|
const nudge = Math.min(step * 0.25, 0.004)
|
|
225
|
-
|
|
254
|
+
const target = Math.min(drawTime + nudge, Math.max(0.000001, durationCfr - 0.000001))
|
|
255
|
+
await seekOnce(v, target)
|
|
226
256
|
}
|
|
227
257
|
|
|
228
258
|
ctx.drawImage(v, 0, 0, canvas.width, canvas.height)
|
|
@@ -255,7 +285,6 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
255
285
|
const sample = new AudioSample({ format: 'f32', sampleRate: TARGET_AUDIO_SR, numberOfChannels: TARGET_AUDIO_CHANNELS, timestamp: 0, data: interleaved })
|
|
256
286
|
await audioSource.add(sample)
|
|
257
287
|
audioSource.close()
|
|
258
|
-
|
|
259
288
|
await output.finalize()
|
|
260
289
|
const { buffer } = output.target
|
|
261
290
|
const payload = new Uint8Array(buffer)
|
|
@@ -323,6 +352,8 @@ function registerStraightToVideoController (app, opts = {}) {
|
|
|
323
352
|
}
|
|
324
353
|
|
|
325
354
|
async _processFileInput (fileInput) {
|
|
355
|
+
const ua = typeof navigator !== 'undefined' && navigator.userAgent ? navigator.userAgent : ''
|
|
356
|
+
const isIos = /iP(hone|ad|od)/.test(ua)
|
|
326
357
|
this._markFlag(fileInput, 'processing')
|
|
327
358
|
fileInput.disabled = true
|
|
328
359
|
try {
|
data/index.html
CHANGED
|
@@ -169,8 +169,8 @@
|
|
|
169
169
|
|
|
170
170
|
<section id="install">
|
|
171
171
|
<h2>Install</h2>
|
|
172
|
-
<pre class="language-bash"><code>npm install straight-to-video</code></pre>
|
|
173
|
-
<pre class="language-bash"><code>bundle add straight_to_video</code></pre>
|
|
172
|
+
<pre class="language-bash"><code>npm install straight-to-video</code></pre>
|
|
173
|
+
<pre class="language-bash"><code>bundle add straight_to_video</code></pre>
|
|
174
174
|
<p>
|
|
175
175
|
<a class="btn ghost readme" href="https://github.com/searlsco/straight-to-video" target="_blank" rel="noopener">View README</a>
|
|
176
176
|
</p>
|
|
@@ -179,7 +179,12 @@
|
|
|
179
179
|
|
|
180
180
|
<script type="module">
|
|
181
181
|
import { Application, Controller } from '@hotwired/stimulus'
|
|
182
|
-
import { registerStraightToVideoController } from 'straight-to-video'
|
|
182
|
+
import { canOptimizeVideo, optimizeVideo, registerStraightToVideoController } from 'straight-to-video'
|
|
183
|
+
|
|
184
|
+
// Declare as global so that people can play with them
|
|
185
|
+
window.canOptimizeVideo = canOptimizeVideo
|
|
186
|
+
window.optimizeVideo = optimizeVideo
|
|
187
|
+
window.registerStraightToVideoController = registerStraightToVideoController
|
|
183
188
|
|
|
184
189
|
const app = Application.start()
|
|
185
190
|
registerStraightToVideoController(app, { Controller })
|
|
@@ -193,8 +198,6 @@
|
|
|
193
198
|
const dl = document.getElementById('download')
|
|
194
199
|
const sizes = document.getElementById('sizes')
|
|
195
200
|
|
|
196
|
-
// Removed environment badge
|
|
197
|
-
|
|
198
201
|
// Single‑button flow: open picker then auto‑submit
|
|
199
202
|
tryBtn.addEventListener('click', () => {
|
|
200
203
|
if (fileEl.showPicker) { try { fileEl.showPicker(); return } catch (_) {} }
|
data/index.js
CHANGED
|
@@ -148,6 +148,20 @@ async function waitForFrameReady (video, budgetMs) {
|
|
|
148
148
|
})
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
async function seekOnce (video, time) {
|
|
152
|
+
if (!video) return
|
|
153
|
+
const t = Number.isFinite(time) ? time : 0
|
|
154
|
+
if (Math.abs(video.currentTime - t) < 1e-6) return
|
|
155
|
+
await new Promise((resolve) => {
|
|
156
|
+
const onSeeked = () => {
|
|
157
|
+
video.removeEventListener('seeked', onSeeked)
|
|
158
|
+
resolve()
|
|
159
|
+
}
|
|
160
|
+
video.addEventListener('seeked', onSeeked, { once: true })
|
|
161
|
+
video.currentTime = t
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
151
165
|
async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
152
166
|
const w = srcMeta.w
|
|
153
167
|
const h = srcMeta.h
|
|
@@ -204,8 +218,22 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
204
218
|
const url = URL.createObjectURL(file)
|
|
205
219
|
const v = document.createElement('video')
|
|
206
220
|
v.muted = true; v.preload = 'auto'; v.playsInline = true
|
|
207
|
-
|
|
208
|
-
|
|
221
|
+
await new Promise((resolve, reject) => {
|
|
222
|
+
const onLoaded = () => {
|
|
223
|
+
v.removeEventListener('loadedmetadata', onLoaded)
|
|
224
|
+
v.removeEventListener('error', onError)
|
|
225
|
+
resolve()
|
|
226
|
+
}
|
|
227
|
+
const onError = () => {
|
|
228
|
+
v.removeEventListener('loadedmetadata', onLoaded)
|
|
229
|
+
v.removeEventListener('error', onError)
|
|
230
|
+
reject(new Error('video load failed'))
|
|
231
|
+
}
|
|
232
|
+
v.addEventListener('loadedmetadata', onLoaded)
|
|
233
|
+
v.addEventListener('error', onError)
|
|
234
|
+
v.src = url
|
|
235
|
+
try { v.load() } catch (_) {}
|
|
236
|
+
})
|
|
209
237
|
const canvas = document.createElement('canvas'); canvas.width = targetWidth; canvas.height = targetHeight
|
|
210
238
|
const ctx = canvas.getContext('2d', { alpha: false })
|
|
211
239
|
|
|
@@ -215,13 +243,15 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
215
243
|
const drawTime = i === 0
|
|
216
244
|
? Math.min(Math.max(0, t + (step * 0.5)), Math.max(0.000001, durationCfr - 0.000001))
|
|
217
245
|
: targetTime
|
|
218
|
-
|
|
219
|
-
await
|
|
246
|
+
if (i === 0) {}
|
|
247
|
+
await seekOnce(v, drawTime)
|
|
248
|
+
if (i === 0) {}
|
|
220
249
|
const budgetMs = Math.min(34, Math.max(17, Math.round(step * 1000)))
|
|
221
250
|
const presented = await waitForFrameReady(v, budgetMs)
|
|
222
251
|
if (!presented && i === 0) {
|
|
223
252
|
const nudge = Math.min(step * 0.25, 0.004)
|
|
224
|
-
|
|
253
|
+
const target = Math.min(drawTime + nudge, Math.max(0.000001, durationCfr - 0.000001))
|
|
254
|
+
await seekOnce(v, target)
|
|
225
255
|
}
|
|
226
256
|
|
|
227
257
|
ctx.drawImage(v, 0, 0, canvas.width, canvas.height)
|
|
@@ -254,7 +284,6 @@ async function encodeVideo ({ file, srcMeta, onProgress }) {
|
|
|
254
284
|
const sample = new AudioSample({ format: 'f32', sampleRate: TARGET_AUDIO_SR, numberOfChannels: TARGET_AUDIO_CHANNELS, timestamp: 0, data: interleaved })
|
|
255
285
|
await audioSource.add(sample)
|
|
256
286
|
audioSource.close()
|
|
257
|
-
|
|
258
287
|
await output.finalize()
|
|
259
288
|
const { buffer } = output.target
|
|
260
289
|
const payload = new Uint8Array(buffer)
|
|
@@ -322,6 +351,8 @@ function registerStraightToVideoController (app, opts = {}) {
|
|
|
322
351
|
}
|
|
323
352
|
|
|
324
353
|
async _processFileInput (fileInput) {
|
|
354
|
+
const ua = typeof navigator !== 'undefined' && navigator.userAgent ? navigator.userAgent : ''
|
|
355
|
+
const isIos = /iP(hone|ad|od)/.test(ua)
|
|
325
356
|
this._markFlag(fileInput, 'processing')
|
|
326
357
|
fileInput.disabled = true
|
|
327
358
|
try {
|
data/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "straight-to-video",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "straight-to-video",
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.6",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"mediabunny": "^1.24.4"
|
data/package.json
CHANGED