@scrypted/prebuffer-mixin 0.1.116 → 0.1.120
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.md +2 -14
- package/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/main.ts +28 -3
package/README.md
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
# Prebuffer
|
|
1
|
+
# Rebroadcast and Prebuffer for Scrypted
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* npm run scrypted-webpack
|
|
5
|
-
* npm run scrypted-deploy <ipaddress>
|
|
6
|
-
* npm run scrypted-debug <ipaddress>
|
|
7
|
-
|
|
8
|
-
## scrypted distribution via npm
|
|
9
|
-
1. Ensure package.json is set up properly for publishing on npm.
|
|
10
|
-
2. npm publish
|
|
11
|
-
|
|
12
|
-
## Visual Studio Code configuration
|
|
13
|
-
|
|
14
|
-
* If using a remote server, edit [.vscode/settings.json](blob/master/.vscode/settings.json) to specify the IP Address of the Scrypted server.
|
|
15
|
-
* Launch Scrypted Debugger from the launch menu.
|
|
3
|
+
This plugin maintains connections to all connected cameras, and buffers a small amount of recent video for instant replays. This instant replay is used by HomeKit Secure Video, as well as speeding up initial live stream load times.
|
package/dist/main.nodejs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(()=>{var e={454:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{systemManager:o}=i.default,s="v4";class AutoenableMixinProvider extends i.ScryptedDeviceBase{constructor(e){var t,r,n;super(e),n={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=o.getComponent("plugins"),o.listen((async(e,t,r)=>{t.eventInterface!==i.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)}));for(const e of Object.keys(o.getSystemState())){const t=o.getDeviceById(e);this.maybeEnableMixin(t)}}async shouldEnableMixin(e){return!0}async maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id]===s)return;if(!await this.canMixin(e.type,e.interfaces))return;if(!await this.shouldEnableMixin(e))return;this.log.i("auto enabling mixin for "+e.name);const r=(e.mixins||[]).slice();r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r),this.setHasEnabledMixin(e.id)}setHasEnabledMixin(e){this.hasEnabledMixin[e]!==s&&(this.hasEnabledMixin[e]=s,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=AutoenableMixinProvider},201:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createRebroadcaster=h,t.parseAudioCodec=f,t.parseResolution=l,t.parseVideoCodec=m,t.startRebroadcastSession=async function(e,t){let r,a,d=0,p=!0;const g=new s.EventEmitter;g.on("error",(e=>v.error("rebroadcast error",e)));const{console:v}=t;let y,S,b,M,P;const D=new Promise(((e,t)=>{M=e,P=t}));function O(){p&&(g.emit("killed"),g.emit("error",new Error("killed"))),p=!1,null==C||C.kill(),null==C||C.kill("SIGKILL");for(const e of A)null==e||e.close();P(new Error("ffmpeg was killed before connecting to the rebroadcast session")),clearTimeout(r),clearTimeout(a)}function x(){t.timeout&&(clearTimeout(r),r=setTimeout(O,t.timeout))}x();const w={},I=e.inputArguments.slice(),A=[];a=setTimeout(O,3e4);for(const n of Object.keys(t.parsers)){const s=t.parsers[n],a=n+"-data";if(t.parseOnly)w[n]={url:void 0,mediaStreamOptions:e.mediaStreamOptions};else{const{server:t,port:i}=await h({connect:(e,t)=>{d++,clearTimeout(r);const i=()=>{g.removeListener(a,e),g.removeListener("killed",t),d--,0===d&&x(),t()};return g.on(a,e),g.once("killed",i),i}});A.push(t);const o=`tcp://127.0.0.1:${i}`;w[n]={url:o,mediaStreamOptions:e.mediaStreamOptions,inputArguments:["-f",n,"-i",o]}}const c=(0,i.createServer)((async e=>{c.close(),M(e);try{const i=n+"-data";for await(const n of s.parse(e,parseInt(null===(t=b)||void 0===t?void 0:t[2]),parseInt(null===(r=b)||void 0===r?void 0:r[3]))){var t,r;g.emit(i,n)}}catch(e){v.error("rebroadcast parse error",e),O()}}));A.push(c);const u=await(0,o.listenZero)(c);I.push(...s.outputArguments,`tcp://127.0.0.1:${u}`)}I.unshift("-hide_banner"),v.log(I.join(" "));const C=n.default.spawn(await u.getFFmpegPath(),I);return(0,c.ffmpegLogInitialOutput)(v,C),C.on("exit",O),f(C).then((e=>y=e)),m(C).then((e=>S=e)),l(C).then((e=>b=e)),await D,clearTimeout(a),{inputAudioCodec:y,inputVideoCodec:S,inputVideoResolution:b,events:g,resetActivityTimer:x,isActive:()=>p,kill:O,servers:A,cp:C,ffmpegInputs:w}};var i=r(808),n=d(r(81)),o=r(769),s=r(361),a=d(r(510)),c=r(833);function d(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:u}=a.default;async function l(e){return new Promise((t=>{const r=i=>{const n=i.toString(),o=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(n);o&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(o))};e.stdout.on("data",r),e.stderr.on("data",r)}))}async function p(e,t){return new Promise((r=>{const i=n=>{const o=n.toString(),s=o.indexOf(`${t}: `);if(-1!==s){const n=o.substring(s+t.length+1).trim();let a=n.indexOf(" ");const c=n.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",i),e.stderr.removeListener("data",i),r(n.substring(0,a)))}};e.stdout.on("data",i),e.stderr.on("data",i)}))}async function m(e){return p(e,"Video")}async function f(e){return p(e,"Audio")}async function h(e){const t=(0,i.createServer)((t=>{let r=!0;const i=()=>{t.removeAllListeners(),t.destroy();const e=n;n=void 0,null==e||e()};let n=null==e?void 0:e.connect((e=>{r&&(r=!1,e.startStream&&t.write(e.startStream));for(const r of e.chunks)t.write(r);return t.writableLength}),i);t.on("end",i),t.on("close",i),t.on("error",i)}));return{server:t,port:await(0,o.listenZero)(t)}}},769:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bindZero=async function(e){return e.bind(0),await(0,i.once)(e,"listening"),e.address().port},t.listenZero=async function(e){return e.listen(0),await(0,i.once)(e,"listening"),e.address().port};var i=r(361)},833:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(568);Object.keys(i).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===i[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return i[e]}}))}))},701:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=async function(e,t){if(!t)return Buffer.alloc(0);{const r=e.read(t);if(r)return r}return new Promise(((r,i)=>{const n=()=>{const i=e.read(t);i&&(s(),r(i))},o=()=>{s(),i(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",n),e.removeListener("end",o)};e.on("readable",n),e.on("end",o)}))}},567:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{deviceManager:o}=i.default;class SettingsMixinDeviceBase extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId,r.mixinStorageSuffix),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey,process.nextTick((()=>o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)))}async getSettings(){const e=this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?this.mixinDevice.getSettings():void 0,t=this.getMixinSettings(),r=[];try{const t=await e||[];r.push(...t)}catch(e){const t=this.name;r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:`${t} Extension settings failed to load.`,readonly:!0})}try{const e=await t||[];for(const t of e)t.group=t.group||this.settingsGroup,t.key=this.settingsGroupKey+":"+t.key;r.push(...e)}catch(e){const t=o.getDeviceState(this.mixinProviderNativeId).name;r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:`${t} Extension settings failed to load.`,readonly:!0})}return r}async putSetting(e,t){const r=this.settingsGroupKey+":";if(null==e||!e.startsWith(r))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(r.length),t),o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}release(){o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=SettingsMixinDeviceBase},129:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PIXEL_FORMAT_YUV420P=t.PIXEL_FORMAT_RGB24=void 0,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof","-f","mp4"],async*parse(e){const t=a(e);let r,i,n;for await(const e of t)r?i||(i=e):r=e,yield{startStream:n,chunks:[e.header,e.data],type:e.type},r&&i&&!n&&(n=Buffer.concat([r.header,r.data,i.header,i.data]))},findSyncFrame:o}},t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-f","mpegts"],parse:s(188,(e=>{if(71!=e[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.")})),findSyncFrame(e){for(let t=0;t<e.length;t++){const r=e[t];for(let i=0;i<r.chunks.length;i++){const n=r.chunks[i];let o=0;for(;o+188<n.length;){const r=n.subarray(o,o+188);if(256==((31&r[1])<<8|r[2])&&32&r[3]&&r[4]>0&&64&r[5])return e.slice(t);o+=188}}}return e}}},t.createPCMParser=function(){return{container:"s16le",outputArguments:["-vn","-acodec","pcm_s16le","-f","s16le"],parse:s(512),findSyncFrame:o}},t.createRawVideoParser=function(e){var t;const r=(null===(t=e)||void 0===t?void 0:t.pixelFormat)||c;let i;e=e||{};const{size:s,everyNFrames:a}=e;s&&(i=`scale=${s.width}:${s.height}`);a&&a>1&&(i?i+=",":i="",i+=`select=not(mod(n\\,${a}))`);return{container:"rawvideo",outputArguments:[...i?["-vf",i]:[],"-an","-vcodec","rawvideo","-pix_fmt",r.name,"-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");t=(null==s?void 0:s.width)||t,i=(null==s?void 0:s.height)||i;const o=r.computeLength(t,i);for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}},t.parseFragmentedMP4=a;var i=r(361),n=r(701);function o(e){return e}function s(e,t){return async function*(r){let n=[],o=0;for(;;){const s=r.read();if(!s){await(0,i.once)(r,"readable");continue}if(n.push(s),o+=s.length,o<e)continue;const a=Buffer.concat(n);null==t||t(a);const c=a.length%e,d=a.slice(0,a.length-c),u=a.slice(a.length-c);n=[u],o=u.length,yield{chunks:[d]}}}}async function*a(e){for(;;){const t=await(0,n.readLength)(e,8),r=t.readInt32BE(0)-8,i=t.slice(4).toString(),o=await(0,n.readLength)(e,r);yield{header:t,length:r,type:i,data:o}}}const c={name:"yuv420p",computeLength:(e,t)=>e*t*1.5};t.PIXEL_FORMAT_YUV420P=c;t.PIXEL_FORMAT_RGB24={name:"rgb24",computeLength:(e,t)=>e*t*3}},510:(e,t,r)=>{"use strict";var i=Object.create?function(e,t,r,i){void 0===i&&(i=r),Object.defineProperty(e,i,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]},n=function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MixinDeviceBase=t.ScryptedDeviceBase=void 0,n(r(393),t);const o=r(393);class ScryptedDeviceBase extends o.DeviceBase{constructor(e){super(),this.nativeId=e}get storage(){return this._storage||(this._storage=deviceManager.getDeviceStorage(this.nativeId)),this._storage}get log(){return this._log||(this._log=deviceManager.getDeviceLogger(this.nativeId)),this._log}get console(){return this._console||(this._console=deviceManager.getDeviceConsole(this.nativeId)),this._console}_lazyLoadDeviceState(){this._deviceState||(this.nativeId?this._deviceState=deviceManager.getDeviceState(this.nativeId):this._deviceState=deviceManager.getDeviceState())}onDeviceEvent(e,t){return deviceManager.onDeviceEvent(this.nativeId,e,t)}}t.ScryptedDeviceBase=ScryptedDeviceBase;class MixinDeviceBase extends o.DeviceBase{constructor(e,t,r,i,n){super(),this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._mixinStorageSuffix=n,this._listeners=new Set,this._deviceState=r}get storage(){if(!this._storage){const e=this._mixinStorageSuffix,t=this.id+(e?":"+e:"");this._storage=deviceManager.getMixinStorage(t,this.mixinProviderNativeId)}return this._storage}get console(){return this._console||(deviceManager.getMixinConsole?this._console=deviceManager.getMixinConsole(this.id,this.mixinProviderNativeId):this._console=deviceManager.getDeviceConsole(this.mixinProviderNativeId)),this._console}onDeviceEvent(e,t){return deviceManager.onMixinEvent(this.id,this,e,t)}_lazyLoadDeviceState(){}manageListener(e){this._listeners.add(e)}release(){for(const e of this._listeners)e.removeListener()}}t.MixinDeviceBase=MixinDeviceBase,function(){function e(e){return function(){return this._lazyLoadDeviceState(),this._deviceState[e]}}function t(e){return function(t){this._lazyLoadDeviceState(),this._deviceState[e]=t}}for(var r of Object.values(o.ScryptedInterfaceProperty))Object.defineProperty(ScryptedDeviceBase.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(MixinDeviceBase.prototype,r,{set:t(r),get:e(r)})}();let s={};try{s=Object.assign(s,{log:deviceManager.getDeviceLogger(void 0),deviceManager,endpointManager,mediaManager,systemManager,pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/sdk/types instead",e)}t.default=s},393:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ThermostatMode=t.TemperatureUnit=t.ScryptedMimeTypes=t.ScryptedInterfaceProperty=t.ScryptedInterfaceDescriptors=t.ScryptedInterface=t.ScryptedDeviceType=t.SCRYPTED_MEDIA_SCHEME=t.MediaPlayerState=t.LockState=t.HumidityMode=t.FanMode=t.DeviceBase=void 0;let r;t.DeviceBase=class DeviceBase{},t.ScryptedInterfaceProperty=r,function(e){e.id="id",e.info="info",e.interfaces="interfaces",e.mixins="mixins",e.name="name",e.providedInterfaces="providedInterfaces",e.providedName="providedName",e.providedRoom="providedRoom",e.providedType="providedType",e.providerId="providerId",e.room="room",e.type="type",e.on="on",e.brightness="brightness",e.colorTemperature="colorTemperature",e.rgb="rgb",e.hsv="hsv",e.running="running",e.paused="paused",e.docked="docked",e.thermostatActiveMode="thermostatActiveMode",e.thermostatAvailableModes="thermostatAvailableModes",e.thermostatMode="thermostatMode",e.thermostatSetpoint="thermostatSetpoint",e.thermostatSetpointHigh="thermostatSetpointHigh",e.thermostatSetpointLow="thermostatSetpointLow",e.temperature="temperature",e.temperatureUnit="temperatureUnit",e.humidity="humidity",e.lockState="lockState",e.entryOpen="entryOpen",e.batteryLevel="batteryLevel",e.online="online",e.updateAvailable="updateAvailable",e.fromMimeType="fromMimeType",e.toMimeType="toMimeType",e.binaryState="binaryState",e.intrusionDetected="intrusionDetected",e.powerDetected="powerDetected",e.audioDetected="audioDetected",e.motionDetected="motionDetected",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position",e.humiditySetting="humiditySetting",e.fan="fan"}(r||(t.ScryptedInterfaceProperty=r={}));let i,n,o,s,a,c,d,u,l;t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",methods:["listen","probe","setName","setRoom","setType"],properties:["id","info","interfaces","mixins","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"]},OnOff:{name:"OnOff",methods:["turnOff","turnOn"],properties:["on"]},Brightness:{name:"Brightness",methods:["setBrightness"],properties:["brightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"],properties:["colorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",methods:["setRgb"],properties:["rgb"]},ColorSettingHsv:{name:"ColorSettingHsv",methods:["setHsv"],properties:["hsv"]},Notifier:{name:"Notifier",methods:["sendNotification"],properties:[]},StartStop:{name:"StartStop",methods:["start","stop"],properties:["running"]},Pause:{name:"Pause",methods:["pause","resume"],properties:["paused"]},Dock:{name:"Dock",methods:["dock"],properties:["docked"]},TemperatureSetting:{name:"TemperatureSetting",methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"],properties:["thermostatActiveMode","thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"]},Thermometer:{name:"Thermometer",methods:[],properties:["temperature","temperatureUnit"]},HumiditySensor:{name:"HumiditySensor",methods:[],properties:["humidity"]},Camera:{name:"Camera",methods:["getPictureOptions","takePicture"],properties:[]},VideoCamera:{name:"VideoCamera",methods:["getVideoStream","getVideoStreamOptions"],properties:[]},Intercom:{name:"Intercom",methods:["startIntercom","stopIntercom"],properties:[]},Lock:{name:"Lock",methods:["lock","unlock"],properties:["lockState"]},PasswordStore:{name:"PasswordStore",methods:["addPassword","getPasswords","removePassword"],properties:[]},Authenticator:{name:"Authenticator",methods:["checkPassword"],properties:[]},Scene:{name:"Scene",methods:["activate","deactivate","isReversible"],properties:[]},Entry:{name:"Entry",methods:["closeEntry","openEntry"],properties:[]},EntrySensor:{name:"EntrySensor",methods:[],properties:["entryOpen"]},DeviceProvider:{name:"DeviceProvider",methods:["getDevice"],properties:[]},DeviceDiscovery:{name:"DeviceDiscovery",methods:["discoverDevices"],properties:[]},DeviceCreator:{name:"DeviceCreator",methods:["createDevice","getCreateDeviceSettings"],properties:[]},Battery:{name:"Battery",methods:[],properties:["batteryLevel"]},Refresh:{name:"Refresh",methods:["getRefreshFrequency","refresh"],properties:[]},MediaPlayer:{name:"MediaPlayer",methods:["getMediaStatus","load","seek","skipNext","skipPrevious"],properties:[]},Online:{name:"Online",methods:[],properties:["online"]},SoftwareUpdate:{name:"SoftwareUpdate",methods:["checkForUpdate","installUpdate"],properties:["updateAvailable"]},BufferConverter:{name:"BufferConverter",methods:["convert"],properties:["fromMimeType","toMimeType"]},Settings:{name:"Settings",methods:["getSettings","putSetting"],properties:[]},BinarySensor:{name:"BinarySensor",methods:[],properties:["binaryState"]},IntrusionSensor:{name:"IntrusionSensor",methods:[],properties:["intrusionDetected"]},PowerSensor:{name:"PowerSensor",methods:[],properties:["powerDetected"]},AudioSensor:{name:"AudioSensor",methods:[],properties:["audioDetected"]},MotionSensor:{name:"MotionSensor",methods:[],properties:["motionDetected"]},OccupancySensor:{name:"OccupancySensor",methods:[],properties:["occupied"]},FloodSensor:{name:"FloodSensor",methods:[],properties:["flooded"]},UltravioletSensor:{name:"UltravioletSensor",methods:[],properties:["ultraviolet"]},LuminanceSensor:{name:"LuminanceSensor",methods:[],properties:["luminance"]},PositionSensor:{name:"PositionSensor",methods:[],properties:["position"]},Readme:{name:"Readme",methods:["getReadmeMarkdown"],properties:[]},OauthClient:{name:"OauthClient",methods:["getOauthUrl","onOauthCallback"],properties:[]},MixinProvider:{name:"MixinProvider",methods:["canMixin","getMixin","releaseMixin"],properties:[]},HttpRequestHandler:{name:"HttpRequestHandler",methods:["onRequest"],properties:[]},EngineIOHandler:{name:"EngineIOHandler",methods:["onConnection"],properties:[]},PushHandler:{name:"PushHandler",methods:["onPush"],properties:[]},Program:{name:"Program",methods:["run"],properties:[]},Scriptable:{name:"Scriptable",methods:["eval","loadScripts","saveScript"],properties:[]},ObjectDetector:{name:"ObjectDetector",methods:["getDetectionInput","getObjectTypes"],properties:[]},ObjectDetection:{name:"ObjectDetection",methods:["detectObjects","getDetectionModel"],properties:[]},HumiditySetting:{name:"HumiditySetting",methods:["setHumidity"],properties:["humiditySetting"]},Fan:{name:"Fan",methods:["setFan"],properties:["fan"]}},t.ScryptedDeviceType=i,function(e){e.Builtin="Builtin",e.Camera="Camera",e.Fan="Fan",e.Light="Light",e.Switch="Switch",e.Outlet="Outlet",e.Sensor="Sensor",e.Scene="Scene",e.Program="Program",e.Automation="Automation",e.Vacuum="Vacuum",e.Notifier="Notifier",e.Thermostat="Thermostat",e.Lock="Lock",e.PasswordControl="PasswordControl",e.Display="Display",e.Speaker="Speaker",e.Event="Event",e.Entry="Entry",e.Garage="Garage",e.DeviceProvider="DeviceProvider",e.DataSource="DataSource",e.API="API",e.Doorbell="Doorbell",e.Irrigation="Irrigation",e.Valve="Valve",e.Person="Person",e.Unknown="Unknown"}(i||(t.ScryptedDeviceType=i={})),t.HumidityMode=n,function(e){e.Humidify="Humidify",e.Dehumidify="Dehumidify",e.Auto="Auto",e.Off="Off"}(n||(t.HumidityMode=n={})),t.FanMode=o,function(e){e.Auto="Auto",e.Manual="Manual"}(o||(t.FanMode=o={})),t.TemperatureUnit=s,function(e){e.C="C",e.F="F"}(s||(t.TemperatureUnit=s={})),t.ThermostatMode=a,function(e){e.Off="Off",e.Cool="Cool",e.Heat="Heat",e.HeatCool="HeatCool",e.Auto="Auto",e.FanOnly="FanOnly",e.Purifier="Purifier",e.Eco="Eco",e.Dry="Dry",e.On="On"}(a||(t.ThermostatMode=a={})),t.LockState=c,function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(c||(t.LockState=c={})),t.MediaPlayerState=d,function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(d||(t.MediaPlayerState=d={})),t.ScryptedInterface=u,function(e){e.ScryptedDevice="ScryptedDevice",e.OnOff="OnOff",e.Brightness="Brightness",e.ColorSettingTemperature="ColorSettingTemperature",e.ColorSettingRgb="ColorSettingRgb",e.ColorSettingHsv="ColorSettingHsv",e.Notifier="Notifier",e.StartStop="StartStop",e.Pause="Pause",e.Dock="Dock",e.TemperatureSetting="TemperatureSetting",e.Thermometer="Thermometer",e.HumiditySensor="HumiditySensor",e.Camera="Camera",e.VideoCamera="VideoCamera",e.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",e.DeviceDiscovery="DeviceDiscovery",e.DeviceCreator="DeviceCreator",e.Battery="Battery",e.Refresh="Refresh",e.MediaPlayer="MediaPlayer",e.Online="Online",e.SoftwareUpdate="SoftwareUpdate",e.BufferConverter="BufferConverter",e.Settings="Settings",e.BinarySensor="BinarySensor",e.IntrusionSensor="IntrusionSensor",e.PowerSensor="PowerSensor",e.AudioSensor="AudioSensor",e.MotionSensor="MotionSensor",e.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.Readme="Readme",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector",e.ObjectDetection="ObjectDetection",e.HumiditySetting="HumiditySetting",e.Fan="Fan"}(u||(t.ScryptedInterface=u={})),t.ScryptedMimeTypes=l,function(e){e.AcceptUrlParameter="accept-url",e.Url="text/x-uri",e.InsecureLocalUrl="text/x-insecure-local-uri",e.LocalUrl="text/x-local-uri",e.PushEndpoint="text/x-push-endpoint",e.MediaStreamUrl="text/x-media-url",e.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer"}(l||(t.ScryptedMimeTypes=l={}));t.SCRYPTED_MEDIA_SCHEME="scryped-media://"},568:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,i){var n,o;function s(e){const n=o=>{const s=o.toString();for(const e of r)if(-1!==s.indexOf(e))return;if(!i&&(-1!==s.indexOf("frame=")||-1!==s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",n),void t.stderr.removeListener("data",n);e(s)};return n}null===(n=t.stdout)||void 0===n||n.on("data",s(e.log)),null===(o=t.stderr)||void 0===o||o.on("data",s(e.error)),t.on("exit",(()=>e.log("ffmpeg exited")))},t.probeVideoCamera=async function(e){let t;try{t=await e.getVideoStreamOptions()||[]}catch(e){}const r=t&&t.length&&null===t[0].audio;return{options:t,noAudio:r}};const r=["decode_slice_header error","no frame!","non-existing PPS"]},81:e=>{"use strict";e.exports=require("child_process")},361:e=>{"use strict";e.exports=require("events")},808:e=>{"use strict";e.exports=require("net")}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}var i={};(()=>{"use strict";var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=l(r(510)),n=l(r(361)),o=r(567),s=r(201),a=r(833),c=r(129),d=r(454);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(u=function(e){return e?r:t})(e)}function l(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=u(t);if(r&&r.has(e))return r.get(e);var i={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var s=n?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(i,o,s):i[o]=e[o]}return i.default=e,r&&r.set(e,i),i}function p(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const{mediaManager:m,log:f,systemManager:h,deviceManager:g}=t.default,v=1e4,y="prebufferDuration",S="sendKeyframe",b="Default",M="MP2/MP3 Audio",P="Other Audio",D="PCM or G.711 Audio",O=`${D} (Copy, Unstable)`,x=["aac","mp3","mp2","AAC","MP3","MP2","",void 0,null];class PrebufferSession{constructor(e,t,r){p(this,"prebuffers",{mp4:[],mpegts:[],s16le:[]}),p(this,"events",new n.default),p(this,"detectedIdrInterval",0),p(this,"prevIdr",0),p(this,"incompatibleDetected",!1),p(this,"legacyDetected",!1),p(this,"audioDisabled",!1),p(this,"AUDIO_CONFIGURATION","audioConfiguration-"+this.streamId),this.mixin=e,this.streamName=t,this.streamId=r,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice}clearPrebuffers(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[]}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log("prebuffer session started",this.streamId),this.parserSessionPromise=this.startPrebufferSession(),this.parserSessionPromise.catch((()=>this.parserSessionPromise=void 0)))}getAudioConfig(){const e=this.storage.getItem(this.AUDIO_CONFIGURATION)||"",t=-1!==e.indexOf(D),r=-1!==e.indexOf(M),i=-1!==e.indexOf(P);return{audioConfig:e,pcmAudio:t,legacyAudio:r,reencodeAudio:i}}async getMixinSettings(){var e,t,r,i,n;const o=[],s=this.parserSession;let a=0,c=0;for(const e of this.prebuffers.mp4){c=c||e.time;for(const t of e.chunk.chunks)a+=t.byteLength}const d=Date.now()-c,u=Math.round(a/d*8),l=this.streamName?`Rebroadcast: ${this.streamName}`:"Rebroadcast";return o.push({title:"Audio Codec Transcoding",group:l,description:"Configuring your camera to output AAC, MP3, or MP2 is recommended. PCM/G711 cameras should set this to Reencode.",type:"string",key:this.AUDIO_CONFIGURATION,value:this.storage.getItem(this.AUDIO_CONFIGURATION)||b,choices:[b,"AAC or No Audio (Copy)","MP2/MP3 Audio (Copy)","Other Audio (Transcode)",O]},{key:"detectedResolution",group:l,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==s||null===(e=s.inputVideoResolution)||void 0===e?void 0:e[0])||"unknown"} @ ${u||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080, 2000Kb/S, Variable Bit Rate, is recommended."},{key:"detectedCodec",group:l,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==s||null===(t=s.inputVideoCodec)||void 0===t?void 0:t.toString())||"unknown")+"/"+((null==s||null===(r=s.inputAudioCodec)||void 0===r?void 0:r.toString())||"unknown"),description:"Configuring your camera to H264 video and AAC/MP3/MP2 audio is recommended."},{key:"detectedKeyframe",group:l,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR aka Frame Interval = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"},{group:l,key:"rebroadcastUrl",title:"Rebroadcast Url",readonly:!0,value:null===(i=this.parserSession)||void 0===i||null===(n=i.ffmpegInputs)||void 0===n?void 0:n.mpegts.url}),o}async startPrebufferSession(){var e,r,i,n;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[];const o=parseInt(this.storage.getItem(y))||v,d=await(0,a.probeVideoCamera)(this.mixinDevice);let u;d.options&&(u=d.options.find((e=>e.id===this.streamId)));const l=null==d||null===(e=d.options)||void 0===e||null===(r=e[0])||void 0===r||null===(i=r.audio)||void 0===i?void 0:i.codec;this.incompatibleDetected=this.incompatibleDetected||l&&!x.includes(l),this.incompatibleDetected&&this.console.warn("configure your camera to output aac, mp3, or mp2 audio. incompatible audio codec detected",l);const p=await this.mixinDevice.getVideoStream(u),h=await m.convertMediaObjectToBuffer(p,t.ScryptedMimeTypes.FFmpegInput),g=JSON.parse(h.toString()),{audioConfig:S,pcmAudio:M,reencodeAudio:P,legacyAudio:D}=this.getAudioConfig(),O=!S||S===b,w=this.incompatibleDetected&&O;let I;this.audioDisabled=!1,d.noAudio||w?(I=["-an"],this.audioDisabled=!0):I=M?["-an"]:P?["-bsf:a","aac_adtstoasc","-acodec","libfdk_aac","-profile:a","aac_low","-flags","+global_header","-ar","8k","-b:a","100k","-ac","1"]:["-acodec","copy",...D||this.legacyDetected?[]:["-bsf:a","aac_adtstoasc"]];const A=["-vcodec","copy"],C={console:this.console,parsers:{mp4:(0,c.createFragmentedMp4Parser)({vcodec:A,acodec:I}),mpegts:(0,c.createMpegTsParser)({vcodec:A,acodec:I})}};d.noAudio||w||!M||(C.parsers.s16le=(0,c.createPCMParser)()),this.parsers=C.parsers,g.inputArguments.unshift("-fflags","+genpts");const k=await(0,s.startRebroadcastSession)(g,C);let _;this.parserSession=k;const T=()=>{clearTimeout(_),_=setTimeout((()=>{this.console.error("watchdog for mp4 parser timed out... killing ffmpeg session"),k.kill()}),6e4)};k.events.on("mp4-data",T),k.events.once("killed",(()=>{this.parserSessionPromise=void 0,k.events.removeListener("mp4-data",T),clearTimeout(_)})),T(),k.inputAudioCodec?x.includes(k.inputAudioCodec)?"aac"!==(null===(n=k.inputAudioCodec)||void 0===n?void 0:n.toLowerCase())&&(this.console.error("Detected audio codec was not AAC.",k.inputAudioCodec),D||(f.a(`${this.mixin.name} is using ${k.inputAudioCodec} audio. Enable MP2/MP3 Audio in Rebroadcast Settings Audio Configuration to suppress this alert.`),this.legacyDetected=!0)):(this.console.error("Detected audio codec is not mp4/mpegts compatible.",k.inputAudioCodec),O&&!d.noAudio&&(f.a(`${this.mixin.name} is using the ${k.inputAudioCodec} audio codec and has had its audio disabled. Select Disable Audio on your Camera or select Reencode Audio in Rebroadcast Settings Audio Configuration to suppress this alert.`),this.incompatibleDetected=!0)):this.console.warn("no audio detected."),"h264"!==k.inputVideoCodec&&this.console.error("video codec is not h264. If there are errors, try changing your camera's encoder output.");for(const e of["mpegts","mp4","s16le"]){const t=e+"-data";let r=this.prebuffers[e],i=0;k.events.on(t,(n=>{const s=Date.now();for("mdat"===n.type&&(this.prevIdr&&(this.detectedIdrInterval=s-this.prevIdr),this.prevIdr=s),r.push({time:s,chunk:n});r.length&&r[0].time<s-o;)r.shift(),i++;i>1e3&&(r=this.prebuffers[e]=r.slice(),i=0),this.events.emit(t,n)}))}return k}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem(S),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);if(!(null!=e&&e.prebuffer||n)){return m.createFFmpegMediaObject(i.ffmpegInputs.mpegts)}this.console.log("prebuffer request started",this.streamId);const a=async e=>{const t=e+"-data",r=this.prebuffers[e],{server:n,port:a}=await(0,s.createRebroadcaster)({connect:(e,s)=>{n.close();const a=Date.now(),c=t=>{e(t)>1e8&&(this.console.log("more than 100MB has been buffered, did downstream die? killing connection."),d())},d=()=>{s(),this.console.log("prebuffer request ended"),this.events.removeListener(t,c),i.events.removeListener("killed",d)};this.events.on(t,c),i.events.once("killed",d);for(const e of r)e.time<a-o||c(e.chunk);return d}});return setTimeout((()=>n.close()),3e4),a},c=(null==e?void 0:e.container)||"mpegts",d=i.ffmpegInputs[c].mediaStreamOptions?Object.assign({},i.ffmpegInputs[c].mediaStreamOptions):{};d.prebuffer=o;const{audioConfig:u,pcmAudio:l,reencodeAudio:p}=this.getAudioConfig();this.audioDisabled?d.audio=null:d.audio=p?{codec:"aac",encoder:"libfdk_aac",profile:"aac_eld"}:{codec:null==i?void 0:i.inputAudioCodec},d.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(d.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const f=Date.now();let h=0;const g=this.prebuffers[c];for(const e of g)if(!(e.time<f-o))for(const t of e.chunk.chunks)h+=t.length;const v=Math.max(5e5,h).toString(),y=`tcp://127.0.0.1:${await a(c)}`,b={url:y,container:c,inputArguments:["-analyzeduration","0","-probesize",v,"-f",c,"-i",y],mediaStreamOptions:d};l&&b.inputArguments.push("-analyzeduration","0","-probesize",v,"-f","s16le","-i",`tcp://127.0.0.1:${await a("s16le")}`);return m.createFFmpegMediaObject(b)}}class PrebufferMixin extends o.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),p(this,"released",!1),p(this,"sessions",new Map),this.delayStart()}delayStart(){this.console.log("prebuffer sessions starting in 5 seconds"),setTimeout((()=>this.ensurePrebufferSessions()),5e3)}async getVideoStream(e){await this.ensurePrebufferSessions();let r=null==e?void 0:e.id;if(!r&&!this.sessions.has(r)){var i;const n=await this.mixinDevice.getVideoStream(e),o=JSON.parse((await m.convertMediaObjectToBuffer(n,t.ScryptedMimeTypes.FFmpegInput)).toString());r=null==o||null===(i=o.mediaStreamOptions)||void 0===i?void 0:i.id,this.sessions.set(null==e?void 0:e.id,this.sessions.get(r))}let n=this.sessions.get(r);return n?(n.ensurePrebufferSession(),await n.parserSessionPromise,n=this.sessions.get(r),n?n.getVideoStream(e):this.mixinDevice.getVideoStream(e)):this.mixinDevice.getVideoStream(e)}async ensurePrebufferSessions(){const e=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(e),i=r?r.map((e=>e.id)):[void 0];let o=0;const s=i.length;for(const t of i){let r=this.sessions.get(t);if(!r){const i=null==e?void 0:e.find((e=>e.id===t));null!=i&&i.prebuffer&&f.a(`Prebuffer is already available on ${this.name}. If this is a grouped device, disable the Rebroadcast extension.`);const a=null==i?void 0:i.name;r=new PrebufferSession(this,a,t),this.sessions.set(t,r),(async()=>{for(;this.sessions.get(t)===r&&!this.released;){r.ensurePrebufferSession();try{const e=await r.parserSessionPromise;o++,this.online=o==s,await(0,n.once)(e.events,"killed"),this.console.error("prebuffer session ended")}catch(e){this.console.error("prebuffer session ended with error",e)}finally{o--,this.online=o==s}this.console.log("restarting prebuffer session in 5 seconds"),await new Promise((e=>setTimeout(e,5e3)))}this.console.log("exiting prebuffer session (released or restarted with new configuration)")})()}}g.onMixinEvent(this.id,this.mixinProviderNativeId,t.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[];try{const t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);r&&(null==t?void 0:t.length)>1&&e.push({title:"Prebuffered Streams",description:"The streams to prebuffer. Enable only as necessary to reduce traffic.",key:"enabledStreams",value:r.map((e=>e.name||"")),choices:t.map((e=>e.name)),multiple:!0})}catch(e){throw this.console.error("error in getVideoStreamOptions",e),e}e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:y,value:this.storage.getItem(y)||v.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:S,value:("false"!==this.storage.getItem(S)).toString()});for(const t of new Set([...this.sessions.values()]))if(t)try{e.push(...await t.getMixinSettings())}catch(e){throw this.console.error("error in prebuffer session getMixinSettings",e),e}return e}async putMixinSetting(e,t){const r=this.sessions;this.sessions=new Map,"enabledStreams"===e?this.storage.setItem(e,JSON.stringify(t)):this.storage.setItem(e,t.toString());for(const e of r.values()){var i;null==e||null===(i=e.parserSessionPromise)||void 0===i||i.then((e=>e.kill()))}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(e&&e.length){try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter((e=>t.includes(e.name)))}catch(e){}return[e[0]]}}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem(y))||v;if(t)for(const e of t)e.prebuffer=r;else e.push({prebuffer:r});return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;t&&(t.clearPrebuffers(),null===(e=t.parserSessionPromise)||void 0===e||e.then((e=>{this.console.log("prebuffer released"),e.kill(),t.clearPrebuffers()})))}}}class PrebufferProvider extends d.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(h.getSystemState())){var t;const r=h.getDeviceById(e);null!==(t=r.mixins)&&void 0!==t&&t.includes(this.id)&&r.getVideoStreamOptions()}const r=function(){var e=new Date;return e.setHours(24),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0),e.getTime()-(new Date).getTime()}()+72e5;this.log.i(`Rebroadcaster scheduled for restart at 2AM: ${Math.round(r/1e3/60)} minutes`),setTimeout((()=>g.requestRestart()),r)}async canMixin(e,r){return r.includes(t.ScryptedInterface.VideoCamera)?[t.ScryptedInterface.VideoCamera,t.ScryptedInterface.Settings,t.ScryptedInterface.Online]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new PrebufferMixin(e,t,r,this.nativeId)}async releaseMixin(e,t){t.online=!0,t.release()}}var w=new PrebufferProvider;e.default=w})();var n=exports="undefined"==typeof exports?{}:exports;for(var o in i)n[o]=i[o];i.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})})();
|
|
1
|
+
(()=>{var e={454:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{systemManager:o}=i.default,s="v4";class AutoenableMixinProvider extends i.ScryptedDeviceBase{constructor(e){var t,r,n;super(e),n={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=o.getComponent("plugins"),o.listen((async(e,t,r)=>{t.eventInterface!==i.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)}));for(const e of Object.keys(o.getSystemState())){const t=o.getDeviceById(e);this.maybeEnableMixin(t)}}async shouldEnableMixin(e){return!0}async maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id]===s)return;if(!await this.canMixin(e.type,e.interfaces))return;if(!await this.shouldEnableMixin(e))return;this.log.i("auto enabling mixin for "+e.name);const r=(e.mixins||[]).slice();r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r),this.setHasEnabledMixin(e.id)}setHasEnabledMixin(e){this.hasEnabledMixin[e]!==s&&(this.hasEnabledMixin[e]=s,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=AutoenableMixinProvider},201:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createRebroadcaster=g,t.parseAudioCodec=h,t.parseResolution=p,t.parseVideoCodec=f,t.safePrintFFmpegArguments=l,t.startRebroadcastSession=async function(e,t){let r,a,d=0,m=!0;const v=new s.EventEmitter;v.on("error",(e=>y.error("rebroadcast error",e)));const{console:y}=t;let S,b,M,P,O;const D=new Promise(((e,t)=>{P=e,O=t}));function x(){m&&(v.emit("killed"),v.emit("error",new Error("killed"))),m=!1,null==k||k.kill(),null==k||k.kill("SIGKILL");for(const e of A)null==e||e.close();O(new Error("ffmpeg was killed before connecting to the rebroadcast session")),clearTimeout(r),clearTimeout(a)}function w(){t.timeout&&(clearTimeout(r),r=setTimeout(x,t.timeout))}w();const I={},C=e.inputArguments.slice(),A=[];a=setTimeout(x,3e4);for(const n of Object.keys(t.parsers)){const s=t.parsers[n],a=n+"-data";if(t.parseOnly)I[n]={url:void 0,mediaStreamOptions:e.mediaStreamOptions};else{const{server:t,port:i}=await g({connect:(e,t)=>{d++,clearTimeout(r);const i=()=>{v.removeListener(a,e),v.removeListener("killed",t),d--,0===d&&w(),t()};return v.on(a,e),v.once("killed",i),i}});A.push(t);const o=`tcp://127.0.0.1:${i}`;I[n]={url:o,mediaStreamOptions:e.mediaStreamOptions,inputArguments:["-f",n,"-i",o]}}const c=(0,i.createServer)((async e=>{c.close(),P(e);try{const i=n+"-data";for await(const n of s.parse(e,parseInt(null===(t=M)||void 0===t?void 0:t[2]),parseInt(null===(r=M)||void 0===r?void 0:r[3]))){var t,r;v.emit(i,n)}}catch(e){y.error("rebroadcast parse error",e),x()}}));A.push(c);const u=await(0,o.listenZero)(c);C.push(...s.outputArguments,`tcp://127.0.0.1:${u}`)}C.unshift("-hide_banner"),l(y,C);const k=n.default.spawn(await u.getFFmpegPath(),C);return(0,c.ffmpegLogInitialOutput)(y,k),k.on("exit",x),h(k).then((e=>S=e)),f(k).then((e=>b=e)),p(k).then((e=>M=e)),await D,clearTimeout(a),{inputAudioCodec:S,inputVideoCodec:b,inputVideoResolution:M,events:v,resetActivityTimer:w,isActive:()=>m,kill:x,servers:A,cp:k,ffmpegInputs:I}};var i=r(808),n=d(r(81)),o=r(769),s=r(361),a=d(r(510)),c=r(833);function d(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:u}=a.default;function l(e,t){const r=[];for(const e of t)try{const t=new URL(e);t.password?(t.password="REDACTED",r.push(t.toString())):r.push(e)}catch(t){r.push(e)}e.log(r.join(" "))}async function p(e){return new Promise((t=>{const r=i=>{const n=i.toString(),o=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(n);o&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(o))};e.stdout.on("data",r),e.stderr.on("data",r)}))}async function m(e,t){return new Promise((r=>{const i=n=>{const o=n.toString(),s=o.indexOf(`${t}: `);if(-1!==s){const n=o.substring(s+t.length+1).trim();let a=n.indexOf(" ");const c=n.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",i),e.stderr.removeListener("data",i),r(n.substring(0,a)))}};e.stdout.on("data",i),e.stderr.on("data",i)}))}async function f(e){return m(e,"Video")}async function h(e){return m(e,"Audio")}async function g(e){const t=(0,i.createServer)((t=>{let r=!0;const i=()=>{t.removeAllListeners(),t.destroy();const e=n;n=void 0,null==e||e()};let n=null==e?void 0:e.connect((e=>{r&&(r=!1,e.startStream&&t.write(e.startStream));for(const r of e.chunks)t.write(r);return t.writableLength}),i);t.on("end",i),t.on("close",i),t.on("error",i)}));return{server:t,port:await(0,o.listenZero)(t)}}},769:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bindZero=async function(e){return e.bind(0),await(0,o.once)(e,"listening"),e.address().port},t.listenZero=s,t.listenZeroSingleClient=async function(){const e=new n.default.Server,t=await s(e),r=new Promise(((t,r)=>{const i=setTimeout((()=>{r(new Error("timeout waiting for client"))}),3e4);e.on("connection",(r=>{e.close(),clearTimeout(i),t(r)}))}));return{url:`tcp://127.0.0.1:${t}`,port:t,clientPromise:r}};var i,n=(i=r(808))&&i.__esModule?i:{default:i},o=r(361);async function s(e){return e.listen(0),await(0,o.once)(e,"listening"),e.address().port}},833:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(568);Object.keys(i).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===i[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return i[e]}}))}))},701:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=async function(e,t){if(!t)return Buffer.alloc(0);{const r=e.read(t);if(r)return r}return new Promise(((r,i)=>{const n=()=>{const i=e.read(t);i&&(s(),r(i))},o=()=>{s(),i(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",n),e.removeListener("end",o)};e.on("readable",n),e.on("end",o)}))}},567:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{deviceManager:o}=i.default;class SettingsMixinDeviceBase extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId,r.mixinStorageSuffix),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey,process.nextTick((()=>o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)))}async getSettings(){const e=this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?this.mixinDevice.getSettings():void 0,t=this.getMixinSettings(),r=[];try{const t=await e||[];r.push(...t)}catch(e){const t=this.name;r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:`${t} Extension settings failed to load.`,readonly:!0})}try{const e=await t||[];for(const t of e)t.group=t.group||this.settingsGroup,t.key=this.settingsGroupKey+":"+t.key;r.push(...e)}catch(e){const t=o.getDeviceState(this.mixinProviderNativeId).name;r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:`${t} Extension settings failed to load.`,readonly:!0})}return r}async putSetting(e,t){const r=this.settingsGroupKey+":";if(null==e||!e.startsWith(r))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(r.length),t),o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}release(){o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=SettingsMixinDeviceBase},129:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PIXEL_FORMAT_YUV420P=t.PIXEL_FORMAT_RGB24=void 0,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof","-f","mp4"],async*parse(e){const t=a(e);let r,i,n;for await(const e of t)r?i||(i=e):r=e,yield{startStream:n,chunks:[e.header,e.data],type:e.type},r&&i&&!n&&(n=Buffer.concat([r.header,r.data,i.header,i.data]))},findSyncFrame:o}},t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-f","mpegts"],parse:s(188,(e=>{if(71!=e[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.")})),findSyncFrame(e){for(let t=0;t<e.length;t++){const r=e[t];for(let i=0;i<r.chunks.length;i++){const n=r.chunks[i];let o=0;for(;o+188<n.length;){const r=n.subarray(o,o+188);if(256==((31&r[1])<<8|r[2])&&32&r[3]&&r[4]>0&&64&r[5])return e.slice(t);o+=188}}}return e}}},t.createPCMParser=function(){return{container:"s16le",outputArguments:["-vn","-acodec","pcm_s16le","-f","s16le"],parse:s(512),findSyncFrame:o}},t.createRawVideoParser=function(e){var t;const r=(null===(t=e)||void 0===t?void 0:t.pixelFormat)||c;let i;e=e||{};const{size:s,everyNFrames:a}=e;s&&(i=`scale=${s.width}:${s.height}`);a&&a>1&&(i?i+=",":i="",i+=`select=not(mod(n\\,${a}))`);return{container:"rawvideo",outputArguments:[...i?["-vf",i]:[],"-an","-vcodec","rawvideo","-pix_fmt",r.name,"-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");t=(null==s?void 0:s.width)||t,i=(null==s?void 0:s.height)||i;const o=r.computeLength(t,i);for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}},t.parseFragmentedMP4=a;var i=r(361),n=r(701);function o(e){return e}function s(e,t){return async function*(r){let n=[],o=0;for(;;){const s=r.read();if(!s){await(0,i.once)(r,"readable");continue}if(n.push(s),o+=s.length,o<e)continue;const a=Buffer.concat(n);null==t||t(a);const c=a.length%e,d=a.slice(0,a.length-c),u=a.slice(a.length-c);n=[u],o=u.length,yield{chunks:[d]}}}}async function*a(e){for(;;){const t=await(0,n.readLength)(e,8),r=t.readInt32BE(0)-8,i=t.slice(4).toString(),o=await(0,n.readLength)(e,r);yield{header:t,length:r,type:i,data:o}}}const c={name:"yuv420p",computeLength:(e,t)=>e*t*1.5};t.PIXEL_FORMAT_YUV420P=c;t.PIXEL_FORMAT_RGB24={name:"rgb24",computeLength:(e,t)=>e*t*3}},510:(e,t,r)=>{"use strict";var i=Object.create?function(e,t,r,i){void 0===i&&(i=r),Object.defineProperty(e,i,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]},n=function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MixinDeviceBase=t.ScryptedDeviceBase=void 0,n(r(393),t);const o=r(393);class ScryptedDeviceBase extends o.DeviceBase{constructor(e){super(),this.nativeId=e}get storage(){return this._storage||(this._storage=deviceManager.getDeviceStorage(this.nativeId)),this._storage}get log(){return this._log||(this._log=deviceManager.getDeviceLogger(this.nativeId)),this._log}get console(){return this._console||(this._console=deviceManager.getDeviceConsole(this.nativeId)),this._console}_lazyLoadDeviceState(){this._deviceState||(this.nativeId?this._deviceState=deviceManager.getDeviceState(this.nativeId):this._deviceState=deviceManager.getDeviceState())}onDeviceEvent(e,t){return deviceManager.onDeviceEvent(this.nativeId,e,t)}}t.ScryptedDeviceBase=ScryptedDeviceBase;class MixinDeviceBase extends o.DeviceBase{constructor(e,t,r,i,n){super(),this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._mixinStorageSuffix=n,this._listeners=new Set,this._deviceState=r}get storage(){if(!this._storage){const e=this._mixinStorageSuffix,t=this.id+(e?":"+e:"");this._storage=deviceManager.getMixinStorage(t,this.mixinProviderNativeId)}return this._storage}get console(){return this._console||(deviceManager.getMixinConsole?this._console=deviceManager.getMixinConsole(this.id,this.mixinProviderNativeId):this._console=deviceManager.getDeviceConsole(this.mixinProviderNativeId)),this._console}onDeviceEvent(e,t){return deviceManager.onMixinEvent(this.id,this,e,t)}_lazyLoadDeviceState(){}manageListener(e){this._listeners.add(e)}release(){for(const e of this._listeners)e.removeListener()}}t.MixinDeviceBase=MixinDeviceBase,function(){function e(e){return function(){return this._lazyLoadDeviceState(),this._deviceState[e]}}function t(e){return function(t){this._lazyLoadDeviceState(),this._deviceState[e]=t}}for(var r of Object.values(o.ScryptedInterfaceProperty))Object.defineProperty(ScryptedDeviceBase.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(MixinDeviceBase.prototype,r,{set:t(r),get:e(r)})}();let s={};try{s=Object.assign(s,{log:deviceManager.getDeviceLogger(void 0),deviceManager,endpointManager,mediaManager,systemManager,pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/sdk/types instead",e)}t.default=s},393:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ThermostatMode=t.TemperatureUnit=t.ScryptedMimeTypes=t.ScryptedInterfaceProperty=t.ScryptedInterfaceDescriptors=t.ScryptedInterface=t.ScryptedDeviceType=t.SCRYPTED_MEDIA_SCHEME=t.MediaPlayerState=t.LockState=t.HumidityMode=t.FanMode=t.DeviceBase=void 0;let r;t.DeviceBase=class DeviceBase{},t.ScryptedInterfaceProperty=r,function(e){e.id="id",e.info="info",e.interfaces="interfaces",e.mixins="mixins",e.name="name",e.providedInterfaces="providedInterfaces",e.providedName="providedName",e.providedRoom="providedRoom",e.providedType="providedType",e.providerId="providerId",e.room="room",e.type="type",e.on="on",e.brightness="brightness",e.colorTemperature="colorTemperature",e.rgb="rgb",e.hsv="hsv",e.running="running",e.paused="paused",e.docked="docked",e.thermostatActiveMode="thermostatActiveMode",e.thermostatAvailableModes="thermostatAvailableModes",e.thermostatMode="thermostatMode",e.thermostatSetpoint="thermostatSetpoint",e.thermostatSetpointHigh="thermostatSetpointHigh",e.thermostatSetpointLow="thermostatSetpointLow",e.temperature="temperature",e.temperatureUnit="temperatureUnit",e.humidity="humidity",e.lockState="lockState",e.entryOpen="entryOpen",e.batteryLevel="batteryLevel",e.online="online",e.updateAvailable="updateAvailable",e.fromMimeType="fromMimeType",e.toMimeType="toMimeType",e.binaryState="binaryState",e.intrusionDetected="intrusionDetected",e.powerDetected="powerDetected",e.audioDetected="audioDetected",e.motionDetected="motionDetected",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position",e.humiditySetting="humiditySetting",e.fan="fan"}(r||(t.ScryptedInterfaceProperty=r={}));let i,n,o,s,a,c,d,u,l;t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",methods:["listen","probe","setName","setRoom","setType"],properties:["id","info","interfaces","mixins","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"]},OnOff:{name:"OnOff",methods:["turnOff","turnOn"],properties:["on"]},Brightness:{name:"Brightness",methods:["setBrightness"],properties:["brightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"],properties:["colorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",methods:["setRgb"],properties:["rgb"]},ColorSettingHsv:{name:"ColorSettingHsv",methods:["setHsv"],properties:["hsv"]},Notifier:{name:"Notifier",methods:["sendNotification"],properties:[]},StartStop:{name:"StartStop",methods:["start","stop"],properties:["running"]},Pause:{name:"Pause",methods:["pause","resume"],properties:["paused"]},Dock:{name:"Dock",methods:["dock"],properties:["docked"]},TemperatureSetting:{name:"TemperatureSetting",methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"],properties:["thermostatActiveMode","thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"]},Thermometer:{name:"Thermometer",methods:["setTemperatureUnit"],properties:["temperature","temperatureUnit"]},HumiditySensor:{name:"HumiditySensor",methods:[],properties:["humidity"]},Camera:{name:"Camera",methods:["getPictureOptions","takePicture"],properties:[]},VideoCamera:{name:"VideoCamera",methods:["getVideoStream","getVideoStreamOptions"],properties:[]},VideoCameraConfiguration:{name:"VideoCameraConfiguration",methods:["setVideoStreamOptions"],properties:[]},Intercom:{name:"Intercom",methods:["startIntercom","stopIntercom"],properties:[]},Lock:{name:"Lock",methods:["lock","unlock"],properties:["lockState"]},PasswordStore:{name:"PasswordStore",methods:["addPassword","getPasswords","removePassword"],properties:[]},Authenticator:{name:"Authenticator",methods:["checkPassword"],properties:[]},Scene:{name:"Scene",methods:["activate","deactivate","isReversible"],properties:[]},Entry:{name:"Entry",methods:["closeEntry","openEntry"],properties:[]},EntrySensor:{name:"EntrySensor",methods:[],properties:["entryOpen"]},DeviceProvider:{name:"DeviceProvider",methods:["getDevice"],properties:[]},DeviceDiscovery:{name:"DeviceDiscovery",methods:["discoverDevices"],properties:[]},DeviceCreator:{name:"DeviceCreator",methods:["createDevice","getCreateDeviceSettings"],properties:[]},Battery:{name:"Battery",methods:[],properties:["batteryLevel"]},Refresh:{name:"Refresh",methods:["getRefreshFrequency","refresh"],properties:[]},MediaPlayer:{name:"MediaPlayer",methods:["getMediaStatus","load","seek","skipNext","skipPrevious"],properties:[]},Online:{name:"Online",methods:[],properties:["online"]},SoftwareUpdate:{name:"SoftwareUpdate",methods:["checkForUpdate","installUpdate"],properties:["updateAvailable"]},BufferConverter:{name:"BufferConverter",methods:["convert"],properties:["fromMimeType","toMimeType"]},Settings:{name:"Settings",methods:["getSettings","putSetting"],properties:[]},BinarySensor:{name:"BinarySensor",methods:[],properties:["binaryState"]},IntrusionSensor:{name:"IntrusionSensor",methods:[],properties:["intrusionDetected"]},PowerSensor:{name:"PowerSensor",methods:[],properties:["powerDetected"]},AudioSensor:{name:"AudioSensor",methods:[],properties:["audioDetected"]},MotionSensor:{name:"MotionSensor",methods:[],properties:["motionDetected"]},OccupancySensor:{name:"OccupancySensor",methods:[],properties:["occupied"]},FloodSensor:{name:"FloodSensor",methods:[],properties:["flooded"]},UltravioletSensor:{name:"UltravioletSensor",methods:[],properties:["ultraviolet"]},LuminanceSensor:{name:"LuminanceSensor",methods:[],properties:["luminance"]},PositionSensor:{name:"PositionSensor",methods:[],properties:["position"]},Readme:{name:"Readme",methods:["getReadmeMarkdown"],properties:[]},OauthClient:{name:"OauthClient",methods:["getOauthUrl","onOauthCallback"],properties:[]},MixinProvider:{name:"MixinProvider",methods:["canMixin","getMixin","releaseMixin"],properties:[]},HttpRequestHandler:{name:"HttpRequestHandler",methods:["onRequest"],properties:[]},EngineIOHandler:{name:"EngineIOHandler",methods:["onConnection"],properties:[]},PushHandler:{name:"PushHandler",methods:["onPush"],properties:[]},Program:{name:"Program",methods:["run"],properties:[]},Scriptable:{name:"Scriptable",methods:["eval","loadScripts","saveScript"],properties:[]},ObjectDetector:{name:"ObjectDetector",methods:["getDetectionInput","getObjectTypes"],properties:[]},ObjectDetection:{name:"ObjectDetection",methods:["detectObjects","getDetectionModel"],properties:[]},HumiditySetting:{name:"HumiditySetting",methods:["setHumidity"],properties:["humiditySetting"]},Fan:{name:"Fan",methods:["setFan"],properties:["fan"]}},t.ScryptedDeviceType=i,function(e){e.Builtin="Builtin",e.Camera="Camera",e.Fan="Fan",e.Light="Light",e.Switch="Switch",e.Outlet="Outlet",e.Sensor="Sensor",e.Scene="Scene",e.Program="Program",e.Automation="Automation",e.Vacuum="Vacuum",e.Notifier="Notifier",e.Thermostat="Thermostat",e.Lock="Lock",e.PasswordControl="PasswordControl",e.Display="Display",e.Speaker="Speaker",e.Event="Event",e.Entry="Entry",e.Garage="Garage",e.DeviceProvider="DeviceProvider",e.DataSource="DataSource",e.API="API",e.Doorbell="Doorbell",e.Irrigation="Irrigation",e.Valve="Valve",e.Person="Person",e.Unknown="Unknown"}(i||(t.ScryptedDeviceType=i={})),t.HumidityMode=n,function(e){e.Humidify="Humidify",e.Dehumidify="Dehumidify",e.Auto="Auto",e.Off="Off"}(n||(t.HumidityMode=n={})),t.FanMode=o,function(e){e.Auto="Auto",e.Manual="Manual"}(o||(t.FanMode=o={})),t.TemperatureUnit=s,function(e){e.C="C",e.F="F"}(s||(t.TemperatureUnit=s={})),t.ThermostatMode=a,function(e){e.Off="Off",e.Cool="Cool",e.Heat="Heat",e.HeatCool="HeatCool",e.Auto="Auto",e.FanOnly="FanOnly",e.Purifier="Purifier",e.Eco="Eco",e.Dry="Dry",e.On="On"}(a||(t.ThermostatMode=a={})),t.LockState=c,function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(c||(t.LockState=c={})),t.MediaPlayerState=d,function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(d||(t.MediaPlayerState=d={})),t.ScryptedInterface=u,function(e){e.ScryptedDevice="ScryptedDevice",e.OnOff="OnOff",e.Brightness="Brightness",e.ColorSettingTemperature="ColorSettingTemperature",e.ColorSettingRgb="ColorSettingRgb",e.ColorSettingHsv="ColorSettingHsv",e.Notifier="Notifier",e.StartStop="StartStop",e.Pause="Pause",e.Dock="Dock",e.TemperatureSetting="TemperatureSetting",e.Thermometer="Thermometer",e.HumiditySensor="HumiditySensor",e.Camera="Camera",e.VideoCamera="VideoCamera",e.VideoCameraConfiguration="VideoCameraConfiguration",e.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",e.DeviceDiscovery="DeviceDiscovery",e.DeviceCreator="DeviceCreator",e.Battery="Battery",e.Refresh="Refresh",e.MediaPlayer="MediaPlayer",e.Online="Online",e.SoftwareUpdate="SoftwareUpdate",e.BufferConverter="BufferConverter",e.Settings="Settings",e.BinarySensor="BinarySensor",e.IntrusionSensor="IntrusionSensor",e.PowerSensor="PowerSensor",e.AudioSensor="AudioSensor",e.MotionSensor="MotionSensor",e.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.Readme="Readme",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector",e.ObjectDetection="ObjectDetection",e.HumiditySetting="HumiditySetting",e.Fan="Fan"}(u||(t.ScryptedInterface=u={})),t.ScryptedMimeTypes=l,function(e){e.AcceptUrlParameter="accept-url",e.Url="text/x-uri",e.InsecureLocalUrl="text/x-insecure-local-uri",e.LocalUrl="text/x-local-uri",e.PushEndpoint="text/x-push-endpoint",e.MediaStreamUrl="text/x-media-url",e.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVServerPrefix="x-scrypted/x-scrypted-rtc-server-",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer"}(l||(t.ScryptedMimeTypes=l={}));t.SCRYPTED_MEDIA_SCHEME="scryped-media://"},568:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,i){var n,o;function s(e){const n=o=>{const s=o.toString();for(const e of r)if(-1!==s.indexOf(e))return;if(!i&&(-1!==s.indexOf("frame=")||-1!==s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",n),void t.stderr.removeListener("data",n);e(s)};return n}null===(n=t.stdout)||void 0===n||n.on("data",s(e.log)),null===(o=t.stderr)||void 0===o||o.on("data",s(e.error)),t.on("exit",(()=>e.log("ffmpeg exited")))},t.probeVideoCamera=async function(e){let t;try{t=await e.getVideoStreamOptions()||[]}catch(e){}const r=t&&t.length&&null===t[0].audio;return{options:t,noAudio:r}};const r=["decode_slice_header error","no frame!","non-existing PPS"]},81:e=>{"use strict";e.exports=require("child_process")},361:e=>{"use strict";e.exports=require("events")},808:e=>{"use strict";e.exports=require("net")}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}var i={};(()=>{"use strict";var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=l(r(510)),n=l(r(361)),o=r(567),s=r(201),a=r(833),c=r(129),d=r(454);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(u=function(e){return e?r:t})(e)}function l(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=u(t);if(r&&r.has(e))return r.get(e);var i={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var s=n?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(i,o,s):i[o]=e[o]}return i.default=e,r&&r.set(e,i),i}function p(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const{mediaManager:m,log:f,systemManager:h,deviceManager:g}=t.default,v=1e4,y="prebufferDuration",S="sendKeyframe",b="Default",M="MP2/MP3 Audio",P="Other Audio",O="PCM or G.711 Audio",D=`${O} (Copy, Unstable)`,x=["aac","mp3","mp2","AAC","MP3","MP2","",void 0,null];class PrebufferSession{constructor(e,t,r){p(this,"prebuffers",{mp4:[],mpegts:[],s16le:[]}),p(this,"events",new n.default),p(this,"detectedIdrInterval",0),p(this,"prevIdr",0),p(this,"incompatibleDetected",!1),p(this,"legacyDetected",!1),p(this,"audioDisabled",!1),p(this,"AUDIO_CONFIGURATION","audioConfiguration-"+this.streamId),this.mixin=e,this.streamName=t,this.streamId=r,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice}clearPrebuffers(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[]}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log("prebuffer session started",this.streamId),this.parserSessionPromise=this.startPrebufferSession(),this.parserSessionPromise.catch((()=>this.parserSessionPromise=void 0)))}getAudioConfig(){const e=this.storage.getItem(this.AUDIO_CONFIGURATION)||"",t=-1!==e.indexOf(O),r=-1!==e.indexOf(M),i=-1!==e.indexOf(P);return{audioConfig:e,pcmAudio:t,legacyAudio:r,reencodeAudio:i}}async getMixinSettings(){var e,t,r,i,n;const o=[],s=this.parserSession;let a=0,c=0;for(const e of this.prebuffers.mp4){c=c||e.time;for(const t of e.chunk.chunks)a+=t.byteLength}const d=Date.now()-c,u=Math.round(a/d*8),l=this.streamName?`Rebroadcast: ${this.streamName}`:"Rebroadcast";return o.push({title:"Audio Codec Transcoding",group:l,description:"Configuring your camera to output AAC, MP3, or MP2 is recommended. PCM/G711 cameras should set this to Reencode.",type:"string",key:this.AUDIO_CONFIGURATION,value:this.storage.getItem(this.AUDIO_CONFIGURATION)||b,choices:[b,"AAC or No Audio (Copy)","MP2/MP3 Audio (Copy)","Other Audio (Transcode)",D]},{key:"detectedResolution",group:l,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==s||null===(e=s.inputVideoResolution)||void 0===e?void 0:e[0])||"unknown"} @ ${u||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080, 2000Kb/S, Variable Bit Rate, is recommended."},{key:"detectedCodec",group:l,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==s||null===(t=s.inputVideoCodec)||void 0===t?void 0:t.toString())||"unknown")+"/"+((null==s||null===(r=s.inputAudioCodec)||void 0===r?void 0:r.toString())||"unknown"),description:"Configuring your camera to H264 video and AAC/MP3/MP2 audio is recommended."},{key:"detectedKeyframe",group:l,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR aka Frame Interval = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"},{group:l,key:"rebroadcastUrl",title:"Rebroadcast Url",readonly:!0,value:null===(i=this.parserSession)||void 0===i||null===(n=i.ffmpegInputs)||void 0===n?void 0:n.mpegts.url}),o}async startPrebufferSession(){var e,r,i,n,o;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[];const d=parseInt(this.storage.getItem(y))||v,u=await(0,a.probeVideoCamera)(this.mixinDevice);let l;u.options&&(l=u.options.find((e=>e.id===this.streamId)));const p=null==u||null===(e=u.options)||void 0===e||null===(r=e[0])||void 0===r||null===(i=r.audio)||void 0===i?void 0:i.codec;this.incompatibleDetected=this.incompatibleDetected||p&&!x.includes(p),this.incompatibleDetected&&this.console.warn("configure your camera to output aac, mp3, or mp2 audio. incompatible audio codec detected",p);const h=await this.mixinDevice.getVideoStream(l),g=await m.convertMediaObjectToBuffer(h,t.ScryptedMimeTypes.FFmpegInput),S=JSON.parse(g.toString()),{audioConfig:M,pcmAudio:P,reencodeAudio:O,legacyAudio:D}=this.getAudioConfig(),w=!M||M===b,I=this.incompatibleDetected&&w;let C;this.audioDisabled=!1,u.noAudio||I?(C=["-an"],this.audioDisabled=!0):C=P?["-an"]:O?["-bsf:a","aac_adtstoasc","-acodec","libfdk_aac","-profile:a","aac_low","-flags","+global_header","-ar","8k","-b:a","100k","-ac","1"]:["-acodec","copy",...D||this.legacyDetected?[]:["-bsf:a","aac_adtstoasc"]];const A=["-vcodec","copy"],k={console:this.console,parsers:{mp4:(0,c.createFragmentedMp4Parser)({vcodec:A,acodec:C}),mpegts:(0,c.createMpegTsParser)({vcodec:A,acodec:C})}};u.noAudio||I||!P||(k.parsers.s16le=(0,c.createPCMParser)()),this.parsers=k.parsers,S.inputArguments.unshift("-fflags","+genpts");const _=await(0,s.startRebroadcastSession)(S,k);if(this.parserSession=_,null!==(n=S.mediaStreamOptions)&&void 0!==n&&n.refreshAt){let e,r=S.mediaStreamOptions;const i=async()=>{if(!_.isActive)return;const e=await this.mixinDevice.getVideoStream(r),i=await m.convertMediaObjectToBuffer(e,t.ScryptedMimeTypes.FFmpegInput),o=JSON.parse(i.toString());r=o.mediaStreamOptions,n(o)},n=t=>{const r=t.mediaStreamOptions.refreshAt-Date.now()-3e4;this.console.log("refreshing media stream in",r),e=setTimeout(i,r)};n(S),_.events.on("killed",(()=>clearTimeout(e)))}let T;const E=()=>{clearTimeout(T),T=setTimeout((()=>{this.console.error("watchdog for mp4 parser timed out... killing ffmpeg session"),_.kill()}),6e4)};_.events.on("mp4-data",E),_.events.once("killed",(()=>{this.parserSessionPromise=void 0,_.events.removeListener("mp4-data",E),clearTimeout(T)})),E(),_.inputAudioCodec?x.includes(_.inputAudioCodec)?"aac"!==(null===(o=_.inputAudioCodec)||void 0===o?void 0:o.toLowerCase())&&(this.console.error("Detected audio codec was not AAC.",_.inputAudioCodec),D||(f.a(`${this.mixin.name} is using ${_.inputAudioCodec} audio. Enable MP2/MP3 Audio in Rebroadcast Settings Audio Configuration to suppress this alert.`),this.legacyDetected=!0)):(this.console.error("Detected audio codec is not mp4/mpegts compatible.",_.inputAudioCodec),w&&!u.noAudio&&(f.a(`${this.mixin.name} is using the ${_.inputAudioCodec} audio codec and has had its audio disabled. Select Disable Audio on your Camera or select Reencode Audio in Rebroadcast Settings Audio Configuration to suppress this alert.`),this.incompatibleDetected=!0)):this.console.warn("no audio detected."),"h264"!==_.inputVideoCodec&&this.console.error("video codec is not h264. If there are errors, try changing your camera's encoder output.");for(const e of["mpegts","mp4","s16le"]){const t=e+"-data";let r=0;_.events.on(t,(i=>{const n=this.prebuffers[e],o=Date.now();for("mdat"===i.type&&(this.prevIdr&&(this.detectedIdrInterval=o-this.prevIdr),this.prevIdr=o),n.push({time:o,chunk:i});n.length&&n[0].time<o-d;)n.shift(),r++;r>1e3&&(this.prebuffers[e]=n.slice(),r=0),this.events.emit(t,i)}))}return _}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem(S),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);if(!(null!=e&&e.prebuffer||n)){return m.createFFmpegMediaObject(i.ffmpegInputs.mpegts)}this.console.log("prebuffer request started",this.streamId);const a=async e=>{const t=e+"-data",r=this.prebuffers[e],{server:n,port:a}=await(0,s.createRebroadcaster)({connect:(e,s)=>{n.close();const a=Date.now(),c=t=>{e(t)>1e8&&(this.console.log("more than 100MB has been buffered, did downstream die? killing connection."),d())},d=()=>{s(),this.console.log("prebuffer request ended"),this.events.removeListener(t,c),i.events.removeListener("killed",d)};this.events.on(t,c),i.events.once("killed",d);for(const e of r)e.time<a-o||c(e.chunk);return d}});return setTimeout((()=>n.close()),3e4),a},c=(null==e?void 0:e.container)||"mpegts",d=i.ffmpegInputs[c].mediaStreamOptions?Object.assign({},i.ffmpegInputs[c].mediaStreamOptions):{};d.prebuffer=o;const{audioConfig:u,pcmAudio:l,reencodeAudio:p}=this.getAudioConfig();this.audioDisabled?d.audio=null:d.audio=p?{codec:"aac",encoder:"libfdk_aac",profile:"aac_eld"}:{codec:null==i?void 0:i.inputAudioCodec},d.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(d.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const f=Date.now();let h=0;const g=this.prebuffers[c];for(const e of g)if(!(e.time<f-o))for(const t of e.chunk.chunks)h+=t.length;const v=Math.max(5e5,h).toString(),y=`tcp://127.0.0.1:${await a(c)}`,b={url:y,container:c,inputArguments:["-analyzeduration","0","-probesize",v,"-f",c,"-i",y],mediaStreamOptions:d};l&&b.inputArguments.push("-analyzeduration","0","-probesize",v,"-f","s16le","-i",`tcp://127.0.0.1:${await a("s16le")}`);return m.createFFmpegMediaObject(b)}}class PrebufferMixin extends o.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),p(this,"released",!1),p(this,"sessions",new Map),this.delayStart()}delayStart(){this.console.log("prebuffer sessions starting in 5 seconds"),setTimeout((()=>this.ensurePrebufferSessions()),5e3)}async getVideoStream(e){await this.ensurePrebufferSessions();let r=null==e?void 0:e.id;if(!r&&!this.sessions.has(r)){var i;const n=await this.mixinDevice.getVideoStream(e),o=JSON.parse((await m.convertMediaObjectToBuffer(n,t.ScryptedMimeTypes.FFmpegInput)).toString());r=null==o||null===(i=o.mediaStreamOptions)||void 0===i?void 0:i.id,this.sessions.set(null==e?void 0:e.id,this.sessions.get(r))}let n=this.sessions.get(r);return n?(n.ensurePrebufferSession(),await n.parserSessionPromise,n=this.sessions.get(r),n?n.getVideoStream(e):this.mixinDevice.getVideoStream(e)):this.mixinDevice.getVideoStream(e)}async ensurePrebufferSessions(){const e=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(e),i=r?r.map((e=>e.id)):[void 0];let o=0;const s=i.length;for(const t of i){let r=this.sessions.get(t);if(!r){const i=null==e?void 0:e.find((e=>e.id===t));null!=i&&i.prebuffer&&f.a(`Prebuffer is already available on ${this.name}. If this is a grouped device, disable the Rebroadcast extension.`);const a=null==i?void 0:i.name;r=new PrebufferSession(this,a,t),this.sessions.set(t,r),(async()=>{for(;this.sessions.get(t)===r&&!this.released;){r.ensurePrebufferSession();try{const e=await r.parserSessionPromise;o++,this.online=o==s,await(0,n.once)(e.events,"killed"),this.console.error("prebuffer session ended")}catch(e){this.console.error("prebuffer session ended with error",e)}finally{o--,this.online=o==s}this.console.log("restarting prebuffer session in 5 seconds"),await new Promise((e=>setTimeout(e,5e3)))}this.console.log("exiting prebuffer session (released or restarted with new configuration)")})()}}g.onMixinEvent(this.id,this.mixinProviderNativeId,t.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[];try{const t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);r&&(null==t?void 0:t.length)>1&&e.push({title:"Prebuffered Streams",description:"The streams to prebuffer. Enable only as necessary to reduce traffic.",key:"enabledStreams",value:r.map((e=>e.name||"")),choices:t.map((e=>e.name)),multiple:!0})}catch(e){throw this.console.error("error in getVideoStreamOptions",e),e}e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:y,value:this.storage.getItem(y)||v.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:S,value:("false"!==this.storage.getItem(S)).toString()});for(const t of new Set([...this.sessions.values()]))if(t)try{e.push(...await t.getMixinSettings())}catch(e){throw this.console.error("error in prebuffer session getMixinSettings",e),e}return e}async putMixinSetting(e,t){const r=this.sessions;this.sessions=new Map,"enabledStreams"===e?this.storage.setItem(e,JSON.stringify(t)):this.storage.setItem(e,t.toString());for(const e of r.values()){var i;null==e||null===(i=e.parserSessionPromise)||void 0===i||i.then((e=>e.kill()))}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(e&&e.length){try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter((e=>t.includes(e.name)))}catch(e){}return[e[0]]}}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem(y))||v;if(t)for(const e of t)e.prebuffer=r;else e.push({prebuffer:r});return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;t&&(t.clearPrebuffers(),null===(e=t.parserSessionPromise)||void 0===e||e.then((e=>{this.console.log("prebuffer released"),e.kill(),t.clearPrebuffers()})))}}}class PrebufferProvider extends d.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(h.getSystemState())){var t;const r=h.getDeviceById(e);null!==(t=r.mixins)&&void 0!==t&&t.includes(this.id)&&r.getVideoStreamOptions()}const r=function(){var e=new Date;return e.setHours(24),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0),e.getTime()-(new Date).getTime()}()+72e5;this.log.i(`Rebroadcaster scheduled for restart at 2AM: ${Math.round(r/1e3/60)} minutes`),setTimeout((()=>g.requestRestart()),r)}async canMixin(e,r){return r.includes(t.ScryptedInterface.VideoCamera)?[t.ScryptedInterface.VideoCamera,t.ScryptedInterface.Settings,t.ScryptedInterface.Online]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new PrebufferMixin(e,t,r,this.nativeId)}async releaseMixin(e,t){t.online=!0,t.release()}}var w=new PrebufferProvider;e.default=w})();var n=exports="undefined"==typeof exports?{}:exports;for(var o in i)n[o]=i[o];i.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})})();
|
|
2
2
|
//# sourceMappingURL=main.nodejs.js.map
|
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -259,6 +259,32 @@ class PrebufferSession {
|
|
|
259
259
|
const session = await startRebroadcastSession(ffmpegInput, rbo);
|
|
260
260
|
this.parserSession = session;
|
|
261
261
|
|
|
262
|
+
// cloud streams need a periodic token refresh.
|
|
263
|
+
if (ffmpegInput.mediaStreamOptions?.refreshAt) {
|
|
264
|
+
let mso = ffmpegInput.mediaStreamOptions;
|
|
265
|
+
let refreshTimeout: NodeJS.Timeout;
|
|
266
|
+
|
|
267
|
+
const refreshStream = async () => {
|
|
268
|
+
if (!session.isActive)
|
|
269
|
+
return;
|
|
270
|
+
const mo = await this.mixinDevice.getVideoStream(mso);
|
|
271
|
+
const moBuffer = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.FFmpegInput);
|
|
272
|
+
const ffmpegInput = JSON.parse(moBuffer.toString()) as FFMpegInput;
|
|
273
|
+
mso = ffmpegInput.mediaStreamOptions
|
|
274
|
+
|
|
275
|
+
scheduleRefresh(ffmpegInput);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const scheduleRefresh = (ffmpegInput: FFMpegInput) => {
|
|
279
|
+
const when = ffmpegInput.mediaStreamOptions.refreshAt - Date.now() - 30000;
|
|
280
|
+
this.console.log('refreshing media stream in', when);
|
|
281
|
+
refreshTimeout = setTimeout(refreshStream, when);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
scheduleRefresh(ffmpegInput);
|
|
285
|
+
session.events.on('killed', () => clearTimeout(refreshTimeout));
|
|
286
|
+
}
|
|
287
|
+
|
|
262
288
|
let watchdog: NodeJS.Timeout;
|
|
263
289
|
const restartWatchdog = () => {
|
|
264
290
|
clearTimeout(watchdog);
|
|
@@ -307,10 +333,10 @@ class PrebufferSession {
|
|
|
307
333
|
// s16le will be a no-op if there's no pcm, no harm.
|
|
308
334
|
for (const container of ['mpegts', 'mp4', 's16le']) {
|
|
309
335
|
const eventName = container + '-data';
|
|
310
|
-
let prebufferContainer: PrebufferStreamChunk[] = this.prebuffers[container];
|
|
311
336
|
let shifts = 0;
|
|
312
337
|
|
|
313
338
|
session.events.on(eventName, (chunk: StreamChunk) => {
|
|
339
|
+
const prebufferContainer: PrebufferStreamChunk[] = this.prebuffers[container];
|
|
314
340
|
const now = Date.now();
|
|
315
341
|
|
|
316
342
|
if (chunk.type === 'mdat') {
|
|
@@ -330,7 +356,7 @@ class PrebufferSession {
|
|
|
330
356
|
}
|
|
331
357
|
|
|
332
358
|
if (shifts > 1000) {
|
|
333
|
-
|
|
359
|
+
this.prebuffers[container] = prebufferContainer.slice();
|
|
334
360
|
shifts = 0;
|
|
335
361
|
}
|
|
336
362
|
|
|
@@ -476,7 +502,6 @@ class PrebufferSession {
|
|
|
476
502
|
)
|
|
477
503
|
}
|
|
478
504
|
|
|
479
|
-
// this.console.log('prebuffer ffmpeg input', ffmpegInput);
|
|
480
505
|
const mo = mediaManager.createFFmpegMediaObject(ffmpegInput);
|
|
481
506
|
return mo;
|
|
482
507
|
}
|