straight_to_video 0.0.3
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 +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +7 -0
- data/README.md +156 -0
- data/Rakefile +4 -0
- data/app/assets/javascripts/mediabunny.min.mjs +110 -0
- data/app/assets/javascripts/straight-to-video.js +360 -0
- data/assets/img/backdrop.webp +0 -0
- data/assets/img/logo.webp +0 -0
- data/config/importmap.rb +2 -0
- data/index.html +247 -0
- data/index.js +359 -0
- data/lib/straight_to_video/engine.rb +22 -0
- data/lib/straight_to_video/version.rb +3 -0
- data/lib/straight_to_video.rb +2 -0
- data/package-lock.json +146 -0
- data/package.json +47 -0
- data/playwright.config.mjs +33 -0
- data/script/release +75 -0
- data/script/test +14 -0
- data/script/upgrade +9 -0
- data/script/vendor +52 -0
- metadata +83 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a8ae8a92714ed086e82dd766dcfe917ba3d0b35515592e05a9b76b3fb431e494
|
|
4
|
+
data.tar.gz: b5c2c50569145ab3829703bfd6d3b4dbef25b6233e9b6fcad264182abab9650b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ef4edc9a433c7a0b3dca107dc6e15d6220aa3b9e5897dd4cfa46e3d31ad3fb98bfcac34ce4c49450f1f060fab786b309af4edc66adcc697322c11953034dcc34
|
|
7
|
+
data.tar.gz: 3e763de5d040d99912b0203af7ab0b1b28c3e4ca04c15de59bffc2b7fc15e317a3262c63b1769014f8eaf19362ccf7926cf5cfe1017b6767972788b38db5cd09
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
- Add a rubygem because `bin/importmap pin` cannot possibly work with mediabunny.
|
|
6
|
+
|
|
7
|
+
## 0.0.2
|
|
8
|
+
|
|
9
|
+
- Change `canOptimizeVideo` return shape to `{ ok, reason, message }`.
|
|
10
|
+
- Remove unused `details` (width/height/duration) from `canOptimizeVideo` results.
|
|
11
|
+
- Remove pre-encode size budget check (no more `too-long`).
|
|
12
|
+
- Surface underlying error messages via the `message` field.
|
|
13
|
+
|
|
14
|
+
## 0.0.1
|
|
15
|
+
|
|
16
|
+
* Initial Release
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Searls LLC
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img width="480" align="center" src="assets/img/logo.webp" alt="straight-to-video VHS Logo">
|
|
3
|
+
<br>
|
|
4
|
+
Check out the <a href="https://searlsco.github.io/straight-to-video/">interactive demo page</a>!
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
# straight-to-video
|
|
8
|
+
|
|
9
|
+
[](https://justin.searls.co/shovelware/)
|
|
10
|
+
|
|
11
|
+
`straight-to-video` optimizes video entirely in the browser using [WebCodecs API](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API). Files are remuxed & re-encoded to meet the requirements of the [Instagram API](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media#video-specifications), which is as good a lowest-common-denominator as any.
|
|
12
|
+
|
|
13
|
+
I'm not here to save the planet—I wrote this because I'm cheap and I was sick of waiting all day to upload 4K videos to [my Rails app](https://beckygram.com), only to have to [pay Heroku tooth-and-nail](https://judoscale.com/blog/priced-out-of-heroku) to transcode those videos for me. That said, **if everyone were to plug this into their HTML forms tomorrow, it'd save users and developers a gobsmacking amount of bandwidth and server costs.**
|
|
14
|
+
|
|
15
|
+
Seriously, most off-iPhone videos are **in the neighborhood of 90-95% smaller after running through `straight-to-video` optimization**, sometimes shaving tens of minutes from the time it would take to upload at 30 Mbps.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
You can find [straight-to-video on npm](https://www.npmjs.com/package/straight-to-video) and its
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
npm install straight-to-video
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or if you're one of us Ruby on Rails weirdos:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
bundle add straight_to_video
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### canOptimizeVideo
|
|
34
|
+
|
|
35
|
+
`canOptimizeVideo(file)` quickly validates that a `File` is a recognizable video and that the browser can encode it with WebCodecs. It does not perform any heavy work and does not estimate output size.
|
|
36
|
+
|
|
37
|
+
Returns an object with three fields only: `{ ok, reason, message }`.
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { canOptimizeVideo } from 'straight-to-video'
|
|
41
|
+
|
|
42
|
+
const result = await canOptimizeVideo(file)
|
|
43
|
+
// Example results:
|
|
44
|
+
// { ok: true, reason: 'ok', message: 'ok' }
|
|
45
|
+
// { ok: false, reason: 'probe-failed', message: '…error message…' }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### optimizeVideo
|
|
49
|
+
|
|
50
|
+
`optimizeVideo(file)` will run your file through [this AI-generated fever dream](/index.js) and it'll return a version that's downscaled to 1080p and ready to share, whether it needs to be remuxed, transcoded, or have a silent AAC track injected.
|
|
51
|
+
|
|
52
|
+
The method will generally fail silently without changing the file (worst-case, I just let users upload the same garbage to my server they always have).
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
import { optimizeVideo } from 'straight-to-video'
|
|
56
|
+
|
|
57
|
+
const result = await optimizeVideo(file)
|
|
58
|
+
// Example results:
|
|
59
|
+
// { changed: true, file: File('my-clip-optimized.mp4') }
|
|
60
|
+
// { changed: false, file: File('image.png') }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### registerStraightToVideoController
|
|
64
|
+
|
|
65
|
+
If you're looking to rig `straight-to-video` up with Stimulus, you can register the included controller by passing your Stimulus app and the `Controller` parent class to `registerStraightToVideoController`:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import { Application, Controller } from '@hotwired/stimulus'
|
|
69
|
+
import { registerStraightToVideoController } from 'straight-to-video'
|
|
70
|
+
|
|
71
|
+
const app = Application.start()
|
|
72
|
+
registerStraightToVideoController(app, { Controller })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This will register a controller named `"straight-to-video"` that will target one or more file inputs and handle video optimization during form submission.
|
|
76
|
+
|
|
77
|
+
More on that in the next section.
|
|
78
|
+
|
|
79
|
+
## Integrating with Stimulus
|
|
80
|
+
|
|
81
|
+
I wrote `straight-to-video` to support video workflows for a [couple](https://www.betterwithbecky.com) Rails [apps](https://posseparty.com/). Both apps use [import maps](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#choosing-between-import-maps-and-a-javascript-bundler) and [Stimulus](https://stimulus.hotwired.dev), so the controller that's bundled with this package is how I use `straight-to-video` myself.
|
|
82
|
+
|
|
83
|
+
Here's how to configure it if you have a similar setup.
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
// app/javascript/controllers/index.js
|
|
87
|
+
import { application } from 'controllers/application'
|
|
88
|
+
import { Controller } from '@hotwired/stimulus'
|
|
89
|
+
import { registerStraightToVideoController } from 'straight-to-video'
|
|
90
|
+
|
|
91
|
+
registerStraightToVideoController(application, { Controller })
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
From there, simply add the `straight-to-video` controller to any of your forms that contain file inputs and flag the inputs for which you want to optimize any video files with the target `fileInput`:
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<form data-controller="straight-to-video">
|
|
98
|
+
<input data-straight-to-video-target="fileInput" type="file">
|
|
99
|
+
<!-- The rest of your form -->
|
|
100
|
+
</form>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
By default, this will automatically intercept form submission, optimize your file input(s), and then resubmit the form with optimized video(s).
|
|
104
|
+
|
|
105
|
+
(So it can run before [Active Storage Direct Upload](https://guides.rubyonrails.org/active_storage_overview.html#direct-uploads) handles the document's submit event, the `straight-to-video` controller binds to the _window_'s submit event.)
|
|
106
|
+
|
|
107
|
+
If you want to get a jump on things, you can also wire up the change event to optimize as soon as the user sets the file, so it's ready to upload as soon as submitted:
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<input data-straight-to-video-target="fileInput" type="file"
|
|
111
|
+
data-action="change->straight-to-video#change">
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
And if you have another controller that's interested in displaying progress of optimization or surfacing any errors, you can also bind to the controller's events:
|
|
115
|
+
|
|
116
|
+
* `progress` - fired as optimization proceeds—this event's `detail` contains `{ progress }` as an integer percent completion
|
|
117
|
+
* `done` - fired after optimization finishes, with `detail` containing a boolean `{ changed }`, indicating whether the file was optimized and swapped out
|
|
118
|
+
* `error` - fired when optimization encounters an error, with `detail` containing `{ error }` of the originating error
|
|
119
|
+
|
|
120
|
+
These might be wired up in the same form like this:
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<form data-controller="straight-to-video progress-bar">
|
|
124
|
+
<input data-straight-to-video-target="fileInput" type="file"
|
|
125
|
+
data-action="change->straight-to-video#change
|
|
126
|
+
straight-to-video:progress->progress-bar#update
|
|
127
|
+
straight-to-video:done->progress-bar#finish">
|
|
128
|
+
|
|
129
|
+
<!-- The rest of your form -->
|
|
130
|
+
</form>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
If you want to work on this code, install [ffmpeg](https://ffmpeg.org) and the dependencies:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
brew install ffmpeg
|
|
139
|
+
npm install
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
You can run the tests (which run against both webkit and Chrome) with:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
# Open graphical browsers
|
|
146
|
+
./script/test
|
|
147
|
+
|
|
148
|
+
# Run headlessly
|
|
149
|
+
CI=true ./script/test
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
You can run the server at [localhost:8080](http://localhost:8080) (which hosts the demo site) with:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
npm run dev
|
|
156
|
+
```
|
data/Rakefile
ADDED