@gjsify/video 0.4.19 → 0.4.20

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.
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{Gst as e}from"./gst-init.js";import{buildMediaStreamPipeline as t,buildUriPipeline as n}from"./pipeline-builder.js";import r from"gi://GObject";import i from"gi://GLib?version=2.0";import a from"gi://Gtk?version=4.0";import{attachEventControllers as o}from"@gjsify/event-bridge";import{Event as s}from"@gjsify/dom-events";import{BridgeEnvironment as c}from"@gjsify/bridge-types";import{HTMLVideoElement as l}from"@gjsify/dom-elements";const u=`media-playback-start-symbolic`,d=`media-playback-pause-symbolic`;function formatTime(e){return!isFinite(e)||isNaN(e)?`--:--`:`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,`0`)}`}const f=r.registerClass({GTypeName:`GjsifyVideoBridge`},class VideoBridge extends a.Box{constructor(e){super({...e,orientation:a.Orientation.VERTICAL}),this._timeOrigin=i.get_monotonic_time(),this._pipeline=null,this._pipelineBus=null,this._pipelineBusHandlers=[],this._readyCallbacks=[],this._resizeCallbacks=[],this._ready=!1,this._controls=null,this._positionTimerId=null,this._updatingFromTimer=!1,this._hideTimerId=null,this._overlay=new a.Overlay({hexpand:!0,vexpand:!0}),this.append(this._overlay),this._picture=new a.Picture({hexpand:!0,vexpand:!0}),this._overlay.set_child(this._picture),this._video=new l;let t={performanceNow:()=>(i.get_monotonic_time()-this._timeOrigin)/1e3,getWidth:()=>this.get_allocated_width(),getHeight:()=>this.get_allocated_height(),getDevicePixelRatio:()=>this.get_native()?.get_surface()?.get_scale_factor()??1};this._environment=new c(t),this._environment.document.body.appendChild(this._video),o(this,()=>this._video),this._video.addEventListener(`srcobjectchange`,()=>this._onSrcObjectChange()),this._video.addEventListener(`srcchange`,()=>this._onSrcChange()),this.connect(`realize`,()=>{if(!this._ready){this._ready=!0;for(let e of this._readyCallbacks)e(this._video);this._readyCallbacks=[]}});let n=0,r=0,checkResize=()=>{let e=this.get_allocated_width(),t=this.get_allocated_height();if(!(e===n&&t===r)){n=e,r=t,this._video.dispatchEvent(new s(`resize`));for(let n of this._resizeCallbacks)n(e,t)}};this.connect(`notify::width-request`,checkResize),this.connect(`notify::height-request`,checkResize),this.connect(`map`,checkResize),this.connect(`unrealize`,()=>{this._destroyPipeline(),this._stopPositionTimer(),this._resizeCallbacks=[]})}get element(){return this._video}get videoElement(){return this._video}get environment(){return this._environment}onReady(e){if(this._ready){e(this._video);return}this._readyCallbacks.push(e)}onResize(e){this._resizeCallbacks.push(e)}installGlobals(){if(globalThis.HTMLVideoElement=l,globalThis.performance===void 0){let e=this._timeOrigin;globalThis.performance={now:()=>(i.get_monotonic_time()-e)/1e3,timeOrigin:Date.now()}}}showControls(e=!0){if(e&&!this._controls){this._controls=this._buildControlBar();let{bar:e}=this._controls;e.set_halign(a.Align.FILL),e.set_valign(a.Align.END),e.set_visible(!1),this._overlay.add_overlay(e),this._startPositionTimer(),this._setupAutoHideMotion(e)}else !e&&this._controls&&(this._overlay.remove_overlay(this._controls.bar),this._controls=null,this._stopPositionTimer(),this._hideTimerId!==null&&(i.Source.remove(this._hideTimerId),this._hideTimerId=null))}_setupAutoHideMotion(e){for(let t of[this,e]){let e=new a.EventControllerMotion;e.connect(`motion`,()=>this._revealControls()),e.connect(`enter`,()=>this._revealControls()),t.add_controller(e)}}_revealControls(){this._controls&&(this._controls.bar.set_visible(!0),this._hideTimerId!==null&&i.Source.remove(this._hideTimerId),this._hideTimerId=i.timeout_add_seconds(i.PRIORITY_DEFAULT,2,()=>(this._hideTimerId=null,this._controls?.bar.set_visible(!1),i.SOURCE_REMOVE)))}_buildControlBar(){let e=new a.Box({orientation:a.Orientation.HORIZONTAL,spacing:6,margin_start:6,margin_end:6,margin_top:4,margin_bottom:4});e.add_css_class(`osd`);let t=new a.Button({icon_name:d});t.connect(`clicked`,()=>{this._video.paused?(this._video.play(),t.set_icon_name(d)):(this._video.pause(),t.set_icon_name(u))}),e.append(t);let n=new a.Adjustment({lower:0,upper:1,step_increment:1,page_increment:10}),r=a.Scale.new(a.Orientation.HORIZONTAL,n);r.set_hexpand(!0),r.set_draw_value(!1),r.connect(`change-value`,(e,t,n)=>(!this._updatingFromTimer&&isFinite(n)&&(this._video.currentTime=n),!1)),e.append(r);let i=new a.Label({label:`--:-- / --:--`,use_markup:!1});e.append(i);let o=new a.VolumeButton({value:1});return o.connect(`value-changed`,(e,t)=>{this._video.volume=t}),e.append(o),{bar:e,playBtn:t,seekAdj:n,seekScale:r,timeLabel:i,volumeBtn:o,lastSeekValue:NaN,lastTimeText:``}}_startPositionTimer(){this._positionTimerId===null&&(this._positionTimerId=i.timeout_add(i.PRIORITY_DEFAULT,200,()=>{let e=this._controls;if(!e)return i.SOURCE_REMOVE;let t=this._video.currentTime,n=this._video.duration;isFinite(n)&&n>0&&(e.seekAdj.upper!==n&&(e.seekAdj.upper=n),t!==e.lastSeekValue&&(this._updatingFromTimer=!0,e.seekAdj.set_value(t),this._updatingFromTimer=!1,e.lastSeekValue=t));let r=`${formatTime(t)} / ${formatTime(n)}`;r!==e.lastTimeText&&(e.timeLabel.set_label(r),e.lastTimeText=r);let a=this._video.paused?u:d;return e.playBtn.get_icon_name()!==a&&e.playBtn.set_icon_name(a),i.SOURCE_CONTINUE}))}_stopPositionTimer(){this._positionTimerId!==null&&(i.Source.remove(this._positionTimerId),this._positionTimerId=null)}_onSrcObjectChange(){this._destroyPipeline();let e=this._video.srcObject;if(!e)return;let n=(e.getVideoTracks?.()??[]).find(e=>e._gstSource!=null);if(!n?._gstSource){console.warn(`VideoBridge: MediaStream has no video track with GStreamer source`);return}try{let{pipeline:e,paintable:r,tee:i}=t(n._gstSource,n._gstPipeline);this._attachPipeline(e,r),n._gstPipeline=e,n._gstTee=i}catch(e){console.error(`VideoBridge: Failed to build MediaStream pipeline:`,e)}}_onSrcChange(){if(this._destroyPipeline(),this._video.src)try{let{pipeline:e,paintable:t}=n(this._video.src);this._attachPipeline(e,t)}catch(e){console.error(`VideoBridge: Failed to build URI pipeline:`,e)}}_attachPipeline(t,n){this._pipeline=t,this._video._pipeline=t,this._picture.set_paintable(n);let r=t.get_bus();r&&(r.add_signal_watch(),this._pipelineBus=r,this._pipelineBusHandlers=[r.connect(`message::error`,(e,t)=>{let[n,r]=t.parse_error();console.error(`VideoBridge pipeline error: ${n?.message??`unknown`} (${r??``})`),this._video.dispatchEvent(new s(`error`))}),r.connect(`message::warning`,(e,t)=>{let[n,r]=t.parse_warning();console.warn(`VideoBridge pipeline warning: ${n?.message??`unknown`} (${r??``})`)})]),t.set_state(e.State.PLAYING)===e.StateChangeReturn.FAILURE&&console.error(`VideoBridge: pipeline state change to PLAYING failed`),this._video.readyState=4,this._video.dispatchEvent(new s(`loadedmetadata`)),this._video.dispatchEvent(new s(`canplay`)),this._video.dispatchEvent(new s(`playing`))}_destroyPipeline(){if(this._pipelineBus){for(let e of this._pipelineBusHandlers)try{this._pipelineBus.disconnect(e)}catch{}try{this._pipelineBus.remove_signal_watch()}catch{}this._pipelineBus=null,this._pipelineBusHandlers=[]}if(this._pipeline){try{this._pipeline.set_state(e.State.NULL)}catch{}this._pipeline=null,this._video._pipeline=null}this._picture.set_paintable(null)}});export{f as VideoBridge};
1
+ import"./_virtual/_rolldown/runtime.js";import{Gst as e}from"./gst-init.js";import{buildMediaStreamPipeline as t,buildUriPipeline as n}from"./pipeline-builder.js";import r from"gi://GObject";import i from"gi://GLib?version=2.0";import a from"gi://Gtk?version=4.0";import{attachEventControllers as o}from"@gjsify/event-bridge";import{Event as s}from"@gjsify/dom-events";import{BridgeEnvironment as c}from"@gjsify/bridge-types";import{HTMLVideoElement as l,notifyElementResize as u}from"@gjsify/dom-elements";const d=`media-playback-start-symbolic`,f=`media-playback-pause-symbolic`;function formatTime(e){return!isFinite(e)||isNaN(e)?`--:--`:`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,`0`)}`}const p=r.registerClass({GTypeName:`GjsifyVideoBridge`},class VideoBridge extends a.Box{constructor(e){super({...e,orientation:a.Orientation.VERTICAL}),this._timeOrigin=i.get_monotonic_time(),this._pipeline=null,this._pipelineBus=null,this._pipelineBusHandlers=[],this._readyCallbacks=[],this._resizeCallbacks=[],this._ready=!1,this._controls=null,this._positionTimerId=null,this._updatingFromTimer=!1,this._hideTimerId=null,this._overlay=new a.Overlay({hexpand:!0,vexpand:!0}),this.append(this._overlay),this._picture=new a.Picture({hexpand:!0,vexpand:!0}),this._overlay.set_child(this._picture),this._video=new l;let t={performanceNow:()=>(i.get_monotonic_time()-this._timeOrigin)/1e3,getWidth:()=>this.get_allocated_width(),getHeight:()=>this.get_allocated_height(),getDevicePixelRatio:()=>this.get_native()?.get_surface()?.get_scale_factor()??1};this._environment=new c(t),this._environment.document.body.appendChild(this._video),o(this,()=>this._video),this._video.addEventListener(`srcobjectchange`,()=>this._onSrcObjectChange()),this._video.addEventListener(`srcchange`,()=>this._onSrcChange()),this.connect(`realize`,()=>{if(!this._ready){this._ready=!0;for(let e of this._readyCallbacks)e(this._video);this._readyCallbacks=[]}});let n=0,r=0,checkResize=()=>{let e=this.get_allocated_width(),t=this.get_allocated_height();if(!(e===n&&t===r)){n=e,r=t,this._video.dispatchEvent(new s(`resize`)),u(this._video,e,t);for(let n of this._resizeCallbacks)n(e,t)}};this.connect(`notify::width-request`,checkResize),this.connect(`notify::height-request`,checkResize),this.connect(`map`,checkResize),this.connect(`unrealize`,()=>{this._destroyPipeline(),this._stopPositionTimer(),this._resizeCallbacks=[]})}get element(){return this._video}get videoElement(){return this._video}get environment(){return this._environment}onReady(e){if(this._ready){e(this._video);return}this._readyCallbacks.push(e)}onResize(e){this._resizeCallbacks.push(e)}installGlobals(){if(globalThis.HTMLVideoElement=l,globalThis.performance===void 0){let e=this._timeOrigin;globalThis.performance={now:()=>(i.get_monotonic_time()-e)/1e3,timeOrigin:Date.now()}}}showControls(e=!0){if(e&&!this._controls){this._controls=this._buildControlBar();let{bar:e}=this._controls;e.set_halign(a.Align.FILL),e.set_valign(a.Align.END),e.set_visible(!1),this._overlay.add_overlay(e),this._startPositionTimer(),this._setupAutoHideMotion(e)}else !e&&this._controls&&(this._overlay.remove_overlay(this._controls.bar),this._controls=null,this._stopPositionTimer(),this._hideTimerId!==null&&(i.Source.remove(this._hideTimerId),this._hideTimerId=null))}_setupAutoHideMotion(e){for(let t of[this,e]){let e=new a.EventControllerMotion;e.connect(`motion`,()=>this._revealControls()),e.connect(`enter`,()=>this._revealControls()),t.add_controller(e)}}_revealControls(){this._controls&&(this._controls.bar.set_visible(!0),this._hideTimerId!==null&&i.Source.remove(this._hideTimerId),this._hideTimerId=i.timeout_add_seconds(i.PRIORITY_DEFAULT,2,()=>(this._hideTimerId=null,this._controls?.bar.set_visible(!1),i.SOURCE_REMOVE)))}_buildControlBar(){let e=new a.Box({orientation:a.Orientation.HORIZONTAL,spacing:6,margin_start:6,margin_end:6,margin_top:4,margin_bottom:4});e.add_css_class(`osd`);let t=new a.Button({icon_name:f});t.connect(`clicked`,()=>{this._video.paused?(this._video.play(),t.set_icon_name(f)):(this._video.pause(),t.set_icon_name(d))}),e.append(t);let n=new a.Adjustment({lower:0,upper:1,step_increment:1,page_increment:10}),r=a.Scale.new(a.Orientation.HORIZONTAL,n);r.set_hexpand(!0),r.set_draw_value(!1),r.connect(`change-value`,(e,t,n)=>(!this._updatingFromTimer&&isFinite(n)&&(this._video.currentTime=n),!1)),e.append(r);let i=new a.Label({label:`--:-- / --:--`,use_markup:!1});e.append(i);let o=new a.VolumeButton({value:1});return o.connect(`value-changed`,(e,t)=>{this._video.volume=t}),e.append(o),{bar:e,playBtn:t,seekAdj:n,seekScale:r,timeLabel:i,volumeBtn:o,lastSeekValue:NaN,lastTimeText:``}}_startPositionTimer(){this._positionTimerId===null&&(this._positionTimerId=i.timeout_add(i.PRIORITY_DEFAULT,200,()=>{let e=this._controls;if(!e)return i.SOURCE_REMOVE;let t=this._video.currentTime,n=this._video.duration;isFinite(n)&&n>0&&(e.seekAdj.upper!==n&&(e.seekAdj.upper=n),t!==e.lastSeekValue&&(this._updatingFromTimer=!0,e.seekAdj.set_value(t),this._updatingFromTimer=!1,e.lastSeekValue=t));let r=`${formatTime(t)} / ${formatTime(n)}`;r!==e.lastTimeText&&(e.timeLabel.set_label(r),e.lastTimeText=r);let a=this._video.paused?d:f;return e.playBtn.get_icon_name()!==a&&e.playBtn.set_icon_name(a),i.SOURCE_CONTINUE}))}_stopPositionTimer(){this._positionTimerId!==null&&(i.Source.remove(this._positionTimerId),this._positionTimerId=null)}_onSrcObjectChange(){this._destroyPipeline();let e=this._video.srcObject;if(!e)return;let n=(e.getVideoTracks?.()??[]).find(e=>e._gstSource!=null);if(!n?._gstSource){console.warn(`VideoBridge: MediaStream has no video track with GStreamer source`);return}try{let{pipeline:e,paintable:r,tee:i}=t(n._gstSource,n._gstPipeline);this._attachPipeline(e,r),n._gstPipeline=e,n._gstTee=i}catch(e){console.error(`VideoBridge: Failed to build MediaStream pipeline:`,e)}}_onSrcChange(){if(this._destroyPipeline(),this._video.src)try{let{pipeline:e,paintable:t}=n(this._video.src);this._attachPipeline(e,t)}catch(e){console.error(`VideoBridge: Failed to build URI pipeline:`,e)}}_attachPipeline(t,n){this._pipeline=t,this._video._pipeline=t,this._picture.set_paintable(n);let r=t.get_bus();r&&(r.add_signal_watch(),this._pipelineBus=r,this._pipelineBusHandlers=[r.connect(`message::error`,(e,t)=>{let[n,r]=t.parse_error();console.error(`VideoBridge pipeline error: ${n?.message??`unknown`} (${r??``})`),this._video.dispatchEvent(new s(`error`))}),r.connect(`message::warning`,(e,t)=>{let[n,r]=t.parse_warning();console.warn(`VideoBridge pipeline warning: ${n?.message??`unknown`} (${r??``})`)})]),t.set_state(e.State.PLAYING)===e.StateChangeReturn.FAILURE&&console.error(`VideoBridge: pipeline state change to PLAYING failed`),this._video.readyState=4,this._video.dispatchEvent(new s(`loadedmetadata`)),this._video.dispatchEvent(new s(`canplay`)),this._video.dispatchEvent(new s(`playing`))}_destroyPipeline(){if(this._pipelineBus){for(let e of this._pipelineBusHandlers)try{this._pipelineBus.disconnect(e)}catch{}try{this._pipelineBus.remove_signal_watch()}catch{}this._pipelineBus=null,this._pipelineBusHandlers=[]}if(this._pipeline){try{this._pipeline.set_state(e.State.NULL)}catch{}this._pipeline=null,this._video._pipeline=null}this._picture.set_paintable(null)}});export{p as VideoBridge};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/video",
3
- "version": "0.4.19",
3
+ "version": "0.4.20",
4
4
  "description": "VideoBridge — GTK container for HTMLVideoElement backed by GStreamer gtk4paintablesink",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -35,14 +35,14 @@
35
35
  "@girs/gobject-2.0": "2.88.0-4.0.0-rc.15",
36
36
  "@girs/gst-1.0": "1.28.1-4.0.0-rc.15",
37
37
  "@girs/gtk-4.0": "4.23.0-4.0.0-rc.15",
38
- "@gjsify/bridge-types": "^0.4.19",
39
- "@gjsify/dom-elements": "^0.4.19",
40
- "@gjsify/dom-events": "^0.4.19",
41
- "@gjsify/dom-exception": "^0.4.19",
42
- "@gjsify/event-bridge": "^0.4.19"
38
+ "@gjsify/bridge-types": "^0.4.20",
39
+ "@gjsify/dom-elements": "^0.4.20",
40
+ "@gjsify/dom-events": "^0.4.20",
41
+ "@gjsify/dom-exception": "^0.4.20",
42
+ "@gjsify/event-bridge": "^0.4.20"
43
43
  },
44
44
  "devDependencies": {
45
- "@gjsify/cli": "^0.4.19",
45
+ "@gjsify/cli": "^0.4.20",
46
46
  "@types/node": "^25.6.2",
47
47
  "typescript": "^6.0.3"
48
48
  }