@wcstack/camera 4.4.0 → 4.6.0
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.
- package/README.ja.md +14 -6
- package/README.md +14 -6
- package/dist/auto.d.ts +2 -0
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.esm.js +264 -52
- package/dist/index.esm.js.map +1 -1
- package/package.json +5 -2
package/README.ja.md
CHANGED
|
@@ -50,16 +50,16 @@ $on: {
|
|
|
50
50
|
|
|
51
51
|
**コマンド:** `start()`・`stop()`・`switchCamera()`(前後カメラ切替)。
|
|
52
52
|
|
|
53
|
-
**バインド可能な値:** `active`(ストリーム生存)・`permission` / `audioPermission`(`prompt`/`granted`/`denied`/`unsupported`)・`deviceId`(ストリームが使っているデバイス。`wcs-camera:device-changed` で publish。プロパティを読むと、決まった後はこの値、それまでは要求した `device-id` を返す。要求そのものは属性に残る)・`devices
|
|
53
|
+
**バインド可能な値:** `active`(ストリーム生存)・`permission` / `audioPermission`(`prompt`/`granted`/`denied`/`unsupported`。4.5 から、このページでカメラ/マイクを使えないとき — secure context でない、Permissions-Policy が切っている — も `unsupported`。そのとき `start()` はブラウザに聞かず、`errorInfo` は理由付きの `capability-missing`)・`deviceId`(ストリームが使っているデバイス。`wcs-camera:device-changed` で publish。プロパティを読むと、決まった後はこの値、それまでは要求した `device-id` を返す。要求そのものは属性に残る)・`devices`(映像入力の一覧。`[{ deviceId, label, groupId, kind: "videoinput" }]`。許可されるまで label は空)・`error`・`errorInfo`(`WcsIoErrorInfo | null`——`error` から派生するシリアライズ可能な失敗分類。`wcs-camera:error-info-changed` で publish。下記「注意・落とし穴」参照)。
|
|
54
54
|
|
|
55
55
|
**イベント(event-token):** `streamReady`(`wcs-camera:stream-ready`、detail = 生 `MediaStream`)・`error`・`ended`(OS によるトラック剥奪)。`streamReady` の「プロパティ」は event-token 配線用で、値としてバインドしないでください。
|
|
56
56
|
|
|
57
57
|
### ライフサイクル
|
|
58
58
|
|
|
59
|
-
- `disconnectedCallback` で全トラックを `track.stop()` し、ハードウェアインジケータを消します。ストリームのリークは本ノード固有の唯一の失敗様式です。
|
|
59
|
+
- `disconnectedCallback` で全トラックを `track.stop()` し、ハードウェアインジケータを消します。ストリームのリークは本ノード固有の唯一の失敗様式です。4.5 から、`active` も `false` に戻し、`wcs-camera:active-changed` で知らせます。ルートを離れた要素は、次に入るときのためにノードを束縛ごと残すので(router の README「ルートの中身の置き場所」)、4.4 までは戻ってきたページがカメラが動いていると表示し続け、`active` の間は押せない開始ボタンも押せないままでした。
|
|
60
60
|
- 要素を DOM 内で移動(remove → 再 append)すると `disconnectedCallback`(dispose・トラック停止)の後に `connectedCallback`(再 observe)が走ります。`autostart` 付きなら reconnect 時に再取得します(再プロンプトの可能性あり)。移動をまたいでストリームを維持したい場合は `autostart` を使わず自分で再 `start()` するか、要素を detach しないでください。
|
|
61
61
|
- 制約変更(`device-id`・`facing-mode`・`switchCamera()`)は **再取得**(stop → 新 `getUserMedia`)を起こし、世代カウンタでガードするため、supersede された取得が orphan ストリームを生かしたまま残すことはありません。
|
|
62
|
-
- ページ非表示中はストリームを suspend し、復帰時に再取得します——`keep-alive` がある場合を除く。`keepAlive: recording` をバインドすると録画中はカメラを生かし続けられます。
|
|
62
|
+
- ページ非表示中はストリームを suspend し、復帰時に再取得します——`keep-alive` がある場合を除く。`keepAlive: recording` をバインドすると録画中はカメラを生かし続けられます。suspend したストリームの録画はそこで終わり、それまでの分がクリップになります。4.5 から、`<wcs-recorder>` が録画中のストリームを suspend するとき、カメラが警告します(`[wcs/camera-keep-alive-missing]`、1 要素につき 1 回)。
|
|
63
63
|
|
|
64
64
|
## `<wcs-recorder>`
|
|
65
65
|
|
|
@@ -78,8 +78,14 @@ $on: {
|
|
|
78
78
|
> **`duration` は stop/pause で確定する値で、録画中はライブ更新しません。** 内部に経過タイマーは持たないため、`start()` から最初の `pause()`/`stop()` までは `0` のままです。録画中の経過時間を表示したい場合は `recording` フラグを起点にクライアント側でタイマーを回してください。
|
|
79
79
|
|
|
80
80
|
> **`mimeType` は「要求」と「解決後」の二面を持ちます。** **入力**は `mime-type` *属性*(recorder に使ってほしいタイプの要求)、**出力**は `mimeType` *バインド可能値*(ブラウザが実際に選んだタイプ。`wcs-recorder:mimetype-changed` で publish)です。基底名を共有しますが別サーフェスで、属性側で要求を設定し(`mime-type` 属性/要素 setter)、値プロパティ側で解決後タイプを読みます。`mimeType` を読んでも書いた値はエコーされません——録画結果を反映します。
|
|
81
|
+
>
|
|
82
|
+
> 宣言は `mimeType` を入力にも出力にも挙げているので、`@wcstack/state` は `mimeType: path` を双方向として扱います。双方向の束縛は、まず state の値を要素に書きます。初期値が `null` なら `mime-type` が外れ、型はブラウザが選びます。要求に触れずに解決後のタイプを読むには `mimeType#init=element: path` と書いてください。
|
|
83
|
+
>
|
|
84
|
+
> Chromium は録画を始めるときにタイプを決めます(タイプを指定せずに作った `MediaRecorder` は、それまで `""` を返します)。4.4 までは、`mime-type` が無いとクリップの `Blob` に型が無く、`mimeType` も `""` のままで、型を確かめるサーバや `<wcs-upload accept="video/*">` がクリップを拒みました。4.5 から、`mimeType` は録画を始めたときのタイプ(無ければ最初のチャンクのタイプ)になり、`Blob` もその型を持ちます。
|
|
81
85
|
|
|
82
|
-
組み立てた `Blob` は structured-clone 可能なので *値* であり、state に流せます——例えば `new File([blob], "clip.webm")` を [`@wcstack/upload`](../upload/) へ。object URL は管理され、新しいクリップ前と **`disconnectedCallback`(dispose)時** に前の URL を revoke します。
|
|
86
|
+
組み立てた `Blob` は structured-clone 可能なので *値* であり、state に流せます——例えば `new File([blob], "clip.webm", { type: blob.type })` を [`@wcstack/upload`](../upload/) へ。object URL は管理され、新しいクリップ前と **`disconnectedCallback`(dispose)時** に前の URL を revoke します。
|
|
87
|
+
|
|
88
|
+
**録画中に文書から外れたとき**(ルートを離れた、`if:` の枝が閉じた): 4.4 までは録画を捨て、`recorded` も出さず、state の `recording` は `true` のままでした。4.5 から、ストリームのトラックが終わったときと同じく録画を止めます。`recording` / `paused` はその場で `false` に戻してイベントで知らせ、クリップはブラウザが渡したとき(少し後)に `wcs-recorder:recorded` で届きます。その `objectURL` は、次のクリップか次の切断まで有効です。
|
|
83
89
|
|
|
84
90
|
> **`objectURL` の寿命は recorder に束縛されます。** dispose が最後の object URL を revoke し、**さらに次の録画は前のクリップの URL を新規発行前に revoke する**ため、古い URL を指したままの `<video src>` / `<wcs-upload>` は `<wcs-recorder>` が外れる・次のクリップが完成した時点で壊れます。常に最新の `objectURL` / `recorded` 値を追従し、古い URL を保持しないでください。URL を寿命の長い消費側へ渡す場合は、URL を使い終わるまで recorder を接続したままにするか、`Blob` から自前で URL を作り(`URL.createObjectURL(blob)`)revoke も自分で管理してください。structured-clone 可能な **`blob`** にはこの結合がありません——`Blob` を state に流し、利用箇所で URL を生成するのが安全です。
|
|
85
91
|
|
|
@@ -152,12 +158,14 @@ Core の構造サーフェスは wcstack I/O ノード横断の規範です([asy
|
|
|
152
158
|
## 注意・落とし穴
|
|
153
159
|
|
|
154
160
|
- **セキュアコンテキスト(https)必須。** `getUserMedia` は `file://` / 素の `http://` では使えません。
|
|
161
|
+
- **Content-Security-Policy。** 4.5 から、プレビュー自身のスタイルは構築したスタイルシートで、`style-src` の対象外です。`'unsafe-inline'` の無いポリシーでもプレビューの配置が保たれます。4.4 までは shadow root の `<style>` で、そうしたポリシーに止められました(プレビューの `<video>` が元の大きさで描かれる)。その版では自分のスタイルシートで `wcs-camera::part(video) { display: block; width: 100%; height: 100% }` と書いてください。
|
|
162
|
+
- **`error` / `errorInfo` は最後の試みを表します。** 後から(サイトの設定で)許可されても消えません。`permission` はすぐに許可に追従しますが、`errorInfo` は次の `start()` が成功するまで `not-allowed` のままです。「止められている」は `permission` で判断し、最後のエラーが `not-allowed` で `permission` が `granted` なら、もう一度開始を促してください。
|
|
155
163
|
- **カメラインジケータ=リーク検出器。** 終了後も点灯したままなら、トラックが停止されていません。
|
|
156
164
|
- **ユーザージェスチャ。** 一部ブラウザは `getUserMedia` をユーザー操作起点で要求します。タイマーから撃つと無言で失敗することがあります(`error` に出ます・throw しません)。
|
|
157
165
|
- **エラーは分類され throw されません:** `NotAllowedError`(拒否)・`NotFoundError`(デバイス無し)・`NotReadableError`(他アプリ使用中)・`OverconstrainedError`。
|
|
158
166
|
- **`errorInfo`——付加的な失敗分類。** `error` と並んで、`<wcs-camera>` / `<wcs-recorder>` はどちらも付加的なバインド可能出力 `errorInfo`(`WcsIoErrorInfo` = 安定した `code` / `phase` / `recoverable` / `message`)を公開します。同じ失敗から派生し、`wcs-camera:error-info-changed` / `wcs-recorder:error-info-changed` で publish されます。`error` の形状は不変で、`errorInfo` は `error` と完全に同期して遷移します(成功時に `null` へクリア)。両要素は 1 つの code セット(`core/mediaCapabilities.ts` の `WCS_MEDIA_ERROR_CODE`)を共有します:
|
|
159
|
-
- `capability-missing`(phase `probe`)——`getUserMedia` / `MediaRecorder` 非対応(非セキュアコンテキスト含む)。
|
|
160
|
-
- `not-allowed`(phase `start`)——`NotAllowedError` / `SecurityError
|
|
167
|
+
- `capability-missing`(phase `probe`)——`getUserMedia` / `MediaRecorder` 非対応(非セキュアコンテキスト含む)。4.5 から、Permissions-Policy が切ったカメラ/マイクも(ブラウザに聞く前に見つける)。
|
|
168
|
+
- `not-allowed`(phase `start`)——`NotAllowedError` / `SecurityError`(権限拒否。4.4 までは feature-policy ブロックも)。
|
|
161
169
|
- `not-found`(phase `start`)——`NotFoundError`(要求した種類のカメラ / マイクが存在しない)。
|
|
162
170
|
- `not-readable`(phase `start`)——`NotReadableError`(デバイス占有・ハードウェア障害)。
|
|
163
171
|
- `invalid-argument`(phase `start`)——`OverconstrainedError` / `NotSupportedError`(制約・mimeType が満たせない)。
|
package/README.md
CHANGED
|
@@ -50,16 +50,16 @@ Acquires a camera stream and renders a preview. Acquisition is **explicit** —
|
|
|
50
50
|
|
|
51
51
|
**Commands:** `start()`, `stop()`, `switchCamera()` (toggle front/back).
|
|
52
52
|
|
|
53
|
-
**Bindable values:** `active` (a stream is live), `permission` / `audioPermission` (`prompt`/`granted`/`denied`/`unsupported`), `deviceId` (the device the stream uses, published with `wcs-camera:device-changed`; reading the property gives the same value once one is known, and the requested `device-id` until then — the request itself stays in the attribute), `devices
|
|
53
|
+
**Bindable values:** `active` (a stream is live), `permission` / `audioPermission` (`prompt`/`granted`/`denied`/`unsupported`; since 4.5 `unsupported` also on a page that cannot use the camera / microphone — not a secure context, or turned off by a Permissions-Policy — where `start()` does not ask the browser and `errorInfo` is `capability-missing` with the reason), `deviceId` (the device the stream uses, published with `wcs-camera:device-changed`; reading the property gives the same value once one is known, and the requested `device-id` until then — the request itself stays in the attribute), `devices` (the video inputs, `[{ deviceId, label, groupId, kind: "videoinput" }]`; labels are empty until a grant), `error`, `errorInfo` (`WcsIoErrorInfo | null` — a serializable failure taxonomy derived from `error`, published via `wcs-camera:error-info-changed`; see [Notes & gotchas](#notes--gotchas) below).
|
|
54
54
|
|
|
55
55
|
**Events (event-token):** `streamReady` (`wcs-camera:stream-ready`, detail = the live `MediaStream`), `error`, `ended` (a track was revoked by the OS). The `streamReady` "property" exists for event-token wiring only — never bind it as a value.
|
|
56
56
|
|
|
57
57
|
### Lifecycle
|
|
58
58
|
|
|
59
|
-
- On `disconnectedCallback` every track is stopped (`track.stop()`), clearing the hardware indicator. Leaking a stream is the one failure mode unique to this node.
|
|
59
|
+
- On `disconnectedCallback` every track is stopped (`track.stop()`), clearing the hardware indicator. Leaking a stream is the one failure mode unique to this node. Since 4.5, `active` goes back to `false` with its `wcs-camera:active-changed` event. A route the element leaves keeps its nodes, bindings included, for the next entry (router README, "Where route content lives"), so up to 4.4 the page kept showing a live camera after the way back, and a Start button disabled while `active` stayed disabled.
|
|
60
60
|
- Moving the element in the DOM (remove → re-append) runs `disconnectedCallback` (dispose, stop tracks) then `connectedCallback` (observe again). With `autostart` it re-acquires on reconnect (and may re-prompt). To keep a stream across a move, avoid `autostart` and re-`start()` yourself, or don't detach the element.
|
|
61
61
|
- A constraints change (`device-id`, `facing-mode`, `switchCamera()`) **re-acquires** (stop → new `getUserMedia`), guarded by a generation counter so a superseded acquire cannot leave an orphan stream live.
|
|
62
|
-
- While the page is hidden the stream is suspended and re-acquired on return — unless `keep-alive` is set. Bind `keepAlive: recording` to keep the camera alive while recording.
|
|
62
|
+
- While the page is hidden the stream is suspended and re-acquired on return — unless `keep-alive` is set. Bind `keepAlive: recording` to keep the camera alive while recording: a recording of a suspended stream ends there, with what it has. Since 4.5 the camera warns when it suspends a stream a `<wcs-recorder>` is recording (`[wcs/camera-keep-alive-missing]`, once per element).
|
|
63
63
|
|
|
64
64
|
## `<wcs-recorder>`
|
|
65
65
|
|
|
@@ -78,8 +78,14 @@ Records a **borrowed** stream received via `attachStream` (the direct channel fr
|
|
|
78
78
|
> **`duration` is finalized at stop/pause, not live.** There is no internal ticking timer: `duration` stays `0` from `start()` until the first `pause()` or `stop()`. For a live elapsed counter while recording, drive your own client-side timer off the `recording` flag.
|
|
79
79
|
|
|
80
80
|
> **`mimeType` has two sides — request vs. resolved.** The **input** is the `mime-type` *attribute* (what you ask the recorder to use). The **output** is the `mimeType` *bindable value* (what the browser actually picked, published via `wcs-recorder:mimetype-changed`). They share a base name by design but are distinct surfaces: bind the attribute to set the request (`mime-type` attribute / element setter), and bind the value property to read the resolved type. Don't expect reading `mimeType` to echo back what you wrote — it reflects the recording, not the request.
|
|
81
|
+
>
|
|
82
|
+
> Because the declaration lists `mimeType` both as an input and as an output, `@wcstack/state` treats `mimeType: path` as two-way, and a two-way binding starts by writing the state's value to the element: a `null` seed removes `mime-type`, and the browser picks the type. To read the resolved type without touching the request, write `mimeType#init=element: path`.
|
|
83
|
+
>
|
|
84
|
+
> Chromium settles the type only when recording starts (a `MediaRecorder` built without one reports `""` until then). Up to 4.4, without `mime-type`, the clip's `Blob` had no type and `mimeType` stayed `""`, so a server that checks the type, or `<wcs-upload accept="video/*">`, refused the clip. Since 4.5 `mimeType` is the type recording started with (or the type of the first chunk), and the `Blob` carries it.
|
|
81
85
|
|
|
82
|
-
The assembled `Blob` is structured-clone friendly, so it *is* a value and may flow through state — for example `new File([blob], "clip.webm")` into [`@wcstack/upload`](../upload/). The object URL is managed: the previous one is revoked before a new clip **and on `disconnectedCallback` (dispose)**.
|
|
86
|
+
The assembled `Blob` is structured-clone friendly, so it *is* a value and may flow through state — for example `new File([blob], "clip.webm", { type: blob.type })` into [`@wcstack/upload`](../upload/). The object URL is managed: the previous one is revoked before a new clip **and on `disconnectedCallback` (dispose)**.
|
|
87
|
+
|
|
88
|
+
**Leaving the document while recording** (a route left, an `if:` branch closed): up to 4.4 the recording was thrown away, with no `recorded` event, and `recording` stayed `true` in state. Since 4.5 the recording stops as it does when the stream's tracks end: `recording` / `paused` go back to `false` at once, with their events, and the clip arrives as `wcs-recorder:recorded` when the browser delivers it (a moment later). Its `objectURL` stays valid until the next clip or the next disconnect.
|
|
83
89
|
|
|
84
90
|
> **`objectURL` lifetime is bound to the recorder.** Because dispose revokes the last object URL — **and a new recording revokes the previous clip's URL before minting the next** — any `<video src>` / `<wcs-upload>` still pointing at an old URL breaks once the `<wcs-recorder>` is removed or the next clip completes. Always follow the latest `objectURL` / `recorded` value; never pin a stale one. If you hand the URL to a longer-lived consumer, either keep the recorder mounted for as long as the URL is in use, or build your own URL from the `Blob` (`URL.createObjectURL(blob)`) and own its revoke. The structured-clone-friendly **`blob`** has no such coupling — prefer flowing the `Blob` through state and minting URLs at the point of use.
|
|
85
91
|
|
|
@@ -153,12 +159,14 @@ The structural Core surface is normative across wcstack IO nodes ([async-io-node
|
|
|
153
159
|
## Notes & gotchas
|
|
154
160
|
|
|
155
161
|
- **Secure context (https) required.** `getUserMedia` is unavailable on `file://` / plain `http://`.
|
|
162
|
+
- **Content-Security-Policy.** The preview's own styles are a constructed stylesheet since 4.5, which `style-src` does not govern, so a policy without `'unsafe-inline'` keeps the preview's layout. Up to 4.4 they were a `<style>` in the shadow root, which such a policy blocks (the preview `<video>` rendered at its own size): style it from your stylesheet with `wcs-camera::part(video) { display: block; width: 100%; height: 100% }`.
|
|
163
|
+
- **`error` / `errorInfo` describe the last attempt.** A permission granted later (in the site settings) does not clear them: `permission` follows the grant at once, while `errorInfo` keeps `not-allowed` until the next `start()` succeeds. Decide "blocked" from `permission`, and offer Start again when the last error was `not-allowed` and `permission` is `granted`.
|
|
156
164
|
- **The camera indicator = a leak detector.** If it stays on after you are done, a track was not stopped.
|
|
157
165
|
- **User gesture.** Some browsers require `getUserMedia` to be triggered by a user action; firing it from a timer may silently fail (surfaced via `error`, never thrown).
|
|
158
166
|
- **Errors are classified, never thrown:** `NotAllowedError` (denied), `NotFoundError` (no device), `NotReadableError` (in use by another app), `OverconstrainedError`.
|
|
159
167
|
- **`errorInfo` — additive failure taxonomy.** Alongside `error`, both `<wcs-camera>` and `<wcs-recorder>` expose an **additive** bindable output `errorInfo` (`WcsIoErrorInfo` = a stable `code` / `phase` / `recoverable` / `message`), derived from the same failure and published via `wcs-camera:error-info-changed` / `wcs-recorder:error-info-changed`. The `error` shape is unchanged; `errorInfo` transitions exactly when `error` does (cleared to `null` on success). Both elements share one code set (`WCS_MEDIA_ERROR_CODE`, defined in `core/mediaCapabilities.ts`):
|
|
160
|
-
- `capability-missing` (phase `probe`) — `getUserMedia` / `MediaRecorder` unavailable, including non-secure context.
|
|
161
|
-
- `not-allowed` (phase `start`) — `NotAllowedError` / `SecurityError` (permission denied
|
|
168
|
+
- `capability-missing` (phase `probe`) — `getUserMedia` / `MediaRecorder` unavailable, including non-secure context; since 4.5 also a camera or microphone a Permissions-Policy turns off, found before the browser is asked.
|
|
169
|
+
- `not-allowed` (phase `start`) — `NotAllowedError` / `SecurityError` (permission denied; up to 4.4 also a feature-policy block).
|
|
162
170
|
- `not-found` (phase `start`) — `NotFoundError` (no camera / mic of the requested kind).
|
|
163
171
|
- `not-readable` (phase `start`) — `NotReadableError` (device busy or hardware fault).
|
|
164
172
|
- `invalid-argument` (phase `start`) — `OverconstrainedError` / `NotSupportedError` (constraints or mimeType unsatisfiable).
|
package/dist/auto.d.ts
ADDED
package/dist/auto.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}};function t(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function i(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}const s={name:"unsupported",message:"getUserMedia is not available (requires a secure context)."};function r(e){if(e)for(const t of e.getTracks())t.stop()}async function n(){if(!t()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class a{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}const o="capability-missing",c="not-allowed",d="not-found",h="not-readable",u="invalid-argument",_="invalid-state",m="aborted",l="media-error";function g(e){const{name:t,message:i}=e;return"unsupported"===t?{code:o,phase:"probe",recoverable:!1,message:i}:"NotAllowedError"===t||"SecurityError"===t?{code:c,phase:"start",recoverable:!1,message:i}:"NotFoundError"===t?{code:d,phase:"start",recoverable:!1,message:i}:"NotReadableError"===t?{code:h,phase:"start",recoverable:!1,message:i}:"OverconstrainedError"===t||"NotSupportedError"===t?{code:u,phase:"start",recoverable:!1,message:i}:"NoStreamError"===t?{code:_,phase:"start",recoverable:!1,message:i}:"AbortError"===t?{code:m,phase:"execute",recoverable:!0,message:i}:{code:l,phase:"execute",recoverable:!1,message:i}}class v extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed",semantics:"state"},{name:"permission",event:"wcs-camera:permission-changed",semantics:"state"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed",semantics:"state"},{name:"deviceId",event:"wcs-camera:device-changed",semantics:"state"},{name:"devices",event:"wcs-camera:devices-changed",semantics:"state"},{name:"error",event:"wcs-camera:error",semantics:"state"},{name:"errorInfo",event:"wcs-camera:error-info-changed",semantics:"state"},{name:"streamReady",event:"wcs-camera:stream-ready",semantics:"handle",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",semantics:"event",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_errorInfo=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:g(e)),this._dispatch("wcs-camera:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-camera:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release(!1)}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){this._gen++,this._stream&&this._release(!1)}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(!0),t()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!t())return this._setPermission("unsupported"),Promise.resolve();this._camWatcher=new a("camera",e=>this._setPermission(e));const e=[this._camWatcher.observe()];return this._constraints.audio&&(this._micWatcher=new a("microphone",e=>this._setAudioPermission(e)),e.push(this._micWatcher.observe())),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{n().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&t()?(this._micWatcher=new a("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe()):(!this._constraints.audio&&this._micWatcher&&(this._micWatcher.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_restart(){this._release(!1),this._acquire()}async _acquire(){const e=++this._gen,a=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:o,error:c}=await async function(e){if(!t())return{error:s};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:i(e)}}}(a);if(e!==this._gen)return void r(o??null);if(c)return this._setError(c),"NotAllowedError"===c.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===c.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const d=o;this._stream=d;for(const e of d.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&d.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(d),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",d);const h=await n();e===this._gen&&this._setDevices(h)}_release(e){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);r(this._stream),this._stream=null,e?this._active=!1:this._setActive(!1)}}_onTrackEnded=()=>{this._release(!1),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}function p(e,t){let i=Object.getPrototypeOf(e);for(;null!==i;){const e=Object.getOwnPropertyDescriptor(i,t);if(void 0!==e)return"function"==typeof e.get||"function"==typeof e.set;i=Object.getPrototypeOf(i)}return!1}function b(e){const t=e.constructor?.wcBindable,i=t?.inputs;if(void 0!==i)for(const t of i){const i=t.name;if(!Object.prototype.hasOwnProperty.call(e,i))continue;if(!p(e,i))continue;const s=e,r=s[i];delete s[i],s[i]=r}}const f=new WeakMap;function y(e,t){let i=f.get(e);return void 0===i&&(i=new Map,f.set(e,i)),i.has(t)||i.set(t,e.getAttribute(t)),i.get(t)}function w(e,t,i){null===i?e.removeAttribute(t):e.setAttribute(t,i)}function P(e,t,i){const s=y(e,t);w(e,t,void 0===i?s:null===i?null:String(i))}function E(e,t,i){const s=y(e,t);w(e,t,void 0===i?s:i?"":null)}class A extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...v.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:v.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connected=!1;_batchingAttrs=!1;_internals=null;constructor(){super(),this._core=new v(this);const e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent=":host{display:inline-block}video{display:block;width:100%;height:100%}",this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(t,this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged),this._internals=this._initInternals(),this._wireStates({"wcs-camera:active-changed":e=>({active:!0===e}),"wcs-camera:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get audio(){return this.hasAttribute("audio")}set audio(e){E(this,"audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){P(this,"facing-mode",e)}get deviceId(){return this._core.deviceId??this.getAttribute("device-id")??""}set deviceId(e){P(this,"device-id",e)}get width(){return this._numberAttr("width")}set width(e){P(this,"width",e)}get height(){return this._numberAttr("height")}set height(e){P(this,"height",e)}get autostart(){return this.hasAttribute("autostart")}set autostart(e){E(this,"autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){E(this,"keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start()}stop(){this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode},t=this.getAttribute("device-id");return t&&(e.deviceId=t),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};connectedCallback(){b(this),this._connected=!0,this._connectedCallbackPromise=this._core.observe(this._constraints()),this.autostart&&this._core.start(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||(this._core.observe(this._constraints()),this._core.active&&this._core.start()))}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class I extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed",semantics:"state"},{name:"paused",event:"wcs-recorder:paused-changed",semantics:"state"},{name:"duration",event:"wcs-recorder:duration-changed",semantics:"state"},{name:"mimeType",event:"wcs-recorder:mimetype-changed",semantics:"state"},{name:"blob",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error",semantics:"state"},{name:"errorInfo",event:"wcs-recorder:error-info-changed",semantics:"state"},{name:"recorded",event:"wcs-recorder:recorded",semantics:"event",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",semantics:"event",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_errorInfo=null;_recorder=null;_stream=null;_chunks=[];_timeslice=!1;_startTime=0;_gen=0;_ready=Promise.resolve();_toldNotReady=!1;constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:g(e)),this._dispatch("wcs-recorder:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-recorder:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."}),void this._warnNotReady();if(this._recording)return;const t={};let s;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{s=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(i(e))}const r=++this._gen;this._chunks=[],this._timeslice="number"==typeof e.timeslice&&e.timeslice>0,s.ondataavailable=e=>{r===this._gen&&e.data&&e.data.size>0&&(this._chunks.push(e.data),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data))},s.onstop=()=>{r===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(),this._setPaused(!1),this._setRecording(!1))},s.onerror=e=>{if(r!==this._gen)return;const t=e.error;this._setError(i(t??{name:"RecorderError"}))},s.onpause=()=>{r===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},s.onresume=()=>{r===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=s,this._setError(null),this._setMimeType(s.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),s.start(this._timeslice?e.timeslice:void 0),this._setRecording(!0)}_warnNotReady(){this._toldNotReady||(this._toldNotReady=!0,console.warn("[@wcstack/camera] [wcs/command-not-ready] start() was refused: no stream is attached, so nothing is recorded. It goes to `error` too. Wire a camera's stream to the recorder first. (Said once per element.)",this._target))}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}observe(){return this._ready}dispose(){if(this._gen++,this._recorder&&"inactive"!==this._recorder.state)try{this._recorder.stop()}catch{}this._recorder=null,this._revokeUrl(),this._stream=null,this._recording=!1,this._paused=!1}_assembleBlob(){const e=new Blob(this._chunks,this._mimeType?{type:this._mimeType}:void 0);this._chunks=[],this._revokeUrl();const t=this._createUrl(e);this._blob=e,this._objectURL=t;const i={blob:e,objectURL:t,mimeType:this._mimeType,duration:this._duration};this._dispatch("wcs-recorder:recorded",i)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class S extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...I.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:I.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new I(this),this._internals=this._initInternals(),this._wireStates({"wcs-recorder:recording-changed":e=>({recording:!0===e}),"wcs-recorder:paused-changed":e=>({paused:!0===e}),"wcs-recorder:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get mimeType(){return this._core.mimeType}set mimeType(e){P(this,"mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){P(this,"timeslice",e)}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){P(this,"audio-bits",e)}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){P(this,"video-bits",e)}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){b(this),this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var k;!function(t=customElements){t.get(e.tagNames.camera)||t.define(e.tagNames.camera,A),t.get(e.tagNames.recorder)||t.define(e.tagNames.recorder,S)}(k);
|
|
1
|
+
const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}},t=new Set(["camera","microphone","geolocation","midi","clipboard-read","clipboard-write","screen-wake-lock","idle-detection","accelerometer","gyroscope","magnetometer","ambient-light-sensor","display-capture"]);function i(e){const i=globalThis;if(!1===i.isSecureContext)return"insecure-context";if(!t.has(e))return null;const s=i.document?.permissionsPolicy??i.document?.featurePolicy;if(void 0===s||"function"!=typeof s.allowsFeature)return null;try{return s.allowsFeature(e)?null:"policy-blocked"}catch{return null}}function s(e,t,i){return"insecure-context"===i?`${e} needs a secure context (https:, or http://localhost), and this page is not one.`:`${e} is turned off for this page by a Permissions-Policy: the server's Permissions-Policy header, or an <iframe> without allow="${t}". The browser's site settings cannot change that.`}function r(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function n(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}function a(){const e=i("camera");return{name:"unsupported",message:"insecure-context"===e?s("getUserMedia","camera",e):"getUserMedia is not available in this browser."}}function o(e){if(e)for(const t of e.getTracks())t.stop()}async function c(){if(!r()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class d{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}const h=new WeakMap;function u(e){const t=(h.get(e)??0)-1;t>0?h.set(e,t):h.delete(e)}const m="capability-missing",l="not-allowed",_="not-found",g="not-readable",p="invalid-argument",v="invalid-state",b="aborted",f="media-error";function w(e){const{name:t,message:i}=e;return"unsupported"===t?{code:m,phase:"probe",recoverable:!1,message:i}:"NotAllowedError"===t||"SecurityError"===t?{code:l,phase:"start",recoverable:!1,message:i}:"NotFoundError"===t?{code:_,phase:"start",recoverable:!1,message:i}:"NotReadableError"===t?{code:g,phase:"start",recoverable:!1,message:i}:"OverconstrainedError"===t||"NotSupportedError"===t?{code:p,phase:"start",recoverable:!1,message:i}:"NoStreamError"===t?{code:v,phase:"start",recoverable:!1,message:i}:"AbortError"===t?{code:b,phase:"execute",recoverable:!0,message:i}:{code:f,phase:"execute",recoverable:!1,message:i}}class y extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed",semantics:"state"},{name:"permission",event:"wcs-camera:permission-changed",semantics:"state"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed",semantics:"state"},{name:"deviceId",event:"wcs-camera:device-changed",semantics:"state"},{name:"devices",event:"wcs-camera:devices-changed",semantics:"state"},{name:"error",event:"wcs-camera:error",semantics:"state"},{name:"errorInfo",event:"wcs-camera:error-info-changed",semantics:"state"},{name:"streamReady",event:"wcs-camera:stream-ready",semantics:"handle",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",semantics:"event",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_errorInfo=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();_toldKeepAlive=!1;constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-camera:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-camera:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release()}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){var e;this._gen++,this._stream&&(e=this._stream,h.has(e)&&this._warnKeepAlive(),this._release())}_warnKeepAlive(){this._toldKeepAlive||(this._toldKeepAlive=!0,console.warn("[@wcstack/camera] [wcs/camera-keep-alive-missing] The page was hidden while this camera's stream was being recorded, so the camera stopped the stream and the recording ended with it. Keep the camera on while recording: bind keepAlive to the recording flag (keepAlive: <recording path>). (Said once per element.)",this._target))}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(),r()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!r())return this._setPermission("unsupported"),Promise.resolve();const e=[];return null!==i("camera")?this._setPermission("unsupported"):(this._camWatcher=new d("camera",e=>this._setPermission(e)),e.push(this._camWatcher.observe())),this._constraints.audio&&e.push(this._watchMicrophone()),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{c().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&r()?this._watchMicrophone():(this._constraints.audio||!this._micWatcher&&null===this._audioPermission||(this._micWatcher?.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_watchMicrophone(){return null!==i("microphone")?(this._setAudioPermission("unsupported"),Promise.resolve()):(this._micWatcher=new d("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe())}_restart(){this._release(),this._acquire()}async _acquire(){const e=++this._gen,t=r()?function(e){const t=i("camera");if(null!==t)return{device:"camera",error:{name:"unsupported",message:s("The camera","camera",t)}};const r=e?i("microphone"):null;return null!==r?{device:"microphone",error:{name:"unsupported",message:s("The microphone","microphone",r)}}:null}(!!this._constraints.audio):null;if(null!==t)return this._setError(t.error),"camera"===t.device?this._setPermission("unsupported"):this._setAudioPermission("unsupported"),this._desired=!1,void this._setActive(!1);const d=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:h,error:u}=await async function(e){if(!r())return{error:a()};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:n(e)}}}(d);if(e!==this._gen)return void o(h??null);if(u)return this._setError(u),"NotAllowedError"===u.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===u.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const m=h;this._stream=m;for(const e of m.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&m.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(m),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",m);const l=await c();e===this._gen&&this._setDevices(l)}_release(){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);o(this._stream),this._stream=null,this._setActive(!1)}}_onTrackEnded=()=>{this._release(),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}function P(e,t){let i=Object.getPrototypeOf(e);for(;null!==i;){const e=Object.getOwnPropertyDescriptor(i,t);if(void 0!==e)return"function"==typeof e.get||"function"==typeof e.set;i=Object.getPrototypeOf(i)}return!1}const S=new WeakSet;function A(e,t,i){if(S.has(e))return;S.add(e);const s=e.localName??"element";console.warn(`[wcs/input-as-attribute] <${s}> has the attribute ${t}="${i}": an object was written as an attribute, so the element did not receive it. A framework does that when the property is set before the element's definition loads (React 19), or when told to (Vue's .attr). Load the definition before rendering, or assign the property through a ref (Vue: .prop). (Said once per element.)`,e)}function E(e){const t=e.constructor?.wcBindable,i=t?.inputs;if(void 0===i)return;const s=e.getAttribute;for(const t of i){const i=t.name,r="function"==typeof s?s.call(e,i.toLowerCase()):null;if(null!==r&&r.startsWith("[object ")&&A(e,i,r),!Object.prototype.hasOwnProperty.call(e,i))continue;if(!P(e,i))continue;const n=e,a=n[i];delete n[i],n[i]=a}}const T=new WeakMap;function k(e,t){let i=T.get(e);return void 0===i&&(i=new Map,T.set(e,i)),i.has(t)||i.set(t,e.getAttribute(t)),i.get(t)}function I(e,t,i){null===i?e.removeAttribute(t):e.setAttribute(t,i)}function C(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:null===i?null:String(i))}function R(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:i?"":null)}const L=":host{display:inline-block}video{display:block;width:100%;height:100%}";let N;class M extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...y.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:y.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connected=!1;_batchingAttrs=!1;_internals=null;constructor(){super(),this._core=new y(this);const e=this.attachShadow({mode:"open"});!function(e){if(void 0===N)try{N=new CSSStyleSheet,N.replaceSync(L)}catch{N=null}if(null!==N&&Array.isArray(e.adoptedStyleSheets))return void(e.adoptedStyleSheets=[N]);const t=document.createElement("style");t.textContent=L,e.append(t)}(e),this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged),this._internals=this._initInternals(),this._wireStates({"wcs-camera:active-changed":e=>({active:!0===e}),"wcs-camera:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get audio(){return this.hasAttribute("audio")}set audio(e){R(this,"audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){C(this,"facing-mode",e)}get deviceId(){return this._core.deviceId??this.getAttribute("device-id")??""}set deviceId(e){C(this,"device-id",e)}get width(){return this._numberAttr("width")}set width(e){C(this,"width",e)}get height(){return this._numberAttr("height")}set height(e){C(this,"height",e)}get autostart(){return this.hasAttribute("autostart")}set autostart(e){R(this,"autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){R(this,"keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start()}stop(){this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode},t=this.getAttribute("device-id");return t&&(e.deviceId=t),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};connectedCallback(){E(this),this._connected=!0,this._connectedCallbackPromise=this._core.observe(this._constraints()),this.autostart&&this._core.start(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||(this._core.observe(this._constraints()),this._core.active&&this._core.start()))}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class B extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed",semantics:"state"},{name:"paused",event:"wcs-recorder:paused-changed",semantics:"state"},{name:"duration",event:"wcs-recorder:duration-changed",semantics:"state"},{name:"mimeType",event:"wcs-recorder:mimetype-changed",semantics:"state"},{name:"blob",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error",semantics:"state"},{name:"errorInfo",event:"wcs-recorder:error-info-changed",semantics:"state"},{name:"recorded",event:"wcs-recorder:recorded",semantics:"event",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",semantics:"event",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_errorInfo=null;_recorder=null;_stream=null;_recordingStream=null;_timeslice=!1;_startTime=0;_gen=0;_closing=null;_ready=Promise.resolve();_toldNotReady=!1;constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-recorder:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-recorder:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."}),void this._warnNotReady();if(this._recording)return;const t={};let i;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{i=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(n(e))}const s=++this._gen,r=[],a=this._stream;this._timeslice="number"==typeof e.timeslice&&e.timeslice>0;const o=()=>null!==this._closing&&this._closing.gen===s;i.ondataavailable=e=>{if((s===this._gen||o())&&e.data&&e.data.size>0){if(r.push(e.data),s!==this._gen)return;!this._mimeType&&e.data.type&&this._setMimeType(e.data.type),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data)}},i.onstart=()=>{s===this._gen&&i.mimeType&&this._setMimeType(i.mimeType)},i.onstop=()=>{if(u(a),this._recordingStream===a&&(this._recordingStream=null),o()){const{mimeType:e,duration:t}=this._closing;return this._closing=null,void this._assembleBlob(r,e,t)}s===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(r,this._mimeType,this._duration),this._setPaused(!1),this._setRecording(!1))},i.onerror=e=>{if(s!==this._gen)return;const t=e.error;this._setError(n(t??{name:"RecorderError"}))},i.onpause=()=>{s===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},i.onresume=()=>{s===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=i,this._setError(null),this._setMimeType(i.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),i.start(this._timeslice?e.timeslice:void 0),function(e){h.set(e,(h.get(e)??0)+1)}(a),this._recordingStream=a,this._setRecording(!0)}_warnNotReady(){this._toldNotReady||(this._toldNotReady=!0,console.warn("[@wcstack/camera] [wcs/command-not-ready] start() was refused: no stream is attached, so nothing is recorded. It goes to `error` too. Wire a camera's stream to the recorder first. (Said once per element.)",this._target))}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}observe(){return this._ready}dispose(){const e=this._recorder,t=null!==e&&"inactive"!==e.state;if(t&&(this._paused||this._setDuration(this._elapsed()),this._closing={gen:this._gen,mimeType:this._mimeType,duration:this._duration}),this._gen++,this._recorder=null,this._revokeUrl(),this._stream=null,this._setPaused(!1),this._setRecording(!1),t)try{e.stop()}catch{this._closing=null,this._recordingStream&&u(this._recordingStream),this._recordingStream=null}}_assembleBlob(e,t,i){const s=t||e[0]?.type||"",r=new Blob(e,s?{type:s}:void 0);this._revokeUrl();const n=this._createUrl(r);this._blob=r,this._objectURL=n;const a={blob:r,objectURL:n,mimeType:s,duration:i};this._dispatch("wcs-recorder:recorded",a)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class U extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...B.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:B.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new B(this),this._internals=this._initInternals(),this._wireStates({"wcs-recorder:recording-changed":e=>({recording:!0===e}),"wcs-recorder:paused-changed":e=>({paused:!0===e}),"wcs-recorder:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get mimeType(){return this._core.mimeType}set mimeType(e){C(this,"mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){C(this,"timeslice",e)}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){C(this,"audio-bits",e)}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){C(this,"video-bits",e)}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){E(this),this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var j;!function(t=customElements){t.get(e.tagNames.camera)||t.define(e.tagNames.camera,M),t.get(e.tagNames.recorder)||t.define(e.tagNames.recorder,U)}(j);
|
|
2
2
|
//# sourceMappingURL=auto.min.js.map
|