@fusion.dev/helix-headless 0.56.1 → 0.57.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/custom-elements.json +0 -558
- package/dist/video/root/keys.js +1 -1
- package/dist/video/root/video-root.js +1 -7
- package/package.json +5 -5
- package/dist/video/root/captions.d.ts +0 -85
- package/dist/video/root/captions.js +0 -45
- package/dist/video/root/fullscreen.d.ts +0 -28
- package/dist/video/root/fullscreen.js +0 -28
- package/dist/video/root/idle.d.ts +0 -34
- package/dist/video/root/idle.js +0 -24
- package/dist/video/root/media-copy.d.ts +0 -25
- package/dist/video/root/media-copy.js +0 -18
- package/dist/video/root/media-session.d.ts +0 -54
- package/dist/video/root/media-session.js +0 -49
- package/dist/video/root/remote-playback.d.ts +0 -34
- package/dist/video/root/remote-playback.js +0 -38
package/custom-elements.json
CHANGED
|
@@ -106746,210 +106746,6 @@
|
|
|
106746
106746
|
}
|
|
106747
106747
|
]
|
|
106748
106748
|
},
|
|
106749
|
-
{
|
|
106750
|
-
"kind": "javascript-module",
|
|
106751
|
-
"path": "src/video/root/captions.ts",
|
|
106752
|
-
"declarations": [
|
|
106753
|
-
{
|
|
106754
|
-
"kind": "function",
|
|
106755
|
-
"name": "captionTracks",
|
|
106756
|
-
"return": {
|
|
106757
|
-
"type": {
|
|
106758
|
-
"text": "TextTrack[]"
|
|
106759
|
-
}
|
|
106760
|
-
},
|
|
106761
|
-
"parameters": [
|
|
106762
|
-
{
|
|
106763
|
-
"name": "video",
|
|
106764
|
-
"type": {
|
|
106765
|
-
"text": "HTMLVideoElement | null"
|
|
106766
|
-
}
|
|
106767
|
-
}
|
|
106768
|
-
],
|
|
106769
|
-
"description": "The tracks a reader could choose between, in the order they were written.\n\nFiltered by kind, because `textTracks` also carries `chapters`, `metadata`\nand `descriptions`, and a menu offering \"Chapters\" as a subtitle language\nwould be offering something that draws no words. Read off the video rather\nthan off the slot: a `<track>` added later is in one and not the other, and\nthe video is the one that has resolved them."
|
|
106770
|
-
},
|
|
106771
|
-
{
|
|
106772
|
-
"kind": "function",
|
|
106773
|
-
"name": "showCaption",
|
|
106774
|
-
"return": {
|
|
106775
|
-
"type": {
|
|
106776
|
-
"text": "void"
|
|
106777
|
-
}
|
|
106778
|
-
},
|
|
106779
|
-
"parameters": [
|
|
106780
|
-
{
|
|
106781
|
-
"name": "tracks",
|
|
106782
|
-
"type": {
|
|
106783
|
-
"text": "readonly TextTrack[]"
|
|
106784
|
-
}
|
|
106785
|
-
},
|
|
106786
|
-
{
|
|
106787
|
-
"name": "index",
|
|
106788
|
-
"type": {
|
|
106789
|
-
"text": "number"
|
|
106790
|
-
}
|
|
106791
|
-
}
|
|
106792
|
-
],
|
|
106793
|
-
"description": "Shows one track and disables every other, or shows none at `-1`.\n\nEvery other, rather than only the one that was on. Two tracks in `hidden`\nboth fire `cuechange`, so the rendered cue becomes whichever fired last and\na reader who switched language gets the two interleaved. Measured as two\nsubtitles alternating line by line.\n\n`hidden`, never `showing`. Hidden is the mode where a track still fires\n`cuechange` and still fills `activeCues`, and the browser draws nothing,\nwhich is exactly what is wanted when something else is doing the drawing."
|
|
106794
|
-
},
|
|
106795
|
-
{
|
|
106796
|
-
"kind": "function",
|
|
106797
|
-
"name": "adoptTrack",
|
|
106798
|
-
"return": {
|
|
106799
|
-
"type": {
|
|
106800
|
-
"text": "boolean"
|
|
106801
|
-
}
|
|
106802
|
-
},
|
|
106803
|
-
"parameters": [
|
|
106804
|
-
{
|
|
106805
|
-
"name": "track",
|
|
106806
|
-
"type": {
|
|
106807
|
-
"text": "TextTrack"
|
|
106808
|
-
}
|
|
106809
|
-
}
|
|
106810
|
-
],
|
|
106811
|
-
"description": "Whether a track the platform started showing is now ours to draw.\n\nA `<track default>` arrives `showing`, which is the browser drawing it.\nMoved to `hidden` so the cues keep coming and we draw them instead. What\ncomes back is whether captions are on, which is not always \"off\": a button\nthat began there reported the opposite of what was on screen."
|
|
106812
|
-
},
|
|
106813
|
-
{
|
|
106814
|
-
"kind": "function",
|
|
106815
|
-
"name": "captionsAmong",
|
|
106816
|
-
"return": {
|
|
106817
|
-
"type": {
|
|
106818
|
-
"text": "Caption[]"
|
|
106819
|
-
}
|
|
106820
|
-
},
|
|
106821
|
-
"parameters": [
|
|
106822
|
-
{
|
|
106823
|
-
"name": "media",
|
|
106824
|
-
"type": {
|
|
106825
|
-
"text": "readonly TrackCopy[]"
|
|
106826
|
-
}
|
|
106827
|
-
}
|
|
106828
|
-
],
|
|
106829
|
-
"description": "The caption tracks among the copies, before the render that draws them.\n\nBy the platform's own reading of `kind`, which is what makes a `<track>` a\ncaption at all: missing is `subtitles` and anything it does not know is\n`metadata`. Read from the video's tracks after the render instead, the count\narrived a render late and asked for one more. Choosing one still goes\nthrough the video's tracks, which hold them in the same order."
|
|
106830
|
-
},
|
|
106831
|
-
{
|
|
106832
|
-
"kind": "function",
|
|
106833
|
-
"name": "activeCue",
|
|
106834
|
-
"return": {
|
|
106835
|
-
"type": {
|
|
106836
|
-
"text": "DocumentFragment | undefined"
|
|
106837
|
-
}
|
|
106838
|
-
},
|
|
106839
|
-
"parameters": [
|
|
106840
|
-
{
|
|
106841
|
-
"name": "track",
|
|
106842
|
-
"type": {
|
|
106843
|
-
"text": "TextTrack | undefined"
|
|
106844
|
-
}
|
|
106845
|
-
}
|
|
106846
|
-
],
|
|
106847
|
-
"description": "The cue on screen now, as nodes, or nothing between cues.\n\nNodes rather than a string, because `VTTCue.text` is the cue's *source*:\nWebVTT carries markup, and a caption file in the wild is full of it. `<v\nHildy>` names who is speaking, `<b>` and `<i>` and `<u>` mark emphasis, and\n`<c.loud>` classes a line. Rendered as text every one of those arrives on\nscreen as visible angle brackets.\n\n`getCueAsHTML` is the platform parsing its own format, so nothing here has\nto know the grammar and no cue text is ever treated as HTML: what comes\nback is a fragment the browser built, not a string anybody interpolated."
|
|
106848
|
-
},
|
|
106849
|
-
{
|
|
106850
|
-
"kind": "class",
|
|
106851
|
-
"description": "Listens to one track's cues at a time, so the next one can take it off.\n\nTaken off whatever it was on before adding it here. `follow` runs from\n`loadedmetadata`, which fires again for every source a reader moves to, and\na consumer adding a `<track>` later gets another. Added without removing, a\nplaylist of six left six listeners on tracks that between them fire for\nevery cue in the video.",
|
|
106852
|
-
"name": "CueController",
|
|
106853
|
-
"members": [
|
|
106854
|
-
{
|
|
106855
|
-
"kind": "field",
|
|
106856
|
-
"name": "track",
|
|
106857
|
-
"type": {
|
|
106858
|
-
"text": "TextTrack | undefined"
|
|
106859
|
-
},
|
|
106860
|
-
"privacy": "private"
|
|
106861
|
-
},
|
|
106862
|
-
{
|
|
106863
|
-
"kind": "method",
|
|
106864
|
-
"name": "follow",
|
|
106865
|
-
"return": {
|
|
106866
|
-
"type": {
|
|
106867
|
-
"text": "void"
|
|
106868
|
-
}
|
|
106869
|
-
},
|
|
106870
|
-
"parameters": [
|
|
106871
|
-
{
|
|
106872
|
-
"name": "track",
|
|
106873
|
-
"type": {
|
|
106874
|
-
"text": "TextTrack"
|
|
106875
|
-
}
|
|
106876
|
-
}
|
|
106877
|
-
]
|
|
106878
|
-
},
|
|
106879
|
-
{
|
|
106880
|
-
"kind": "method",
|
|
106881
|
-
"name": "hostDisconnected",
|
|
106882
|
-
"return": {
|
|
106883
|
-
"type": {
|
|
106884
|
-
"text": "void"
|
|
106885
|
-
}
|
|
106886
|
-
}
|
|
106887
|
-
},
|
|
106888
|
-
{
|
|
106889
|
-
"kind": "method",
|
|
106890
|
-
"name": "stop",
|
|
106891
|
-
"privacy": "private",
|
|
106892
|
-
"return": {
|
|
106893
|
-
"type": {
|
|
106894
|
-
"text": "void"
|
|
106895
|
-
}
|
|
106896
|
-
}
|
|
106897
|
-
}
|
|
106898
|
-
],
|
|
106899
|
-
"helix": {}
|
|
106900
|
-
}
|
|
106901
|
-
],
|
|
106902
|
-
"exports": [
|
|
106903
|
-
{
|
|
106904
|
-
"kind": "js",
|
|
106905
|
-
"name": "captionTracks",
|
|
106906
|
-
"declaration": {
|
|
106907
|
-
"name": "captionTracks",
|
|
106908
|
-
"module": "src/video/root/captions.ts"
|
|
106909
|
-
}
|
|
106910
|
-
},
|
|
106911
|
-
{
|
|
106912
|
-
"kind": "js",
|
|
106913
|
-
"name": "showCaption",
|
|
106914
|
-
"declaration": {
|
|
106915
|
-
"name": "showCaption",
|
|
106916
|
-
"module": "src/video/root/captions.ts"
|
|
106917
|
-
}
|
|
106918
|
-
},
|
|
106919
|
-
{
|
|
106920
|
-
"kind": "js",
|
|
106921
|
-
"name": "adoptTrack",
|
|
106922
|
-
"declaration": {
|
|
106923
|
-
"name": "adoptTrack",
|
|
106924
|
-
"module": "src/video/root/captions.ts"
|
|
106925
|
-
}
|
|
106926
|
-
},
|
|
106927
|
-
{
|
|
106928
|
-
"kind": "js",
|
|
106929
|
-
"name": "captionsAmong",
|
|
106930
|
-
"declaration": {
|
|
106931
|
-
"name": "captionsAmong",
|
|
106932
|
-
"module": "src/video/root/captions.ts"
|
|
106933
|
-
}
|
|
106934
|
-
},
|
|
106935
|
-
{
|
|
106936
|
-
"kind": "js",
|
|
106937
|
-
"name": "activeCue",
|
|
106938
|
-
"declaration": {
|
|
106939
|
-
"name": "activeCue",
|
|
106940
|
-
"module": "src/video/root/captions.ts"
|
|
106941
|
-
}
|
|
106942
|
-
},
|
|
106943
|
-
{
|
|
106944
|
-
"kind": "js",
|
|
106945
|
-
"name": "CueController",
|
|
106946
|
-
"declaration": {
|
|
106947
|
-
"name": "CueController",
|
|
106948
|
-
"module": "src/video/root/captions.ts"
|
|
106949
|
-
}
|
|
106950
|
-
}
|
|
106951
|
-
]
|
|
106952
|
-
},
|
|
106953
106749
|
{
|
|
106954
106750
|
"kind": "javascript-module",
|
|
106955
106751
|
"path": "src/video/root/extra-controls.ts",
|
|
@@ -107086,117 +106882,6 @@
|
|
|
107086
106882
|
}
|
|
107087
106883
|
]
|
|
107088
106884
|
},
|
|
107089
|
-
{
|
|
107090
|
-
"kind": "javascript-module",
|
|
107091
|
-
"path": "src/video/root/fullscreen.ts",
|
|
107092
|
-
"declarations": [
|
|
107093
|
-
{
|
|
107094
|
-
"kind": "class",
|
|
107095
|
-
"description": "Into fullscreen and back, and honest about where the element is.\n\nThe state follows `fullscreenchange` rather than the request, so a refused\nrequest leaves a button that correctly reports \"not fullscreen\".",
|
|
107096
|
-
"name": "FullscreenController",
|
|
107097
|
-
"members": [
|
|
107098
|
-
{
|
|
107099
|
-
"kind": "method",
|
|
107100
|
-
"name": "toggle",
|
|
107101
|
-
"return": {
|
|
107102
|
-
"type": {
|
|
107103
|
-
"text": "Promise<void>"
|
|
107104
|
-
}
|
|
107105
|
-
},
|
|
107106
|
-
"description": "Into fullscreen and back, and quiet about being refused.\n\n`requestFullscreen` rejects rather than returning false, and the two\nrefusals a consumer will actually meet are a Permissions Policy that does\nnot grant `fullscreen` to the frame the player is in, and a call without\nuser activation. Both are `TypeError: Permissions check failed`, which is\nthe same message for two different causes.\n\nCaught rather than `void`ed, or a refusal becomes an unhandled rejection\nnaming neither the element nor the reason."
|
|
107107
|
-
},
|
|
107108
|
-
{
|
|
107109
|
-
"kind": "method",
|
|
107110
|
-
"name": "hostConnected",
|
|
107111
|
-
"return": {
|
|
107112
|
-
"type": {
|
|
107113
|
-
"text": "void"
|
|
107114
|
-
}
|
|
107115
|
-
}
|
|
107116
|
-
},
|
|
107117
|
-
{
|
|
107118
|
-
"kind": "method",
|
|
107119
|
-
"name": "hostDisconnected",
|
|
107120
|
-
"return": {
|
|
107121
|
-
"type": {
|
|
107122
|
-
"text": "void"
|
|
107123
|
-
}
|
|
107124
|
-
}
|
|
107125
|
-
},
|
|
107126
|
-
{
|
|
107127
|
-
"kind": "field",
|
|
107128
|
-
"name": "handleChange",
|
|
107129
|
-
"privacy": "private",
|
|
107130
|
-
"readonly": true
|
|
107131
|
-
}
|
|
107132
|
-
],
|
|
107133
|
-
"helix": {}
|
|
107134
|
-
}
|
|
107135
|
-
],
|
|
107136
|
-
"exports": [
|
|
107137
|
-
{
|
|
107138
|
-
"kind": "js",
|
|
107139
|
-
"name": "FullscreenController",
|
|
107140
|
-
"declaration": {
|
|
107141
|
-
"name": "FullscreenController",
|
|
107142
|
-
"module": "src/video/root/fullscreen.ts"
|
|
107143
|
-
}
|
|
107144
|
-
}
|
|
107145
|
-
]
|
|
107146
|
-
},
|
|
107147
|
-
{
|
|
107148
|
-
"kind": "javascript-module",
|
|
107149
|
-
"path": "src/video/root/idle.ts",
|
|
107150
|
-
"declarations": [
|
|
107151
|
-
{
|
|
107152
|
-
"kind": "class",
|
|
107153
|
-
"description": "How long the reader has been still, which decides when the bar goes.\n\nA wait rather than leaving with the pointer, or a reader moving across the\npicture to reach a control watches the bar vanish under them. The wait is\nonly ever about *idling*: whether a paused video keeps its bar is the\nelement's decision, and this only says when the reader stopped.\n\nThe delay is asked for on every restart rather than taken once, because it\nis a property the consumer can change while the video plays.",
|
|
107154
|
-
"name": "IdleController",
|
|
107155
|
-
"members": [
|
|
107156
|
-
{
|
|
107157
|
-
"kind": "field",
|
|
107158
|
-
"name": "timer",
|
|
107159
|
-
"type": {
|
|
107160
|
-
"text": "ReturnType<typeof setTimeout> | undefined"
|
|
107161
|
-
},
|
|
107162
|
-
"privacy": "private"
|
|
107163
|
-
},
|
|
107164
|
-
{
|
|
107165
|
-
"kind": "field",
|
|
107166
|
-
"name": "stir",
|
|
107167
|
-
"readonly": true,
|
|
107168
|
-
"description": "Restarts the wait, on anything that counts as the reader being here.\n\nPointer movement and focus both count, and both have to: a reader using the\nkeyboard never moves a pointer, and a bar that only answered the mouse\nwould leave them nothing to look at. Zero turns the wait off and the bar\ngoes immediately; a large number keeps it up for as long as anyone is\nlikely to look."
|
|
107169
|
-
},
|
|
107170
|
-
{
|
|
107171
|
-
"kind": "field",
|
|
107172
|
-
"name": "leave",
|
|
107173
|
-
"readonly": true,
|
|
107174
|
-
"description": "Leaving is the one case that does not wait: the reader has gone somewhere\nelse, and there is nothing to reach for."
|
|
107175
|
-
},
|
|
107176
|
-
{
|
|
107177
|
-
"kind": "method",
|
|
107178
|
-
"name": "hostDisconnected",
|
|
107179
|
-
"return": {
|
|
107180
|
-
"type": {
|
|
107181
|
-
"text": "void"
|
|
107182
|
-
}
|
|
107183
|
-
}
|
|
107184
|
-
}
|
|
107185
|
-
],
|
|
107186
|
-
"helix": {}
|
|
107187
|
-
}
|
|
107188
|
-
],
|
|
107189
|
-
"exports": [
|
|
107190
|
-
{
|
|
107191
|
-
"kind": "js",
|
|
107192
|
-
"name": "IdleController",
|
|
107193
|
-
"declaration": {
|
|
107194
|
-
"name": "IdleController",
|
|
107195
|
-
"module": "src/video/root/idle.ts"
|
|
107196
|
-
}
|
|
107197
|
-
}
|
|
107198
|
-
]
|
|
107199
|
-
},
|
|
107200
106885
|
{
|
|
107201
106886
|
"kind": "javascript-module",
|
|
107202
106887
|
"path": "src/video/root/keys.ts",
|
|
@@ -107243,249 +106928,6 @@
|
|
|
107243
106928
|
}
|
|
107244
106929
|
]
|
|
107245
106930
|
},
|
|
107246
|
-
{
|
|
107247
|
-
"kind": "javascript-module",
|
|
107248
|
-
"path": "src/video/root/media-copy.ts",
|
|
107249
|
-
"declarations": [
|
|
107250
|
-
{
|
|
107251
|
-
"kind": "function",
|
|
107252
|
-
"name": "copiesOf",
|
|
107253
|
-
"return": {
|
|
107254
|
-
"type": {
|
|
107255
|
-
"text": "MediaCopy[]"
|
|
107256
|
-
}
|
|
107257
|
-
},
|
|
107258
|
-
"parameters": [
|
|
107259
|
-
{
|
|
107260
|
-
"name": "elements",
|
|
107261
|
-
"type": {
|
|
107262
|
-
"text": "readonly Element[]"
|
|
107263
|
-
}
|
|
107264
|
-
}
|
|
107265
|
-
],
|
|
107266
|
-
"description": "The copies to make of what the default slot is given."
|
|
107267
|
-
},
|
|
107268
|
-
{
|
|
107269
|
-
"kind": "function",
|
|
107270
|
-
"name": "sameCopies",
|
|
107271
|
-
"return": {
|
|
107272
|
-
"type": {
|
|
107273
|
-
"text": "boolean"
|
|
107274
|
-
}
|
|
107275
|
-
},
|
|
107276
|
-
"parameters": [
|
|
107277
|
-
{
|
|
107278
|
-
"name": "one",
|
|
107279
|
-
"type": {
|
|
107280
|
-
"text": "readonly MediaCopy[]"
|
|
107281
|
-
}
|
|
107282
|
-
},
|
|
107283
|
-
{
|
|
107284
|
-
"name": "two",
|
|
107285
|
-
"type": {
|
|
107286
|
-
"text": "readonly MediaCopy[]"
|
|
107287
|
-
}
|
|
107288
|
-
}
|
|
107289
|
-
],
|
|
107290
|
-
"description": "Whether two reads describe the same media.\n\nThe same set again is not a change. A video reloads for every new list, and\nthe first `slotchange` reports the children the element has already read."
|
|
107291
|
-
}
|
|
107292
|
-
],
|
|
107293
|
-
"exports": [
|
|
107294
|
-
{
|
|
107295
|
-
"kind": "js",
|
|
107296
|
-
"name": "copiesOf",
|
|
107297
|
-
"declaration": {
|
|
107298
|
-
"name": "copiesOf",
|
|
107299
|
-
"module": "src/video/root/media-copy.ts"
|
|
107300
|
-
}
|
|
107301
|
-
},
|
|
107302
|
-
{
|
|
107303
|
-
"kind": "js",
|
|
107304
|
-
"name": "sameCopies",
|
|
107305
|
-
"declaration": {
|
|
107306
|
-
"name": "sameCopies",
|
|
107307
|
-
"module": "src/video/root/media-copy.ts"
|
|
107308
|
-
}
|
|
107309
|
-
}
|
|
107310
|
-
]
|
|
107311
|
-
},
|
|
107312
|
-
{
|
|
107313
|
-
"kind": "javascript-module",
|
|
107314
|
-
"path": "src/video/root/media-session.ts",
|
|
107315
|
-
"declarations": [
|
|
107316
|
-
{
|
|
107317
|
-
"kind": "variable",
|
|
107318
|
-
"name": "SKIP_SECONDS",
|
|
107319
|
-
"type": {
|
|
107320
|
-
"text": "number"
|
|
107321
|
-
},
|
|
107322
|
-
"default": "10",
|
|
107323
|
-
"description": "How far a skip control moves, in seconds.\n\nTen, which is the number every player that has one uses, and the number the\nplatform names in `seekbackward` and `seekforward` when it hands over a\ndefault. A different length here would be correct only on this player."
|
|
107324
|
-
},
|
|
107325
|
-
{
|
|
107326
|
-
"kind": "class",
|
|
107327
|
-
"description": "Holds the page's media session for one player, and tells the platform what\nis playing.\n\nThe handlers are what make a headset's play, pause and skip work at all.\nWithout them a platform falls back to guessing which element was meant,\nwhich is right often enough to hide the fault and wrong exactly when there\nare two.",
|
|
107328
|
-
"name": "MediaSessionController",
|
|
107329
|
-
"members": [
|
|
107330
|
-
{
|
|
107331
|
-
"kind": "method",
|
|
107332
|
-
"name": "claim",
|
|
107333
|
-
"return": {
|
|
107334
|
-
"type": {
|
|
107335
|
-
"text": "void"
|
|
107336
|
-
}
|
|
107337
|
-
},
|
|
107338
|
-
"parameters": [
|
|
107339
|
-
{
|
|
107340
|
-
"name": "metadata",
|
|
107341
|
-
"type": {
|
|
107342
|
-
"text": "{ artwork: string; title: string }"
|
|
107343
|
-
}
|
|
107344
|
-
},
|
|
107345
|
-
{
|
|
107346
|
-
"name": "handlers",
|
|
107347
|
-
"type": {
|
|
107348
|
-
"text": "SessionHandlers"
|
|
107349
|
-
}
|
|
107350
|
-
}
|
|
107351
|
-
],
|
|
107352
|
-
"description": "Takes the session.\n\nOn play rather than on render, which is the ownership rule: a page with two\nplayers gives the session to whichever one the reader started, so a\nheadset's buttons land on what they are listening to rather than on\nwhichever came first in the markup."
|
|
107353
|
-
},
|
|
107354
|
-
{
|
|
107355
|
-
"kind": "method",
|
|
107356
|
-
"name": "report",
|
|
107357
|
-
"return": {
|
|
107358
|
-
"type": {
|
|
107359
|
-
"text": "void"
|
|
107360
|
-
}
|
|
107361
|
-
},
|
|
107362
|
-
"parameters": [
|
|
107363
|
-
{
|
|
107364
|
-
"name": "playing",
|
|
107365
|
-
"type": {
|
|
107366
|
-
"text": "boolean"
|
|
107367
|
-
}
|
|
107368
|
-
}
|
|
107369
|
-
],
|
|
107370
|
-
"description": "Tells the platform whether the owner is playing, and nothing if it is not the owner."
|
|
107371
|
-
},
|
|
107372
|
-
{
|
|
107373
|
-
"kind": "method",
|
|
107374
|
-
"name": "hostDisconnected",
|
|
107375
|
-
"return": {
|
|
107376
|
-
"type": {
|
|
107377
|
-
"text": "void"
|
|
107378
|
-
}
|
|
107379
|
-
},
|
|
107380
|
-
"description": "The page's one media session goes back to nobody, but only if this\nelement is the one holding it."
|
|
107381
|
-
}
|
|
107382
|
-
],
|
|
107383
|
-
"helix": {}
|
|
107384
|
-
}
|
|
107385
|
-
],
|
|
107386
|
-
"exports": [
|
|
107387
|
-
{
|
|
107388
|
-
"kind": "js",
|
|
107389
|
-
"name": "SKIP_SECONDS",
|
|
107390
|
-
"declaration": {
|
|
107391
|
-
"name": "SKIP_SECONDS",
|
|
107392
|
-
"module": "src/video/root/media-session.ts"
|
|
107393
|
-
}
|
|
107394
|
-
},
|
|
107395
|
-
{
|
|
107396
|
-
"kind": "js",
|
|
107397
|
-
"name": "MediaSessionController",
|
|
107398
|
-
"declaration": {
|
|
107399
|
-
"name": "MediaSessionController",
|
|
107400
|
-
"module": "src/video/root/media-session.ts"
|
|
107401
|
-
}
|
|
107402
|
-
}
|
|
107403
|
-
]
|
|
107404
|
-
},
|
|
107405
|
-
{
|
|
107406
|
-
"kind": "javascript-module",
|
|
107407
|
-
"path": "src/video/root/remote-playback.ts",
|
|
107408
|
-
"declarations": [
|
|
107409
|
-
{
|
|
107410
|
-
"kind": "class",
|
|
107411
|
-
"description": "Watches for something to play the video on, and stops on the way out.\n\nThe Remote Playback API is the standard one, and it is what Safari\nimplements for AirPlay and Chrome for Cast: one call covers both, where the\nolder `webkitShowPlaybackTargetPicker` covers only the first. Firefox has\nneither, and `remote` simply being absent is what turns the control off\nthere.\n\n`watchAvailability` returns an id that has to be canceled, or the callback\noutlives the element that owns it: a callback holding a removed element is a\nleak with a `@state` write on the end.",
|
|
107412
|
-
"name": "RemotePlaybackController",
|
|
107413
|
-
"members": [
|
|
107414
|
-
{
|
|
107415
|
-
"kind": "field",
|
|
107416
|
-
"name": "video",
|
|
107417
|
-
"type": {
|
|
107418
|
-
"text": "HTMLVideoElement | undefined"
|
|
107419
|
-
},
|
|
107420
|
-
"privacy": "private"
|
|
107421
|
-
},
|
|
107422
|
-
{
|
|
107423
|
-
"kind": "field",
|
|
107424
|
-
"name": "watch",
|
|
107425
|
-
"type": {
|
|
107426
|
-
"text": "number | undefined"
|
|
107427
|
-
},
|
|
107428
|
-
"privacy": "private",
|
|
107429
|
-
"description": "Cancels the availability watch, since it outlives a single render."
|
|
107430
|
-
},
|
|
107431
|
-
{
|
|
107432
|
-
"kind": "method",
|
|
107433
|
-
"name": "attach",
|
|
107434
|
-
"return": {
|
|
107435
|
-
"type": {
|
|
107436
|
-
"text": "Promise<void>"
|
|
107437
|
-
}
|
|
107438
|
-
},
|
|
107439
|
-
"parameters": [
|
|
107440
|
-
{
|
|
107441
|
-
"name": "video",
|
|
107442
|
-
"type": {
|
|
107443
|
-
"text": "HTMLVideoElement"
|
|
107444
|
-
}
|
|
107445
|
-
}
|
|
107446
|
-
],
|
|
107447
|
-
"description": "Starts watching the video, once there is one."
|
|
107448
|
-
},
|
|
107449
|
-
{
|
|
107450
|
-
"kind": "method",
|
|
107451
|
-
"name": "prompt",
|
|
107452
|
-
"return": {
|
|
107453
|
-
"type": {
|
|
107454
|
-
"text": "Promise<void>"
|
|
107455
|
-
}
|
|
107456
|
-
},
|
|
107457
|
-
"description": "Opens the platform's own device picker. It has no list of its own."
|
|
107458
|
-
},
|
|
107459
|
-
{
|
|
107460
|
-
"kind": "method",
|
|
107461
|
-
"name": "hostDisconnected",
|
|
107462
|
-
"return": {
|
|
107463
|
-
"type": {
|
|
107464
|
-
"text": "void"
|
|
107465
|
-
}
|
|
107466
|
-
}
|
|
107467
|
-
},
|
|
107468
|
-
{
|
|
107469
|
-
"kind": "field",
|
|
107470
|
-
"name": "handleState",
|
|
107471
|
-
"privacy": "private",
|
|
107472
|
-
"readonly": true
|
|
107473
|
-
}
|
|
107474
|
-
],
|
|
107475
|
-
"helix": {}
|
|
107476
|
-
}
|
|
107477
|
-
],
|
|
107478
|
-
"exports": [
|
|
107479
|
-
{
|
|
107480
|
-
"kind": "js",
|
|
107481
|
-
"name": "RemotePlaybackController",
|
|
107482
|
-
"declaration": {
|
|
107483
|
-
"name": "RemotePlaybackController",
|
|
107484
|
-
"module": "src/video/root/remote-playback.ts"
|
|
107485
|
-
}
|
|
107486
|
-
}
|
|
107487
|
-
]
|
|
107488
|
-
},
|
|
107489
106931
|
{
|
|
107490
106932
|
"kind": "javascript-module",
|
|
107491
106933
|
"path": "src/video/root/video-root.ts",
|
package/dist/video/root/keys.js
CHANGED
|
@@ -4,20 +4,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, drawIcon, HelixElement, HelixNextEvent, HelixPreviousEvent, onUnlessFalse, writtenList, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { activeCue, adoptTrack, CueController, captionsAmong, captionTracks, copiesOf, customElement, drawIcon, FullscreenController, HelixElement, HelixNextEvent, HelixPreviousEvent, IdleController, MediaSessionController, onUnlessFalse, RemotePlaybackController, SKIP_SECONDS, sameCopies, showCaption, writtenList, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { airplay, arrowClockwise, arrowCounterClockwise, closedCaptioning, cornersIn, cornersOut, dotsThreeVertical, download, gauge, pause, pictureInPicture, play, skipBack, skipForward, speakerHigh, speakerX, } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
12
12
|
import '../../menu/index.js';
|
|
13
|
-
import { activeCue, adoptTrack, CueController, captionsAmong, captionTracks, showCaption, } from './captions.js';
|
|
14
13
|
import { barControls, menuControls, ownFile } from './extra-controls.js';
|
|
15
|
-
import { FullscreenController } from './fullscreen.js';
|
|
16
|
-
import { IdleController } from './idle.js';
|
|
17
14
|
import { takeKey } from './keys.js';
|
|
18
|
-
import { copiesOf, sameCopies } from './media-copy.js';
|
|
19
|
-
import { MediaSessionController, SKIP_SECONDS } from './media-session.js';
|
|
20
|
-
import { RemotePlaybackController } from './remote-playback.js';
|
|
21
15
|
const clock = (seconds) => {
|
|
22
16
|
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
23
17
|
return '0:00';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusion.dev/helix-headless",
|
|
3
3
|
"description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.57.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ross Sheppard <ross.sheppard@fusion.dev>",
|
|
7
7
|
"repository": {
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
},
|
|
47
47
|
"bugs": "https://github.com/project-fusion/fusion/issues",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@fusion.dev/helix-core": "0.
|
|
50
|
-
"@fusion.dev/helix-icons": "0.
|
|
49
|
+
"@fusion.dev/helix-core": "0.57.0",
|
|
50
|
+
"@fusion.dev/helix-icons": "0.57.0",
|
|
51
51
|
"uqr": "^0.1.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
55
|
-
"@fusion.dev/helix-cem": "0.
|
|
56
|
-
"@fusion.dev/helix-testing": "0.
|
|
55
|
+
"@fusion.dev/helix-cem": "0.57.0",
|
|
56
|
+
"@fusion.dev/helix-testing": "0.57.0",
|
|
57
57
|
"@fusion.dev/scripts": "0.0.0-dev",
|
|
58
58
|
"@happy-dom/global-registrator": "^20.0.0",
|
|
59
59
|
"@lit-labs/ssr": "^4.1.0",
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
-
/** One caption track, named as the menu names it. */
|
|
3
|
-
export interface Caption {
|
|
4
|
-
label: string;
|
|
5
|
-
language: string;
|
|
6
|
-
}
|
|
7
|
-
/** A `<track>` as the consumer wrote it, reduced to what naming it needs. */
|
|
8
|
-
interface TrackCopy {
|
|
9
|
-
attrs: Record<string, string>;
|
|
10
|
-
tag: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* The tracks a reader could choose between, in the order they were written.
|
|
14
|
-
*
|
|
15
|
-
* Filtered by kind, because `textTracks` also carries `chapters`, `metadata`
|
|
16
|
-
* and `descriptions`, and a menu offering "Chapters" as a subtitle language
|
|
17
|
-
* would be offering something that draws no words. Read off the video rather
|
|
18
|
-
* than off the slot: a `<track>` added later is in one and not the other, and
|
|
19
|
-
* the video is the one that has resolved them.
|
|
20
|
-
*/
|
|
21
|
-
export declare const captionTracks: (video: HTMLVideoElement | null) => TextTrack[];
|
|
22
|
-
/**
|
|
23
|
-
* Shows one track and disables every other, or shows none at `-1`.
|
|
24
|
-
*
|
|
25
|
-
* Every other, rather than only the one that was on. Two tracks in `hidden`
|
|
26
|
-
* both fire `cuechange`, so the rendered cue becomes whichever fired last and
|
|
27
|
-
* a reader who switched language gets the two interleaved. Measured as two
|
|
28
|
-
* subtitles alternating line by line.
|
|
29
|
-
*
|
|
30
|
-
* `hidden`, never `showing`. Hidden is the mode where a track still fires
|
|
31
|
-
* `cuechange` and still fills `activeCues`, and the browser draws nothing,
|
|
32
|
-
* which is exactly what is wanted when something else is doing the drawing.
|
|
33
|
-
*/
|
|
34
|
-
export declare const showCaption: (tracks: readonly TextTrack[], index: number) => void;
|
|
35
|
-
/**
|
|
36
|
-
* Whether a track the platform started showing is now ours to draw.
|
|
37
|
-
*
|
|
38
|
-
* A `<track default>` arrives `showing`, which is the browser drawing it.
|
|
39
|
-
* Moved to `hidden` so the cues keep coming and we draw them instead. What
|
|
40
|
-
* comes back is whether captions are on, which is not always "off": a button
|
|
41
|
-
* that began there reported the opposite of what was on screen.
|
|
42
|
-
*/
|
|
43
|
-
export declare const adoptTrack: (track: TextTrack) => boolean;
|
|
44
|
-
/**
|
|
45
|
-
* The caption tracks among the copies, before the render that draws them.
|
|
46
|
-
*
|
|
47
|
-
* By the platform's own reading of `kind`, which is what makes a `<track>` a
|
|
48
|
-
* caption at all: missing is `subtitles` and anything it does not know is
|
|
49
|
-
* `metadata`. Read from the video's tracks after the render instead, the count
|
|
50
|
-
* arrived a render late and asked for one more. Choosing one still goes
|
|
51
|
-
* through the video's tracks, which hold them in the same order.
|
|
52
|
-
*/
|
|
53
|
-
export declare const captionsAmong: (media: readonly TrackCopy[]) => Caption[];
|
|
54
|
-
/**
|
|
55
|
-
* The cue on screen now, as nodes, or nothing between cues.
|
|
56
|
-
*
|
|
57
|
-
* Nodes rather than a string, because `VTTCue.text` is the cue's *source*:
|
|
58
|
-
* WebVTT carries markup, and a caption file in the wild is full of it. `<v
|
|
59
|
-
* Hildy>` names who is speaking, `<b>` and `<i>` and `<u>` mark emphasis, and
|
|
60
|
-
* `<c.loud>` classes a line. Rendered as text every one of those arrives on
|
|
61
|
-
* screen as visible angle brackets.
|
|
62
|
-
*
|
|
63
|
-
* `getCueAsHTML` is the platform parsing its own format, so nothing here has
|
|
64
|
-
* to know the grammar and no cue text is ever treated as HTML: what comes
|
|
65
|
-
* back is a fragment the browser built, not a string anybody interpolated.
|
|
66
|
-
*/
|
|
67
|
-
export declare const activeCue: (track: TextTrack | undefined) => DocumentFragment | undefined;
|
|
68
|
-
/**
|
|
69
|
-
* Listens to one track's cues at a time, so the next one can take it off.
|
|
70
|
-
*
|
|
71
|
-
* Taken off whatever it was on before adding it here. `follow` runs from
|
|
72
|
-
* `loadedmetadata`, which fires again for every source a reader moves to, and
|
|
73
|
-
* a consumer adding a `<track>` later gets another. Added without removing, a
|
|
74
|
-
* playlist of six left six listeners on tracks that between them fire for
|
|
75
|
-
* every cue in the video.
|
|
76
|
-
*/
|
|
77
|
-
export declare class CueController implements ReactiveController {
|
|
78
|
-
private readonly listener;
|
|
79
|
-
private track;
|
|
80
|
-
constructor(host: ReactiveControllerHost, listener: () => void);
|
|
81
|
-
follow(track: TextTrack): void;
|
|
82
|
-
hostDisconnected(): void;
|
|
83
|
-
private stop;
|
|
84
|
-
}
|
|
85
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export const captionTracks = (video) => {
|
|
2
|
-
const tracks = video?.textTracks;
|
|
3
|
-
if (!tracks) {
|
|
4
|
-
return [];
|
|
5
|
-
}
|
|
6
|
-
return [...tracks].filter((one) => one.kind === 'subtitles' || one.kind === 'captions');
|
|
7
|
-
};
|
|
8
|
-
export const showCaption = (tracks, index) => {
|
|
9
|
-
tracks.forEach((track, at) => {
|
|
10
|
-
track.mode = at === index ? 'hidden' : 'disabled';
|
|
11
|
-
});
|
|
12
|
-
};
|
|
13
|
-
export const adoptTrack = (track) => {
|
|
14
|
-
if (track.mode === 'showing') {
|
|
15
|
-
track.mode = 'hidden';
|
|
16
|
-
}
|
|
17
|
-
return track.mode === 'hidden';
|
|
18
|
-
};
|
|
19
|
-
export const captionsAmong = (media) => media
|
|
20
|
-
.filter((one) => one.tag === 'track' &&
|
|
21
|
-
(one.attrs.kind === undefined ||
|
|
22
|
-
['captions', 'subtitles'].includes(one.attrs.kind.toLowerCase())))
|
|
23
|
-
.map((one) => ({ label: one.attrs.label ?? '', language: one.attrs.srclang ?? '' }));
|
|
24
|
-
export const activeCue = (track) => {
|
|
25
|
-
const active = track?.activeCues?.[0];
|
|
26
|
-
return active instanceof VTTCue ? active.getCueAsHTML() : undefined;
|
|
27
|
-
};
|
|
28
|
-
export class CueController {
|
|
29
|
-
constructor(host, listener) {
|
|
30
|
-
this.listener = listener;
|
|
31
|
-
host.addController(this);
|
|
32
|
-
}
|
|
33
|
-
follow(track) {
|
|
34
|
-
this.stop();
|
|
35
|
-
this.track = track;
|
|
36
|
-
track.addEventListener('cuechange', this.listener);
|
|
37
|
-
}
|
|
38
|
-
hostDisconnected() {
|
|
39
|
-
this.stop();
|
|
40
|
-
}
|
|
41
|
-
stop() {
|
|
42
|
-
this.track?.removeEventListener('cuechange', this.listener);
|
|
43
|
-
this.track = undefined;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
-
/**
|
|
3
|
-
* Into fullscreen and back, and honest about where the element is.
|
|
4
|
-
*
|
|
5
|
-
* The state follows `fullscreenchange` rather than the request, so a refused
|
|
6
|
-
* request leaves a button that correctly reports "not fullscreen".
|
|
7
|
-
*/
|
|
8
|
-
export declare class FullscreenController implements ReactiveController {
|
|
9
|
-
private readonly host;
|
|
10
|
-
private readonly report;
|
|
11
|
-
constructor(host: ReactiveControllerHost & HTMLElement, report: (full: boolean) => void);
|
|
12
|
-
/**
|
|
13
|
-
* Into fullscreen and back, and quiet about being refused.
|
|
14
|
-
*
|
|
15
|
-
* `requestFullscreen` rejects rather than returning false, and the two
|
|
16
|
-
* refusals a consumer will actually meet are a Permissions Policy that does
|
|
17
|
-
* not grant `fullscreen` to the frame the player is in, and a call without
|
|
18
|
-
* user activation. Both are `TypeError: Permissions check failed`, which is
|
|
19
|
-
* the same message for two different causes.
|
|
20
|
-
*
|
|
21
|
-
* Caught rather than `void`ed, or a refusal becomes an unhandled rejection
|
|
22
|
-
* naming neither the element nor the reason.
|
|
23
|
-
*/
|
|
24
|
-
toggle(): Promise<void>;
|
|
25
|
-
hostConnected(): void;
|
|
26
|
-
hostDisconnected(): void;
|
|
27
|
-
private readonly handleChange;
|
|
28
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export class FullscreenController {
|
|
2
|
-
constructor(host, report) {
|
|
3
|
-
this.host = host;
|
|
4
|
-
this.report = report;
|
|
5
|
-
this.handleChange = () => {
|
|
6
|
-
this.report(document.fullscreenElement === this.host);
|
|
7
|
-
};
|
|
8
|
-
host.addController(this);
|
|
9
|
-
}
|
|
10
|
-
async toggle() {
|
|
11
|
-
try {
|
|
12
|
-
if (document.fullscreenElement === this.host) {
|
|
13
|
-
await document.exitFullscreen();
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
await this.host.requestFullscreen();
|
|
17
|
-
}
|
|
18
|
-
catch (error) {
|
|
19
|
-
console.warn(`<${this.host.localName}> could not enter fullscreen. If it is inside an iframe, that frame needs allow="fullscreen".`, error);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
hostConnected() {
|
|
23
|
-
document.addEventListener('fullscreenchange', this.handleChange);
|
|
24
|
-
}
|
|
25
|
-
hostDisconnected() {
|
|
26
|
-
document.removeEventListener('fullscreenchange', this.handleChange);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
-
/**
|
|
3
|
-
* How long the reader has been still, which decides when the bar goes.
|
|
4
|
-
*
|
|
5
|
-
* A wait rather than leaving with the pointer, or a reader moving across the
|
|
6
|
-
* picture to reach a control watches the bar vanish under them. The wait is
|
|
7
|
-
* only ever about *idling*: whether a paused video keeps its bar is the
|
|
8
|
-
* element's decision, and this only says when the reader stopped.
|
|
9
|
-
*
|
|
10
|
-
* The delay is asked for on every restart rather than taken once, because it
|
|
11
|
-
* is a property the consumer can change while the video plays.
|
|
12
|
-
*/
|
|
13
|
-
export declare class IdleController implements ReactiveController {
|
|
14
|
-
private readonly delay;
|
|
15
|
-
private readonly report;
|
|
16
|
-
private timer;
|
|
17
|
-
constructor(host: ReactiveControllerHost, delay: () => number, report: (idle: boolean) => void);
|
|
18
|
-
/**
|
|
19
|
-
* Restarts the wait, on anything that counts as the reader being here.
|
|
20
|
-
*
|
|
21
|
-
* Pointer movement and focus both count, and both have to: a reader using the
|
|
22
|
-
* keyboard never moves a pointer, and a bar that only answered the mouse
|
|
23
|
-
* would leave them nothing to look at. Zero turns the wait off and the bar
|
|
24
|
-
* goes immediately; a large number keeps it up for as long as anyone is
|
|
25
|
-
* likely to look.
|
|
26
|
-
*/
|
|
27
|
-
readonly stir: () => void;
|
|
28
|
-
/**
|
|
29
|
-
* Leaving is the one case that does not wait: the reader has gone somewhere
|
|
30
|
-
* else, and there is nothing to reach for.
|
|
31
|
-
*/
|
|
32
|
-
readonly leave: () => void;
|
|
33
|
-
hostDisconnected(): void;
|
|
34
|
-
}
|
package/dist/video/root/idle.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export class IdleController {
|
|
2
|
-
constructor(host, delay, report) {
|
|
3
|
-
this.delay = delay;
|
|
4
|
-
this.report = report;
|
|
5
|
-
this.stir = () => {
|
|
6
|
-
this.report(false);
|
|
7
|
-
clearTimeout(this.timer);
|
|
8
|
-
const delay = this.delay();
|
|
9
|
-
if (delay > 0) {
|
|
10
|
-
this.timer = setTimeout(() => {
|
|
11
|
-
this.report(true);
|
|
12
|
-
}, delay);
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
this.leave = () => {
|
|
16
|
-
clearTimeout(this.timer);
|
|
17
|
-
this.report(true);
|
|
18
|
-
};
|
|
19
|
-
host.addController(this);
|
|
20
|
-
}
|
|
21
|
-
hostDisconnected() {
|
|
22
|
-
clearTimeout(this.timer);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What the consumer slotted, as the plain data a `<video>` can be given.
|
|
3
|
-
*
|
|
4
|
-
* Separable because it is a read of light DOM and nothing else: no media
|
|
5
|
-
* element, no playback state, nothing drawn. A media element runs resource
|
|
6
|
-
* selection over its own child nodes and a slot is one node, so the element
|
|
7
|
-
* copies rather than slots, and the two questions the copying asks are answered
|
|
8
|
-
* here: which attributes come across, and whether a fresh read differs from the
|
|
9
|
-
* set already held. When to read, and what to do with the answer, stay with the
|
|
10
|
-
* element.
|
|
11
|
-
*/
|
|
12
|
-
/** A `<source>` or `<track>` as the consumer wrote it, reduced to what the copy carries. */
|
|
13
|
-
export interface MediaCopy {
|
|
14
|
-
attrs: Record<string, string>;
|
|
15
|
-
tag: 'source' | 'track';
|
|
16
|
-
}
|
|
17
|
-
/** The copies to make of what the default slot is given. */
|
|
18
|
-
export declare const copiesOf: (elements: readonly Element[]) => MediaCopy[];
|
|
19
|
-
/**
|
|
20
|
-
* Whether two reads describe the same media.
|
|
21
|
-
*
|
|
22
|
-
* The same set again is not a change. A video reloads for every new list, and
|
|
23
|
-
* the first `slotchange` reports the children the element has already read.
|
|
24
|
-
*/
|
|
25
|
-
export declare const sameCopies: (one: readonly MediaCopy[], two: readonly MediaCopy[]) => boolean;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const CARRIED = {
|
|
2
|
-
source: ['media', 'sizes', 'src', 'srcset', 'type'],
|
|
3
|
-
track: ['default', 'kind', 'label', 'src', 'srclang'],
|
|
4
|
-
};
|
|
5
|
-
export const copiesOf = (elements) => elements
|
|
6
|
-
.filter((node) => node instanceof HTMLSourceElement || node instanceof HTMLTrackElement)
|
|
7
|
-
.map((node) => {
|
|
8
|
-
const tag = node instanceof HTMLTrackElement ? 'track' : 'source';
|
|
9
|
-
const attrs = {};
|
|
10
|
-
for (const name of CARRIED[tag]) {
|
|
11
|
-
const value = node.getAttribute(name);
|
|
12
|
-
if (value !== null) {
|
|
13
|
-
attrs[name] = value;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return { attrs, tag };
|
|
17
|
-
});
|
|
18
|
-
export const sameCopies = (one, two) => JSON.stringify(one) === JSON.stringify(two);
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
-
/**
|
|
3
|
-
* How far a skip control moves, in seconds.
|
|
4
|
-
*
|
|
5
|
-
* Ten, which is the number every player that has one uses, and the number the
|
|
6
|
-
* platform names in `seekbackward` and `seekforward` when it hands over a
|
|
7
|
-
* default. A different length here would be correct only on this player.
|
|
8
|
-
*/
|
|
9
|
-
export declare const SKIP_SECONDS = 10;
|
|
10
|
-
/** What a headset's buttons do, handed over by the player that owns them. */
|
|
11
|
-
export interface SessionHandlers {
|
|
12
|
-
/**
|
|
13
|
-
* Asks for the next item, or `null` where the consumer has named nothing to
|
|
14
|
-
* skip to: the platform draws the key from whether a handler is set.
|
|
15
|
-
*/
|
|
16
|
-
next: (() => void) | null;
|
|
17
|
-
/** And the previous one, under the same rule. */
|
|
18
|
-
previous: (() => void) | null;
|
|
19
|
-
seek: (seconds: number) => void;
|
|
20
|
-
skipBy: (seconds: number) => void;
|
|
21
|
-
toggle: () => void;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Holds the page's media session for one player, and tells the platform what
|
|
25
|
-
* is playing.
|
|
26
|
-
*
|
|
27
|
-
* The handlers are what make a headset's play, pause and skip work at all.
|
|
28
|
-
* Without them a platform falls back to guessing which element was meant,
|
|
29
|
-
* which is right often enough to hide the fault and wrong exactly when there
|
|
30
|
-
* are two.
|
|
31
|
-
*/
|
|
32
|
-
export declare class MediaSessionController implements ReactiveController {
|
|
33
|
-
private readonly host;
|
|
34
|
-
constructor(host: ReactiveControllerHost & HTMLElement);
|
|
35
|
-
/**
|
|
36
|
-
* Takes the session.
|
|
37
|
-
*
|
|
38
|
-
* On play rather than on render, which is the ownership rule: a page with two
|
|
39
|
-
* players gives the session to whichever one the reader started, so a
|
|
40
|
-
* headset's buttons land on what they are listening to rather than on
|
|
41
|
-
* whichever came first in the markup.
|
|
42
|
-
*/
|
|
43
|
-
claim(metadata: {
|
|
44
|
-
artwork: string;
|
|
45
|
-
title: string;
|
|
46
|
-
}, handlers: SessionHandlers): void;
|
|
47
|
-
/** Tells the platform whether the owner is playing, and nothing if it is not the owner. */
|
|
48
|
-
report(playing: boolean): void;
|
|
49
|
-
/**
|
|
50
|
-
* The page's one media session goes back to nobody, but only if this
|
|
51
|
-
* element is the one holding it.
|
|
52
|
-
*/
|
|
53
|
-
hostDisconnected(): void;
|
|
54
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
let owner;
|
|
2
|
-
export const SKIP_SECONDS = 10;
|
|
3
|
-
export class MediaSessionController {
|
|
4
|
-
constructor(host) {
|
|
5
|
-
this.host = host;
|
|
6
|
-
host.addController(this);
|
|
7
|
-
}
|
|
8
|
-
claim(metadata, handlers) {
|
|
9
|
-
if (typeof navigator === 'undefined' || !navigator.mediaSession) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
owner = this.host;
|
|
13
|
-
const session = navigator.mediaSession;
|
|
14
|
-
session.metadata = new MediaMetadata({
|
|
15
|
-
artwork: metadata.artwork === '' ? [] : [{ src: metadata.artwork }],
|
|
16
|
-
title: metadata.title,
|
|
17
|
-
});
|
|
18
|
-
session.setActionHandler('play', handlers.toggle);
|
|
19
|
-
session.setActionHandler('pause', handlers.toggle);
|
|
20
|
-
session.setActionHandler('seekto', (details) => {
|
|
21
|
-
if (details.seekTime !== undefined && details.seekTime !== null) {
|
|
22
|
-
handlers.seek(details.seekTime);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
session.setActionHandler('seekbackward', (details) => {
|
|
26
|
-
handlers.skipBy(-(details.seekOffset ?? SKIP_SECONDS));
|
|
27
|
-
});
|
|
28
|
-
session.setActionHandler('seekforward', (details) => {
|
|
29
|
-
handlers.skipBy(details.seekOffset ?? SKIP_SECONDS);
|
|
30
|
-
});
|
|
31
|
-
session.setActionHandler('nexttrack', handlers.next);
|
|
32
|
-
session.setActionHandler('previoustrack', handlers.previous);
|
|
33
|
-
}
|
|
34
|
-
report(playing) {
|
|
35
|
-
if (owner === this.host && typeof navigator !== 'undefined' && navigator.mediaSession) {
|
|
36
|
-
navigator.mediaSession.playbackState = playing ? 'playing' : 'paused';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
hostDisconnected() {
|
|
40
|
-
if (owner !== this.host) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
owner = undefined;
|
|
44
|
-
if (typeof navigator !== 'undefined' && navigator.mediaSession) {
|
|
45
|
-
navigator.mediaSession.metadata = null;
|
|
46
|
-
navigator.mediaSession.playbackState = 'none';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
-
/** What the watch reports, as it learns it. */
|
|
3
|
-
export interface RemoteReport {
|
|
4
|
-
/** Whether anything on the network offers to play this. */
|
|
5
|
-
available: (available: boolean) => void;
|
|
6
|
-
/** Whether it is playing somewhere else right now. */
|
|
7
|
-
connected: (connected: boolean) => void;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Watches for something to play the video on, and stops on the way out.
|
|
11
|
-
*
|
|
12
|
-
* The Remote Playback API is the standard one, and it is what Safari
|
|
13
|
-
* implements for AirPlay and Chrome for Cast: one call covers both, where the
|
|
14
|
-
* older `webkitShowPlaybackTargetPicker` covers only the first. Firefox has
|
|
15
|
-
* neither, and `remote` simply being absent is what turns the control off
|
|
16
|
-
* there.
|
|
17
|
-
*
|
|
18
|
-
* `watchAvailability` returns an id that has to be canceled, or the callback
|
|
19
|
-
* outlives the element that owns it: a callback holding a removed element is a
|
|
20
|
-
* leak with a `@state` write on the end.
|
|
21
|
-
*/
|
|
22
|
-
export declare class RemotePlaybackController implements ReactiveController {
|
|
23
|
-
private readonly report;
|
|
24
|
-
private video;
|
|
25
|
-
/** Cancels the availability watch, since it outlives a single render. */
|
|
26
|
-
private watch;
|
|
27
|
-
constructor(host: ReactiveControllerHost, report: RemoteReport);
|
|
28
|
-
/** Starts watching the video, once there is one. */
|
|
29
|
-
attach(video: HTMLVideoElement): Promise<void>;
|
|
30
|
-
/** Opens the platform's own device picker. It has no list of its own. */
|
|
31
|
-
prompt(): Promise<void>;
|
|
32
|
-
hostDisconnected(): void;
|
|
33
|
-
private readonly handleState;
|
|
34
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export class RemotePlaybackController {
|
|
2
|
-
constructor(host, report) {
|
|
3
|
-
this.report = report;
|
|
4
|
-
this.handleState = () => {
|
|
5
|
-
this.report.connected(this.video?.remote?.state === 'connected');
|
|
6
|
-
};
|
|
7
|
-
host.addController(this);
|
|
8
|
-
}
|
|
9
|
-
async attach(video) {
|
|
10
|
-
this.video = video;
|
|
11
|
-
video.remote?.addEventListener?.('connect', this.handleState);
|
|
12
|
-
video.remote?.addEventListener?.('disconnect', this.handleState);
|
|
13
|
-
const remote = video.remote;
|
|
14
|
-
if (!remote?.watchAvailability) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
this.watch = await remote.watchAvailability((available) => {
|
|
19
|
-
this.report.available(available);
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async prompt() {
|
|
26
|
-
try {
|
|
27
|
-
await this.video?.remote?.prompt();
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
hostDisconnected() {
|
|
33
|
-
if (this.watch !== undefined) {
|
|
34
|
-
void this.video?.remote?.cancelWatchAvailability?.(this.watch);
|
|
35
|
-
this.watch = undefined;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|