diff --git a/src-widgets/craco.config.js b/src-widgets/craco.config.js deleted file mode 100644 index 8fc0cc0..0000000 --- a/src-widgets/craco.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@iobroker/vis-2-widgets-react-dev/craco.config.js'); diff --git a/src-widgets/eslint.config.mjs b/src-widgets/eslint.config.mjs new file mode 100644 index 0000000..810a5a3 --- /dev/null +++ b/src-widgets/eslint.config.mjs @@ -0,0 +1,30 @@ +import config, { reactConfig } from '@iobroker/eslint-config'; + +// disable temporary the rule 'jsdoc/require-param' and enable 'jsdoc/require-jsdoc' +config.forEach(rule => { + if (rule?.plugins?.jsdoc) { + rule.rules['jsdoc/require-jsdoc'] = 'off'; + rule.rules['jsdoc/require-param'] = 'off'; + } +}); + +export default [ + ...config, + ...reactConfig, + { + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + }, + }, + { + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProject: ['*.js', '*.mjs'], + }, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, +]; diff --git a/src-widgets/public/index.html b/src-widgets/index.html similarity index 90% rename from src-widgets/public/index.html rename to src-widgets/index.html index acbb90f..886f7ee 100644 --- a/src-widgets/public/index.html +++ b/src-widgets/index.html @@ -22,6 +22,10 @@ script.src = window.location.protocol + '//' + window.location.hostname + ':8082/lib/js/socket.io.js'; document.head.appendChild(script); +
diff --git a/src-widgets/modulefederation.config.js b/src-widgets/modulefederation.config.js deleted file mode 100644 index 5e21e54..0000000 --- a/src-widgets/modulefederation.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const makeFederation = require('@iobroker/vis-2-widgets-react-dev/modulefederation.config'); - -module.exports = makeFederation('vis2CameraWidgets', { - './RtspCamera': './src/RtspCamera', - './SnapshotCamera': './src/SnapshotCamera', - './translations': './src/translations', -}); diff --git a/src-widgets/package.json b/src-widgets/package.json index 8e3173e..b12240e 100644 --- a/src-widgets/package.json +++ b/src-widgets/package.json @@ -3,39 +3,34 @@ "private": true, "version": "2.1.2", "dependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@craco/craco": "^7.1.0", "@iobroker/adapter-react-v5": "^7.1.4", - "@iobroker/vis-2-widgets-react-dev": "^4.0.3", + "@iobroker/eslint-config": "^0.1.6", + "@iobroker/types": "^6.0.11", + "@iobroker/types-vis-2": "^2.10.7", "@mui/icons-material": "^6.1.1", "@mui/material": "^6.1.1", - "craco-esbuild": "^0.6.1", - "craco-module-federation": "^1.1.0", - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-react-app": "^7.0.1", - "eslint-plugin-eqeqeq-fix": "^1.0.3", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.9.0", - "eslint-plugin-only-warn": "^1.1.0", - "eslint-plugin-react": "^7.34.4", - "eslint-plugin-react-hooks": "^4.6.2", + "@types/react": "^18.3.7", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "@originjs/vite-plugin-federation": "^1.3.6", "hls.js": "^1.5.15", "moment": "^2.30.1", - "node-sass": "^9.0.0", "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-scripts": "^5.0.1", "sass": "^1.79.1", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "vite": "^5.4.6", + "vite-tsconfig-paths": "^5.0.1", + "vite-plugin-top-level-await": "^1.4.4" }, "scripts": { - "start": "set PORT=4173 && craco start", + "start": "vite", + "build": "tsc && vite build", + "preview": "vite preview", "lint": "eslint -c eslint.config.mjs src", - "build": "craco build", - "test": "react-scripts test", - "eject": "react-scripts eject", + "tsc": "tsc -p tsconfig.json", + "prettier": "prettier -c prettier.config.mjs --write src", "i18n": "node node_modules/@iobroker/vis-2-widgets-react-dev/searchI18n vis_2_widgets_camera" }, "eslintConfig": { diff --git a/src-widgets/prettier.config.mjs b/src-widgets/prettier.config.mjs new file mode 100644 index 0000000..2f00708 --- /dev/null +++ b/src-widgets/prettier.config.mjs @@ -0,0 +1,3 @@ +import prettierConfig from '@iobroker/eslint-config/prettier.config.mjs'; + +export default prettierConfig; diff --git a/src-widgets/src/.eslintrc.js b/src-widgets/src/.eslintrc.js deleted file mode 100644 index c80dfde..0000000 --- a/src-widgets/src/.eslintrc.js +++ /dev/null @@ -1,76 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'airbnb', - // 'react-app', - 'plugin:eqeqeq-fix/recommended', - ], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - ecmaVersion: 'latest', - sourceType: 'module', - }, - plugins: [ - 'only-warn', - 'react', - ], - rules: { - 'arrow-parens': [1, 'as-needed'], - 'react/jsx-indent': 'off', - 'react/jsx-indent-props': 'off', - 'react/no-access-state-in-setstate': 'off', - 'jsx-a11y/click-events-have-key-events': 'off', - 'jsx-a11y/no-static-element-interactions': 'off', - 'no-plusplus': 'off', - 'react/react-in-jsx-scope': 'off', - 'react/prop-types': 'off', - 'react/no-render-return-value': 'off', - 'max-len': 'off', - 'react/destructuring-assignment': 'off', - 'react/prefer-stateless-function': 'off', - 'react/self-closing-comp': 'off', - 'react/jsx-filename-extension': 'off', - 'no-nested-ternary': 'off', - 'react/no-array-index-key': 'off', - 'react/jsx-props-no-spreading': 'off', - 'react/sort-comp': 'off', - 'react/no-did-update-set-state': 'off', - 'global-require': 'off', - 'import/extensions': 'off', - 'operator-linebreak': 'off', - 'no-unused-expressions': 'off', - 'prefer-destructuring': 'off', - 'no-return-assign': 'off', - 'no-multi-spaces': 'off', - 'key-spacing': 'off', - 'no-undef': 2, - 'react/forbid-prop-types': 'off', - 'react/require-default-props': 'off', - 'import/no-extraneous-dependencies': 'off', - 'react/jsx-wrap-multilines': 'off', - 'react/jsx-closing-tag-location': 'off', - 'no-restricted-syntax': 'off', - 'guard-for-in': 'off', - // 'linebreak-style': ["error", "windows"], - 'linebreak-style': ['off'], - 'no-param-reassign': 'off', - 'no-await-in-loop': 'off', - 'no-console': ['error', { allow: ['warn', 'error', 'log'] }], - 'no-underscore-dangle': 'off', - 'no-constant-condition': 'off', - 'no-loop-func': 'off', - 'no-continue': 'off', - 'implicit-arrow-linebreak': 'off', - radix: 'off', - indent: ['error', 4, { SwitchCase: 1 }], - 'no-alert': 'off', - 'react/function-component-definition': 'off', - }, -}; diff --git a/src-widgets/src/Generic.jsx b/src-widgets/src/Generic.jsx deleted file mode 100644 index f224b86..0000000 --- a/src-widgets/src/Generic.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import PropTypes from 'prop-types'; - -class Generic extends window.visRxWidget { - static getI18nPrefix() { - return 'cameras_'; - } - - // static getObjectIcon(obj, id, imagePrefix) { - // imagePrefix = imagePrefix || '../..'; // http://localhost:8081'; - // let src = ''; - // const common = obj && obj.common; - // - // if (common) { - // const cIcon = common.icon; - // if (cIcon) { - // if (!cIcon.startsWith('data:image/')) { - // if (cIcon.includes('.')) { - // let instance; - // if (obj.type === 'instance' || obj.type === 'adapter') { - // src = `${imagePrefix}/adapter/${common.name}/${cIcon}`; - // } else if (id && id.startsWith('system.adapter.')) { - // instance = id.split('.', 3); - // if (cIcon[0] === '/') { - // instance[2] += cIcon; - // } else { - // instance[2] += `/${cIcon}`; - // } - // src = `${imagePrefix}/adapter/${instance[2]}`; - // } else { - // instance = id.split('.', 2); - // if (cIcon[0] === '/') { - // instance[0] += cIcon; - // } else { - // instance[0] += `/${cIcon}`; - // } - // src = `${imagePrefix}/adapter/${instance[0]}`; - // } - // } else { - // return null; - // } - // } else { - // src = cIcon; - // } - // } - // } - // - // return src || null; - // } - // - // wrapContent(content, addToHeader, cardContentStyle, headerStyle, onCardClick) { - // return super.wrapContent(content, addToHeader, cardContentStyle, headerStyle, onCardClick, { Card, CardContent }); - // } -} - -Generic.propTypes = { - context: PropTypes.object, - themeType: PropTypes.string, - style: PropTypes.object, - data: PropTypes.object, -}; - -export default Generic; diff --git a/src-widgets/src/RtspCamera.jsx b/src-widgets/src/RtspCamera.tsx similarity index 50% rename from src-widgets/src/RtspCamera.jsx rename to src-widgets/src/RtspCamera.tsx index d8d4f73..d14fe75 100644 --- a/src-widgets/src/RtspCamera.jsx +++ b/src-widgets/src/RtspCamera.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, type JSX } from 'react'; import { Button, CircularProgress, @@ -12,9 +12,19 @@ import { import { Close } from '@mui/icons-material'; -import Generic from './Generic'; +import VisRxWidget from './VisRxWidget'; +import type { VisRxWidgetState, VisRxWidgetProps } from '@iobroker/types-vis-2/visRxWidget'; +import type { + RxRenderWidgetProps, + RxWidgetInfoAttributesField, + RxWidgetInfoCustomComponentContext, + RxWidgetInfoCustomComponentProperties, +} from '@iobroker/types-vis-2'; +import { I18n } from '@iobroker/adapter-react-v5'; -const styles = { +export const TRANSLATION_PREFIX = 'cameras_'; + +const styles: Record = { camera: { width: '100%', height: '100%', @@ -35,26 +45,75 @@ const styles = { }, }; -export const CameraField = props => { - const [cameras, setCameras] = React.useState(null); - const [camera, setCamera] = React.useState(props.data[props.field.name] || ''); +interface RtspCameraRxData { + noCard: boolean; + widgetTitle: string; + width: number; + camera: string; +} + +interface RtspCameraState extends VisRxWidgetState { + loading: boolean; + full: boolean; + alive: boolean; +} + +interface CameraEntry { + enabled: boolean; + value: string; + label: string; + subLabel: string; +} + +interface CameraFieldProps { + data: Record; + field: RxWidgetInfoAttributesField; + rtsp?: boolean; + onDataChange: (newData: Partial) => void; + context: RxWidgetInfoCustomComponentContext; + t: (word: string) => string; +} + +export const CameraField = (props: CameraFieldProps): JSX.Element => { + const [cameras, setCameras] = React.useState(null); + const [camera, setCamera] = React.useState( + (props.data as unknown as Record)[props.field.name] || '', + ); useEffect(() => { - (async () => { - const _cameras = []; - const instances = await props.context.socket.getAdapterInstances('cameras'); + void (async () => { + const _cameras: CameraEntry[] = []; + const instances: ioBroker.InstanceObject[] = await props.context.socket.getAdapterInstances('cameras'); instances.forEach(instance => { const instanceId = instance._id.split('.').pop(); instance.native.cameras - .filter(iCamera => !props.rtsp || iCamera.type === 'rtsp' || iCamera.rtsp) - .forEach(iCamera => { - _cameras.push({ - enabled: iCamera.enabled !== false, - value: `${instanceId}/${iCamera.name}`, - label: `cameras.${instanceId}/${iCamera.name}`, - subLabel: iCamera.desc ? `${iCamera.desc}/${iCamera.ip}` : iCamera.ip || '', - }); - }); + .filter( + (iCamera: { + enabled: boolean; + name: string; + desc: string; + ip?: string; + type: string; + rtsp?: boolean; + }) => !props.rtsp || iCamera.type === 'rtsp' || iCamera.rtsp, + ) + .forEach( + (iCamera: { + enabled: boolean; + name: string; + desc: string; + ip?: string; + type: string; + rtsp?: boolean; + }) => { + _cameras.push({ + enabled: iCamera.enabled !== false, + value: `${instanceId}/${iCamera.name}`, + label: `cameras.${instanceId}/${iCamera.name}`, + subLabel: iCamera.desc ? `${iCamera.desc}/${iCamera.ip}` : iCamera.ip || '', + }); + }, + ); }); setCameras(_cameras); })(); @@ -66,7 +125,7 @@ export const CameraField = props => { variant="standard" value={camera} onChange={e => { - props.setData({ [props.field.name]: e.target.value }); + props.onDataChange({ [props.field.name]: e.target.value }); setCamera(e.target.value); }} > @@ -87,7 +146,7 @@ export const CameraField = props => { color: 'red', }} > - {Generic.t('disabled')} + {props.t('disabled')} ) : null} @@ -98,15 +157,31 @@ export const CameraField = props => { ); }; -class RtspCamera extends Generic { - constructor(props) { +class RtspCamera extends VisRxWidget { + private videoInterval: ReturnType | null = null; + + private readonly videoRef: React.RefObject; + + private readonly fullVideoRef: React.RefObject; + + private currentCam: string | null = null; + + private useMessages: boolean | undefined; + + private subscribedOnAlive: string = ''; + + constructor(props: VisRxWidgetProps) { super(props); - this.videoInterval = null; this.videoRef = React.createRef(); this.fullVideoRef = React.createRef(); - this.currentCam = null; - this.state.full = false; - this.state.alive = false; + Object.assign(this.state, { + full: false, + alive: false, + }); + } + + static getI18nPrefix(): string { + return TRANSLATION_PREFIX; } static getWidgetInfo() { @@ -142,13 +217,19 @@ class RtspCamera extends Generic { label: 'Camera', name: 'camera', type: 'custom', - component: (field, data, setData, props) => ( + component: ( + field: RxWidgetInfoAttributesField, + data: RtspCameraRxData, + onDataChange: (newData: Partial) => void, + props: RxWidgetInfoCustomComponentProperties, + ) => ( } + onDataChange={onDataChange} context={props.context} + t={(word: string) => I18n.t(TRANSLATION_PREFIX + word)} /> ), }, @@ -164,64 +245,67 @@ class RtspCamera extends Generic { }; } + // @ts-expect-error Fix later // eslint-disable-next-line class-methods-use-this getWidgetInfo() { return RtspCamera.getWidgetInfo(); } - static drawCamera(ref, data) { + static drawCamera(ref: React.RefObject, data: string) { const canvas = ref.current; if (!canvas) { return; } const context = canvas.getContext('2d'); - try { - const imageObj = new Image(); - imageObj.src = `data:image/jpeg;base64,${data}`; - imageObj.onload = () => { - canvas.width = imageObj.width; - canvas.height = imageObj.height; - // const hRatio = canvas.width / imageObj.width; - // const vRatio = canvas.height / imageObj.height; - // const ratio = Math.min(hRatio, vRatio); - // const centerShiftX = (canvas.width - imageObj.width * ratio) / 2; - // const centerShiftY = (canvas.height - imageObj.height * ratio) / 2; - // context.clearRect(0, 0, canvas.width, canvas.height); - context.drawImage( - imageObj, - 0, - 0, - imageObj.width, - imageObj.height, - // centerShiftX, - // centerShiftY, - // imageObj.width * ratio, - // imageObj.height * ratio, - ); - }; - imageObj.onerror = e => { + if (context) { + try { + const imageObj = new Image(); + imageObj.src = `data:image/jpeg;base64,${data}`; + imageObj.onload = () => { + canvas.width = imageObj.width; + canvas.height = imageObj.height; + // const hRatio = canvas.width / imageObj.width; + // const vRatio = canvas.height / imageObj.height; + // const ratio = Math.min(hRatio, vRatio); + // const centerShiftX = (canvas.width - imageObj.width * ratio) / 2; + // const centerShiftY = (canvas.height - imageObj.height * ratio) / 2; + // context.clearRect(0, 0, canvas.width, canvas.height); + context.drawImage( + imageObj, + 0, + 0, + imageObj.width, + imageObj.height, + // centerShiftX, + // centerShiftY, + // imageObj.width * ratio, + // imageObj.height * ratio, + ); + }; + imageObj.onerror = e => { + console.error(e); + }; + } catch (e) { console.error(e); - }; - } catch (e) { - console.error(e); + } } } - updateStream = (id, state) => { + updateStream = (_id: string, state: ioBroker.State | null | undefined) => { if (state?.val) { if (this.state.loading) { this.setState({ loading: false }); } - RtspCamera.drawCamera(this.videoRef, state.val); + RtspCamera.drawCamera(this.videoRef, state.val as string); if (this.state.full) { - RtspCamera.drawCamera(this.fullVideoRef, state.val); + RtspCamera.drawCamera(this.fullVideoRef, state.val as string); } } }; - static getNameAndInstance(value) { + static getNameAndInstance(value: string): { instanceId: string; name: string } | null { if (!value) { return null; } @@ -235,7 +319,7 @@ class RtspCamera extends Generic { }; } - onCameras = data => { + onCameras = (data: string | { accepted: boolean; error?: string }): void => { if (data) { // if it is success or error object if (typeof data === 'object' && (data.accepted || data.error)) { @@ -249,15 +333,15 @@ class RtspCamera extends Generic { this.setState({ loading: false }); } - RtspCamera.drawCamera(this.videoRef, data); + RtspCamera.drawCamera(this.videoRef, data as string); if (this.state.full) { - RtspCamera.drawCamera(this.fullVideoRef, data); + RtspCamera.drawCamera(this.fullVideoRef, data as string); } } }; - async propertiesUpdate() { + async propertiesUpdate(): Promise { if (this.useMessages === undefined) { this.useMessages = await this.props.context.socket.checkFeatureSupported('INSTANCE_MESSAGES'); } @@ -267,126 +351,143 @@ class RtspCamera extends Generic { // this.width = this.getImageWidth(); // if we were subscribed, unsubscribe if (this.currentCam) { - const { instanceId, name } = RtspCamera.getNameAndInstance(this.currentCam); - if (this.useMessages) { - await this.props.context.socket.unsubscribeFromInstance( - `cameras.${instanceId}`, - `startCamera/${name}`, - this.onCameras, - ); - } else { - // Bluefox 2023.09.28: delete this branch after js-controller 5.0.13 will be mainstream - await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { - val: false, - }); - await this.props.context.socket.unsubscribeState( - `cameras.${instanceId}.${name}.stream`, - this.updateStream, - ); + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + await this.props.context.socket.unsubscribeFromInstance( + `cameras.${instanceId}`, + `startCamera/${name}`, + this.onCameras as (data: Record) => void, + ); + } else { + // Bluefox 2023.09.28: delete this branch after js-controller 5.0.13 will be mainstream + await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: false, + }); + await this.props.context.socket.unsubscribeState( + `cameras.${instanceId}.${name}.stream`, + this.updateStream, + ); + } } } // subscribe on new camera if (this.state.rxData.camera) { this.setState({ loading: true }); - const { instanceId, name } = RtspCamera.getNameAndInstance(this.state.rxData.camera); - if (this.useMessages) { - await this.props.context.socket.subscribeOnInstance( - `cameras.${instanceId}`, - `startCamera/${name}`, - { width: this.getImageWidth() }, - this.onCameras, - ); - } else { - await this.props.context.socket.subscribeState( - `cameras.${instanceId}.${name}.stream`, - this.updateStream, - ); + const result = RtspCamera.getNameAndInstance(this.state.rxData.camera); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + await this.props.context.socket.subscribeOnInstance( + `cameras.${instanceId}`, + `startCamera/${name}`, + { width: this.getImageWidth() }, + this.onCameras as (data: Record) => void, + ); + } else { + await this.props.context.socket.subscribeState( + `cameras.${instanceId}.${name}.stream`, + this.updateStream, + ); + } } } else { const canvas = this.videoRef.current; if (canvas) { const context = canvas.getContext('2d'); - context.clearRect(0, 0, canvas.width, canvas.height); + context?.clearRect(0, 0, canvas.width, canvas.height); } } this.currentCam = this.state.rxData.camera; } else if (this.currentCam) { // not alive - const { instanceId, name } = RtspCamera.getNameAndInstance(this.currentCam); - if (!this.useMessages) { - await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { val: false }); - await this.props.context.socket.unsubscribeState( - `cameras.${instanceId}.${name}.stream`, - this.updateStream, - ); + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (!this.useMessages) { + await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: false, + }); + await this.props.context.socket.unsubscribeState( + `cameras.${instanceId}.${name}.stream`, + this.updateStream, + ); + } } this.currentCam = null; } } else if (this.currentCam && this.state.alive) { // refresh stream - const { instanceId, name } = RtspCamera.getNameAndInstance(this.currentCam); - if (this.useMessages) { - await this.props.context.socket.subscribeOnInstance( - `cameras.${instanceId}`, - `startCamera/${name}`, - { width: this.getImageWidth() }, - this.onCameras, - ); - } else { - await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { - val: true, - expire: 30, // expire in 30 seconds - }); + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + await this.props.context.socket.subscribeOnInstance( + `cameras.${instanceId}`, + `startCamera/${name}`, + { width: this.getImageWidth() }, + this.onCameras as (data: Record) => void, + ); + } else { + await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: true, + expire: 30, // expire in 30 seconds + }); + } } } else if (this.currentCam && !this.state.alive) { // not alive - const { instanceId, name } = RtspCamera.getNameAndInstance(this.currentCam); - if (!this.useMessages) { - await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { val: false }); - await this.props.context.socket.unsubscribeState( - `cameras.${instanceId}.${name}.stream`, - this.updateStream, - ); + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (!this.useMessages) { + await this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { val: false }); + await this.props.context.socket.unsubscribeState( + `cameras.${instanceId}.${name}.stream`, + this.updateStream, + ); + } } this.currentCam = null; } } - getImageWidth(isFull) { + getImageWidth(isFull?: boolean): number { isFull = isFull === undefined ? this.state.full : isFull; // if (parseInt(this.state.rxData.width, 10)) { // return parseInt(this.state.rxData.width, 10); // } if (isFull && this.fullVideoRef.current) { - return this.fullVideoRef.current.parentElement.clientWidth || 0; + return this.fullVideoRef.current.parentElement?.clientWidth || 0; } - return this.videoRef.current?.parentElement.clientWidth || 0; + return this.videoRef.current?.parentElement?.clientWidth || 0; } - async subscribeOnAlive() { + subscribeOnAlive(): void { const data = RtspCamera.getNameAndInstance(this.state.rxData.camera); - if (this.subsribedOnAlive !== (data ? data.instanceId : null)) { - if (this.subsribedOnAlive) { + if (this.subscribedOnAlive !== (data ? data.instanceId : null)) { + if (this.subscribedOnAlive) { this.props.context.socket.unsubscribeState( - `system.adapter.cameras.${this.subsribedOnAlive}.alive`, + `system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = ''; + this.subscribedOnAlive = ''; } if (data) { this.props.context.socket.subscribeState( `system.adapter.cameras.${data.instanceId}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = data.instanceId; + this.subscribedOnAlive = data.instanceId; } } } - onAliveChanged = (id, state) => { + onAliveChanged = (id: string, state: ioBroker.State | null | undefined) => { const data = RtspCamera.getNameAndInstance(this.state.rxData.camera); if (data && id === `system.adapter.cameras.${data.instanceId}.alive`) { const alive = !!state?.val; @@ -406,7 +507,7 @@ class RtspCamera extends Generic { } async onRxDataChanged(/* prevRxData */) { - await this.subscribeOnAlive(); + this.subscribeOnAlive(); await this.propertiesUpdate(); } @@ -415,25 +516,32 @@ class RtspCamera extends Generic { this.videoInterval && clearInterval(this.videoInterval); this.videoInterval = null; - if (this.subsribedOnAlive) { + if (this.subscribedOnAlive) { this.props.context.socket.unsubscribeState( - `system.adapter.cameras.${this.subsribedOnAlive}.alive`, + `system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = null; + this.subscribedOnAlive = ''; } if (this.currentCam) { - const { instanceId, name } = RtspCamera.getNameAndInstance(this.currentCam); - if (this.useMessages) { - this.props.context.socket - .unsubscribeFromInstance(`cameras.${instanceId}`, `startCamera/${name}`, this.onCameras) - .catch(e => console.error(e)); + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + this.props.context.socket + .unsubscribeFromInstance( + `cameras.${instanceId}`, + `startCamera/${name}`, + this.onCameras as (data: Record) => void, + ) + .catch((e: Error) => console.error(e)); + } } } } - renderDialog() { + renderDialog(): JSX.Element | null { return this.state.full ? ( - {Generic.t('Close')} + {I18n.t(`${TRANSLATION_PREFIX}Close`)} ) : null; } - renderWidgetBody(props) { + renderWidgetBody(props: RxRenderWidgetProps): React.JSX.Element | null { super.renderWidgetBody(props); const content = ( @@ -479,7 +587,10 @@ class RtspCamera extends Generic { {this.state.loading && this.state.alive && } {!this.state.alive ? (
- {Generic.t('Camera instance %s inactive', (this.state.rxData.camera || '').split('/')[0])} + {I18n.t( + `${TRANSLATION_PREFIX}Camera instance %s inactive`, + (this.state.rxData.camera || '').split('/')[0], + )}
) : null} = { camera: { width: '100%', height: '100%', @@ -33,16 +33,44 @@ const styles = { }, }; -class SnapshotCamera extends Generic { - constructor(props) { +interface SnapshotCameraRxData { + noCard: boolean; + widgetTitle: string; + pollingInterval: number; + pollingIntervalFull: number; + noCacheByFull: boolean; + rotate: number; + camera: string; + bigCamera: string; +} + +interface SnapshotCameraState extends VisRxWidgetState { + loading: boolean; + full: boolean; + alive: boolean; + error: boolean; +} + +class SnapshotCamera extends VisRxWidget { + private readonly videoRef: React.RefObject; + + private readonly fullVideoRef: React.RefObject; + + private loading: boolean = false; + + private subscribedOnAlive: string = ''; + + private pollingInterval: ReturnType | null = null; + + constructor(props: VisRxWidgetProps) { super(props); - this.videoInterval = null; this.videoRef = React.createRef(); this.fullVideoRef = React.createRef(); - this.currentCam = null; - this.state.full = false; - this.state.alive = false; - this.state.error = false; + Object.assign(this.state, { + full: false, + alive: false, + error: false, + }); } static getWidgetInfo() { @@ -100,12 +128,18 @@ class SnapshotCamera extends Generic { label: 'Camera', name: 'camera', type: 'custom', - component: (field, data, setData, props) => ( + component: ( + field: RxWidgetInfoAttributesField, + data: SnapshotCameraRxData, + onDataChange: (newData: Partial) => void, + props: RxWidgetInfoCustomComponentProperties, + ) => ( } + onDataChange={onDataChange} context={props.context} + t={(word: string) => I18n.t(TRANSLATION_PREFIX + word)} /> ), }, @@ -113,12 +147,18 @@ class SnapshotCamera extends Generic { label: 'camera_in_dialog', name: 'bigCamera', type: 'custom', - component: (field, data, setData, props) => ( + component: ( + field: RxWidgetInfoAttributesField, + data: SnapshotCameraRxData, + onDataChange: (newData: Partial) => void, + props: RxWidgetInfoCustomComponentProperties, + ) => ( } + onDataChange={onDataChange} context={props.context} + t={(word: string) => I18n.t(TRANSLATION_PREFIX + word)} /> ), hidden: '!data.camera', @@ -135,12 +175,13 @@ class SnapshotCamera extends Generic { }; } + // @ts-expect-error Fix later // eslint-disable-next-line class-methods-use-this getWidgetInfo() { return SnapshotCamera.getWidgetInfo(); } - static getNameAndInstance(value) { + static getNameAndInstance(value: string): { instanceId: string; name: string } | null { if (!value) { return null; } @@ -154,51 +195,54 @@ class SnapshotCamera extends Generic { }; } - getImageWidth(isFull) { + getImageWidth(isFull?: boolean): number { isFull = isFull === undefined ? this.state.full : isFull; if (isFull && this.fullVideoRef.current) { - return this.fullVideoRef.current?.parentElement.clientWidth || 0; + return this.fullVideoRef.current?.parentElement?.clientWidth || 0; } - return this.videoRef.current?.parentElement.clientWidth || 0; + return this.videoRef.current?.parentElement?.clientWidth || 0; } - async subscribeOnAlive() { + subscribeOnAlive(): void { const data = SnapshotCamera.getNameAndInstance(this.state.rxData.camera); - if (this.subsribedOnAlive !== (data ? data.instanceId : null)) { - if (this.subsribedOnAlive) { + if (this.subscribedOnAlive !== (data ? data.instanceId : null)) { + if (this.subscribedOnAlive) { this.props.context.socket.unsubscribeState( - `system.adapter.cameras.${this.subsribedOnAlive}.alive`, + `system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = ''; + this.subscribedOnAlive = ''; } if (data) { this.props.context.socket.subscribeState( `system.adapter.cameras.${data.instanceId}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = data.instanceId; + this.subscribedOnAlive = data.instanceId; } } } - updateImage = () => { + updateImage = (): void => { if (!this.loading) { this.loading = true; if (this.videoRef.current) { this.videoRef.current.src = this.getUrl(); this.videoRef.current.onload = e => { - if (e.target && !e.target.style.opacity !== '1') { - e.target.style.opacity = '1'; + const image: HTMLImageElement = e.currentTarget as HTMLImageElement; + if (image && image.style.opacity !== '1') { + image.style.opacity = '1'; } this.state.error && this.setState({ error: false }); this.loading = false; }; this.videoRef.current.onerror = e => { - if (e.target && e.target.style.opacity !== '0') { - e.target.style.opacity = '0'; + // @ts-expect-error fix later + const image: HTMLImageElement = e.target as HTMLImageElement; + if (image && image.style.opacity !== '0') { + image.style.opacity = '0'; } !this.state.error && this.setState({ error: true }); @@ -220,14 +264,16 @@ class SnapshotCamera extends Generic { this.pollingInterval = setInterval( this.updateImage, parseInt( - this.state.full ? this.state.rxData.pollingIntervalFull : this.state.rxData.pollingInterval, + this.state.full + ? (this.state.rxData.pollingIntervalFull as unknown as string) + : (this.state.rxData.pollingInterval as unknown as string), 10, ) || 500, ); } } - onAliveChanged = (id, state) => { + onAliveChanged = (id: string, state: ioBroker.State | null | undefined) => { const data = SnapshotCamera.getNameAndInstance(this.state.rxData.camera); if (data && id === `system.adapter.cameras.${data.instanceId}.alive`) { const alive = !!state?.val; @@ -243,8 +289,8 @@ class SnapshotCamera extends Generic { this.subscribeOnAlive(); } - async onRxDataChanged(/* prevRxData */) { - await this.subscribeOnAlive(); + onRxDataChanged(/* prevRxData */) { + this.subscribeOnAlive(); } componentWillUnmount() { @@ -252,16 +298,16 @@ class SnapshotCamera extends Generic { this.pollingInterval && clearInterval(this.pollingInterval); this.pollingInterval = null; - if (this.subsribedOnAlive) { + if (this.subscribedOnAlive) { this.props.context.socket.unsubscribeState( - `system.adapter.cameras.${this.subsribedOnAlive}.alive`, + `system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged, ); - this.subsribedOnAlive = null; + this.subscribedOnAlive = ''; } } - renderDialog(url) { + renderDialog(url: string): JSX.Element | null { if (this.state.full && this.state.rxData.bigCamera) { url = this.getUrl(true) || url; } @@ -295,14 +341,14 @@ class SnapshotCamera extends Generic { color="primary" variant="contained" > - {Generic.t('Close')} + {I18n.t(`${TRANSLATION_PREFIX}Close`)} ) : null; } - getUrl(isFull) { + getUrl(isFull?: boolean): string { if (isFull && !this.state.rxData.bigCamera) { const url = `../cameras.${this.state.rxData.bigCamera}?`; const params = [ @@ -327,7 +373,7 @@ class SnapshotCamera extends Generic { return ''; } - renderWidgetBody(props) { + renderWidgetBody(props: RxRenderWidgetProps): React.JSX.Element | null { super.renderWidgetBody(props); const url = this.getUrl(); @@ -339,7 +385,10 @@ class SnapshotCamera extends Generic { > {!this.state.alive ? (
- {Generic.t('Camera instance %s inactive', (this.state.rxData.camera || '').split('/')[0])} + {I18n.t( + `${TRANSLATION_PREFIX}Camera instance %s inactive`, + (this.state.rxData.camera || '').split('/')[0], + )}
) : null} {url ? ( @@ -350,7 +399,7 @@ class SnapshotCamera extends Generic { alt={this.state.rxData.camera} /> ) : ( - Generic.t('No camera selected') + I18n.t(`${TRANSLATION_PREFIX}No camera selected`) )} {this.state.alive && this.state.error ? (
-
- {Generic.t('Cannot load URL')} - : -
+
{I18n.t(`${TRANSLATION_PREFIX}Cannot load URL`)}:
{this.getUrl(true)}
) : null} diff --git a/src-widgets/src/VisRxWidget.tsx b/src-widgets/src/VisRxWidget.tsx new file mode 100644 index 0000000..d3aa705 --- /dev/null +++ b/src-widgets/src/VisRxWidget.tsx @@ -0,0 +1,157 @@ +import React, { Component, type JSX } from 'react'; +import type { + AnyWidgetId, + ResizeHandler, + RxRenderWidgetProps, + RxWidgetInfo, + RxWidgetInfoAttributesField, + RxWidgetInfoWriteable, + StateID, + VisViewProps, + VisWidgetCommand, + WidgetStyle, + Writeable, +} from '@iobroker/types-vis-2'; +import type { VisRxWidgetProps, VisRxWidgetState } from '@iobroker/types-vis-2/visRxWidget'; +import type { WidgetDataState } from '@iobroker/types-vis-2/visBaseWidget'; + +export interface WidgetStyleState extends WidgetStyle { + bindings?: string[]; + _originalData?: string; +} +export interface VisBaseWidgetState { + applyBindings?: + | false + | true + | { + top: string | number; + left: string | number; + }; + data: WidgetDataState; + draggable?: boolean; + editMode: boolean; + gap?: number; + hideHelper?: boolean; + isHidden?: boolean; + multiViewWidget?: boolean; + resizable?: boolean; + resizeHandles?: ResizeHandler[]; + rxStyle?: WidgetStyleState; + selected?: boolean; + selectedOne?: boolean; + showRelativeMoveMenu?: boolean; + style: WidgetStyleState; + usedInWidget: boolean; + widgetHint?: 'light' | 'dark' | 'hide'; +} + +class VisRxWidget< + TRxData extends Record, + TState extends Partial = VisRxWidgetState, +> extends Component { + static POSSIBLE_MUI_STYLES: string[]; + constructor(props: VisRxWidgetProps) { + super(props); + this.state = {} as TState & VisBaseWidgetState & { rxData: TRxData }; + } + // eslint-disable-next-line class-methods-use-this + componentDidMount(): void {} + + // eslint-disable-next-line class-methods-use-this + componentWillUnmount(): void {} + + static findField( + _widgetInfo: RxWidgetInfo | RxWidgetInfoWriteable, + _name: string, + ): Writeable | null { + return null; + } + static getI18nPrefix(): string { + return ''; + } + static getText(_text: string | ioBroker.Translated): string { + return ''; + } + static t(_key: string, ..._args: string[]): string { + return ''; + } + static getLanguage(): ioBroker.Languages { + return 'en'; + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + onCommand(_command: VisWidgetCommand, _option?: any): boolean { + return false; + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + onStateUpdated(_id: string, _state: ioBroker.State): void {} + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + onIoBrokerStateChanged(_id: StateID, _state: ioBroker.State | null | undefined): void {} + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + onStateChanged( + /** state object */ + _id?: StateID | null, + /** state value */ + _state?: ioBroker.State | null, + /** if state should not be set */ + _doNotApplyState?: boolean, + ): Partial< + VisRxWidgetState & + TState & { + rxData: TRxData; + } + > { + return {}; + } + // eslint-disable-next-line react/no-unused-class-component-methods + applyBinding(_stateId: string, _newState: typeof this.state): void {} + // eslint-disable-next-line react/no-unused-class-component-methods + onRxDataChanged(_prevRxData: typeof this.state.rxData): void {} + // eslint-disable-next-line react/no-unused-class-component-methods + onRxStyleChanged(_prevRxStyle: typeof this.state.rxStyle): void {} + + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + onPropertiesUpdated(): Promise { + return Promise.resolve(); + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + formatValue(value: number | string, _round: number): string { + return value.toString(); + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + wrapContent( + _content: React.JSX.Element | React.JSX.Element[], + _addToHeader?: React.JSX.Element | null | React.JSX.Element[], + _cardContentStyle?: React.CSSProperties, + _headerStyle?: React.CSSProperties, + _onCardClick?: (e?: React.MouseEvent) => void, + _components?: Record>, + ): JSX.Element { + return
; + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + renderWidgetBody(_props: RxRenderWidgetProps): React.JSX.Element | null { + return
; + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + getWidgetView(_view: string, _props?: Partial): JSX.Element { + return
; + } + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + getWidgetInWidget( + _view: string, + _wid: AnyWidgetId, + _props?: { + index?: number; + refParent?: React.RefObject; + isRelative?: boolean; + }, + ): JSX.Element { + return
; + } + + // eslint-disable-next-line class-methods-use-this,react/no-unused-class-component-methods + getWidgetInfo(): Readonly { + return {} as Readonly; + } +} +export default VisRxWidget; diff --git a/src-widgets/src/i18n/de.json b/src-widgets/src/i18n/de.json index b76ca8e..5de28be 100644 --- a/src-widgets/src/i18n/de.json +++ b/src-widgets/src/i18n/de.json @@ -1,23 +1,23 @@ { - "RTSP url": "RTSP-URL", - "RTSP Camera": "RTSP-Kamera", - "cameras_set_label": "RTSP-Kamera", - "name": "Name", - "without_card": "Ohne Rahmen", - "Cameras": "Kameras", - "Camera": "Kamera", - "Close": "Schließen", - "videoWidth": "Videobreite", - "tooltip_videoWidth": "Stellen Sie die Breite des Videos nicht zu hoch ein, um Breitband zu sparen. Es ist sowohl für kleine als auch für vollständige Videos geeignet. Wenn nicht festgelegt, wird die anfängliche Größe des Widgets verwendet.", - "Camera instance %s inactive": "Kamerainstanz %s inaktiv", - "disabled": "deaktiviert", - "pollingInterval": "Abrufintervall", - "pollingIntervalFull": "Abfrageintervall im Dialog", - "tooltip_ms": "Millisekunden", - "rotate": "Bild drehen", - "Polling Camera": "URL kamera", - "Cannot load URL": "URL kann nicht geladen werden", - "noCacheByFull": "Kein Cache im Dialog", - "camera_in_dialog": "Kamera im Dialog", - "No camera selected": "Keine Kamera ausgewählt" -} \ No newline at end of file + "RTSP url": "RTSP-URL", + "RTSP Camera": "RTSP-Kamera", + "cameras_set_label": "RTSP-Kamera", + "name": "Name", + "without_card": "Ohne Rahmen", + "Cameras": "Kameras", + "Camera": "Kamera", + "Close": "Schließen", + "videoWidth": "Videobreite", + "tooltip_videoWidth": "Stellen Sie die Breite des Videos nicht zu hoch ein, um Breitband zu sparen. Es ist sowohl für kleine als auch für vollständige Videos geeignet. Wenn nicht festgelegt, wird die anfängliche Größe des Widgets verwendet.", + "Camera instance %s inactive": "Kamerainstanz %s inaktiv", + "disabled": "deaktiviert", + "pollingInterval": "Abrufintervall", + "pollingIntervalFull": "Abfrageintervall im Dialog", + "tooltip_ms": "Millisekunden", + "rotate": "Bild drehen", + "Polling Camera": "URL kamera", + "Cannot load URL": "URL kann nicht geladen werden", + "noCacheByFull": "Kein Cache im Dialog", + "camera_in_dialog": "Kamera im Dialog", + "No camera selected": "Keine Kamera ausgewählt" +} diff --git a/src-widgets/src/i18n/en.json b/src-widgets/src/i18n/en.json index 99bbd69..5bf2e87 100644 --- a/src-widgets/src/i18n/en.json +++ b/src-widgets/src/i18n/en.json @@ -1,23 +1,23 @@ { - "RTSP Camera": "RTSP Camera", - "without_card": "Without frame", - "name": "Name", - "RTSP url": "RTSP url", - "cameras_set_label": "RTSP Camera", - "Cameras": "Cameras", - "Camera": "Camera", - "Close": "Close", - "videoWidth": "Video width", - "tooltip_videoWidth": "Do not set width of video too hight to save broadband. It is for both: small and full videos. If not set, the initial size of widget will be taken.", - "Camera instance %s inactive": "Camera instance %s inactive", - "disabled": "disabled", - "pollingInterval": "Polling interval", - "pollingIntervalFull": "Polling interval in dialog", - "tooltip_ms": "milliseconds", - "rotate": "Rotate image", - "Polling Camera": "Polling Camera", - "Cannot load URL": "Cannot load URL", - "noCacheByFull": "No cache in dialog", - "camera_in_dialog": "Camera in Dialog", - "No camera selected": "No camera selected" -} \ No newline at end of file + "RTSP Camera": "RTSP Camera", + "without_card": "Without frame", + "name": "Name", + "RTSP url": "RTSP url", + "cameras_set_label": "RTSP Camera", + "Cameras": "Cameras", + "Camera": "Camera", + "Close": "Close", + "videoWidth": "Video width", + "tooltip_videoWidth": "Do not set width of video too hight to save broadband. It is for both: small and full videos. If not set, the initial size of widget will be taken.", + "Camera instance %s inactive": "Camera instance %s inactive", + "disabled": "disabled", + "pollingInterval": "Polling interval", + "pollingIntervalFull": "Polling interval in dialog", + "tooltip_ms": "milliseconds", + "rotate": "Rotate image", + "Polling Camera": "Polling Camera", + "Cannot load URL": "Cannot load URL", + "noCacheByFull": "No cache in dialog", + "camera_in_dialog": "Camera in Dialog", + "No camera selected": "No camera selected" +} diff --git a/src-widgets/src/i18n/es.json b/src-widgets/src/i18n/es.json index f427e42..7fdf44a 100644 --- a/src-widgets/src/i18n/es.json +++ b/src-widgets/src/i18n/es.json @@ -1,23 +1,23 @@ { - "RTSP url": "URL de RTSP", - "RTSP Camera": "Cámara RTSP", - "cameras_set_label": "Cámara RTSP", - "name": "Nombre", - "without_card": "sin marco", - "Cameras": "Cámaras", - "Camera": "Cámara", - "Close": "Cerca", - "videoWidth": "Ancho de vídeo", - "tooltip_videoWidth": "No configure el ancho del video demasiado alto para ahorrar banda ancha. Es para ambos: videos pequeños y completos. Si no se establece, se tomará el tamaño inicial del widget.", - "Camera instance %s inactive": "Instancia de cámara %s inactiva", - "disabled": "desactivado", - "pollingInterval": "Intervalo de votación", - "pollingIntervalFull": "Intervalo de sondeo en el diálogo", - "tooltip_ms": "milisegundos", - "rotate": "Girar imagen", - "Polling Camera": "Cámara de sondeo", - "Cannot load URL": "No se puede cargar la URL", - "noCacheByFull": "No hay caché en el diálogo", - "camera_in_dialog": "Cámara en diálogo", - "No camera selected": "Ninguna cámara seleccionada" -} \ No newline at end of file + "RTSP url": "URL de RTSP", + "RTSP Camera": "Cámara RTSP", + "cameras_set_label": "Cámara RTSP", + "name": "Nombre", + "without_card": "sin marco", + "Cameras": "Cámaras", + "Camera": "Cámara", + "Close": "Cerca", + "videoWidth": "Ancho de vídeo", + "tooltip_videoWidth": "No configure el ancho del video demasiado alto para ahorrar banda ancha. Es para ambos: videos pequeños y completos. Si no se establece, se tomará el tamaño inicial del widget.", + "Camera instance %s inactive": "Instancia de cámara %s inactiva", + "disabled": "desactivado", + "pollingInterval": "Intervalo de votación", + "pollingIntervalFull": "Intervalo de sondeo en el diálogo", + "tooltip_ms": "milisegundos", + "rotate": "Girar imagen", + "Polling Camera": "Cámara de sondeo", + "Cannot load URL": "No se puede cargar la URL", + "noCacheByFull": "No hay caché en el diálogo", + "camera_in_dialog": "Cámara en diálogo", + "No camera selected": "Ninguna cámara seleccionada" +} diff --git a/src-widgets/src/i18n/fr.json b/src-widgets/src/i18n/fr.json index 1bc064e..f768f63 100644 --- a/src-widgets/src/i18n/fr.json +++ b/src-widgets/src/i18n/fr.json @@ -1,23 +1,23 @@ { - "RTSP url": "URL RTSP", - "RTSP Camera": "Caméra RTSP", - "cameras_set_label": "Caméra RTSP", - "name": "Nom", - "without_card": "Sans cadre", - "Cameras": "Appareils photo", - "Camera": "Caméra", - "Close": "Fermer", - "videoWidth": "Largeur vidéo", - "tooltip_videoWidth": "Ne définissez pas une largeur de vidéo trop élevée pour économiser le haut débit. C'est pour les deux : petites et complètes vidéos. S'il n'est pas défini, la taille initiale du widget sera prise.", - "Camera instance %s inactive": "Instance de caméra %s inactive", - "disabled": "désactivé", - "pollingInterval": "Intervalle d'interrogation", - "pollingIntervalFull": "Intervalle d'interrogation dans la boîte de dialogue", - "tooltip_ms": "millisecondes", - "rotate": "Faire pivoter l'image", - "Polling Camera": "Caméra de sondage", - "Cannot load URL": "Impossible de charger l'URL", - "noCacheByFull": "Pas de cache dans la boîte de dialogue", - "camera_in_dialog": "Caméra dans la boîte de dialogue", - "No camera selected": "Aucune caméra sélectionnée" -} \ No newline at end of file + "RTSP url": "URL RTSP", + "RTSP Camera": "Caméra RTSP", + "cameras_set_label": "Caméra RTSP", + "name": "Nom", + "without_card": "Sans cadre", + "Cameras": "Appareils photo", + "Camera": "Caméra", + "Close": "Fermer", + "videoWidth": "Largeur vidéo", + "tooltip_videoWidth": "Ne définissez pas une largeur de vidéo trop élevée pour économiser le haut débit. C'est pour les deux : petites et complètes vidéos. S'il n'est pas défini, la taille initiale du widget sera prise.", + "Camera instance %s inactive": "Instance de caméra %s inactive", + "disabled": "désactivé", + "pollingInterval": "Intervalle d'interrogation", + "pollingIntervalFull": "Intervalle d'interrogation dans la boîte de dialogue", + "tooltip_ms": "millisecondes", + "rotate": "Faire pivoter l'image", + "Polling Camera": "Caméra de sondage", + "Cannot load URL": "Impossible de charger l'URL", + "noCacheByFull": "Pas de cache dans la boîte de dialogue", + "camera_in_dialog": "Caméra dans la boîte de dialogue", + "No camera selected": "Aucune caméra sélectionnée" +} diff --git a/src-widgets/src/i18n/it.json b/src-widgets/src/i18n/it.json index e55447f..eb6318b 100644 --- a/src-widgets/src/i18n/it.json +++ b/src-widgets/src/i18n/it.json @@ -1,23 +1,23 @@ { - "RTSP url": "URL RTSP", - "RTSP Camera": "Fotocamera RTSP", - "cameras_set_label": "Fotocamera RTSP", - "name": "Nome", - "without_card": "Senza cornice", - "Cameras": "Macchine fotografiche", - "Camera": "Telecamera", - "Close": "Vicino", - "videoWidth": "Larghezza video", - "tooltip_videoWidth": "Non impostare la larghezza del video troppo alta per salvare la banda larga. È per entrambi: video piccoli e completi. Se non impostato, verrà utilizzata la dimensione iniziale del widget.", - "Camera instance %s inactive": "Istanza fotocamera %s inattiva", - "disabled": "Disabilitato", - "pollingInterval": "Intervallo di polling", - "pollingIntervalFull": "Intervallo di polling nella finestra di dialogo", - "tooltip_ms": "millisecondi", - "rotate": "Ruota l'immagine", - "Polling Camera": "Telecamera per sondaggi", - "Cannot load URL": "Impossibile caricare l'URL", - "noCacheByFull": "Nessuna cache nella finestra di dialogo", - "camera_in_dialog": "Fotocamera nella finestra di dialogo", - "No camera selected": "Nessuna telecamera selezionata" -} \ No newline at end of file + "RTSP url": "URL RTSP", + "RTSP Camera": "Fotocamera RTSP", + "cameras_set_label": "Fotocamera RTSP", + "name": "Nome", + "without_card": "Senza cornice", + "Cameras": "Macchine fotografiche", + "Camera": "Telecamera", + "Close": "Vicino", + "videoWidth": "Larghezza video", + "tooltip_videoWidth": "Non impostare la larghezza del video troppo alta per salvare la banda larga. È per entrambi: video piccoli e completi. Se non impostato, verrà utilizzata la dimensione iniziale del widget.", + "Camera instance %s inactive": "Istanza fotocamera %s inattiva", + "disabled": "Disabilitato", + "pollingInterval": "Intervallo di polling", + "pollingIntervalFull": "Intervallo di polling nella finestra di dialogo", + "tooltip_ms": "millisecondi", + "rotate": "Ruota l'immagine", + "Polling Camera": "Telecamera per sondaggi", + "Cannot load URL": "Impossibile caricare l'URL", + "noCacheByFull": "Nessuna cache nella finestra di dialogo", + "camera_in_dialog": "Fotocamera nella finestra di dialogo", + "No camera selected": "Nessuna telecamera selezionata" +} diff --git a/src-widgets/src/i18n/nl.json b/src-widgets/src/i18n/nl.json index 3bdc872..67e4981 100644 --- a/src-widgets/src/i18n/nl.json +++ b/src-widgets/src/i18n/nl.json @@ -1,23 +1,23 @@ { - "RTSP url": "RTSP-URL", - "RTSP Camera": "RTSP-camera", - "cameras_set_label": "RTSP-camera", - "name": "Naam", - "without_card": "Zonder lijst", - "Cameras": "Camera's", - "Camera": "Camera", - "Close": "Dichtbij", - "videoWidth": "Videobreedte", - "tooltip_videoWidth": "Stel de breedte van de video niet te hoog in om breedband te besparen. Het is voor beide: kleine en volledige video's. Als dit niet is ingesteld, wordt de oorspronkelijke grootte van de widget gebruikt.", - "Camera instance %s inactive": "Camera-instantie %s inactief", - "disabled": "gehandicapt", - "pollingInterval": "Polling-interval", - "pollingIntervalFull": "Polling-interval in dialoogvenster", - "tooltip_ms": "milliseconden", - "rotate": "Afbeelding roteren", - "Polling Camera": "Pollingcamera", - "Cannot load URL": "Kan URL niet laden", - "noCacheByFull": "Geen cache in dialoogvenster", - "camera_in_dialog": "Camera in dialoog", - "No camera selected": "Geen camera geselecteerd" -} \ No newline at end of file + "RTSP url": "RTSP-URL", + "RTSP Camera": "RTSP-camera", + "cameras_set_label": "RTSP-camera", + "name": "Naam", + "without_card": "Zonder lijst", + "Cameras": "Camera's", + "Camera": "Camera", + "Close": "Dichtbij", + "videoWidth": "Videobreedte", + "tooltip_videoWidth": "Stel de breedte van de video niet te hoog in om breedband te besparen. Het is voor beide: kleine en volledige video's. Als dit niet is ingesteld, wordt de oorspronkelijke grootte van de widget gebruikt.", + "Camera instance %s inactive": "Camera-instantie %s inactief", + "disabled": "gehandicapt", + "pollingInterval": "Polling-interval", + "pollingIntervalFull": "Polling-interval in dialoogvenster", + "tooltip_ms": "milliseconden", + "rotate": "Afbeelding roteren", + "Polling Camera": "Pollingcamera", + "Cannot load URL": "Kan URL niet laden", + "noCacheByFull": "Geen cache in dialoogvenster", + "camera_in_dialog": "Camera in dialoog", + "No camera selected": "Geen camera geselecteerd" +} diff --git a/src-widgets/src/i18n/pl.json b/src-widgets/src/i18n/pl.json index dcdc471..f515eb0 100644 --- a/src-widgets/src/i18n/pl.json +++ b/src-widgets/src/i18n/pl.json @@ -1,23 +1,23 @@ { - "RTSP url": "URL RTSP", - "RTSP Camera": "Kamera RTSP", - "cameras_set_label": "Kamera RTSP", - "name": "Nazwa", - "without_card": "Bez ramy", - "Cameras": "kamery", - "Camera": "Kamera", - "Close": "Zamknąć", - "videoWidth": "Szerokość wideo", - "tooltip_videoWidth": "Nie ustawiaj zbyt dużej szerokości wideo, aby zaoszczędzić łącze szerokopasmowe. Dotyczy to zarówno małych, jak i pełnych filmów. Jeśli nie ustawiono, zostanie wzięty początkowy rozmiar widżetu.", - "Camera instance %s inactive": "Instancja kamery %s nieaktywna", - "disabled": "wyłączony", - "pollingInterval": "Interwał odpytywania", - "pollingIntervalFull": "Interwał odpytywania w oknie dialogowym", - "tooltip_ms": "milisekundy", - "rotate": "Obróć obraz", - "Polling Camera": "Kamera wyborcza", - "Cannot load URL": "Nie można załadować adresu URL", - "noCacheByFull": "Brak pamięci podręcznej w oknie dialogowym", - "camera_in_dialog": "Kamera w oknie dialogowym", - "No camera selected": "Nie wybrano kamery" -} \ No newline at end of file + "RTSP url": "URL RTSP", + "RTSP Camera": "Kamera RTSP", + "cameras_set_label": "Kamera RTSP", + "name": "Nazwa", + "without_card": "Bez ramy", + "Cameras": "kamery", + "Camera": "Kamera", + "Close": "Zamknąć", + "videoWidth": "Szerokość wideo", + "tooltip_videoWidth": "Nie ustawiaj zbyt dużej szerokości wideo, aby zaoszczędzić łącze szerokopasmowe. Dotyczy to zarówno małych, jak i pełnych filmów. Jeśli nie ustawiono, zostanie wzięty początkowy rozmiar widżetu.", + "Camera instance %s inactive": "Instancja kamery %s nieaktywna", + "disabled": "wyłączony", + "pollingInterval": "Interwał odpytywania", + "pollingIntervalFull": "Interwał odpytywania w oknie dialogowym", + "tooltip_ms": "milisekundy", + "rotate": "Obróć obraz", + "Polling Camera": "Kamera wyborcza", + "Cannot load URL": "Nie można załadować adresu URL", + "noCacheByFull": "Brak pamięci podręcznej w oknie dialogowym", + "camera_in_dialog": "Kamera w oknie dialogowym", + "No camera selected": "Nie wybrano kamery" +} diff --git a/src-widgets/src/i18n/pt.json b/src-widgets/src/i18n/pt.json index 6df9a13..643428b 100644 --- a/src-widgets/src/i18n/pt.json +++ b/src-widgets/src/i18n/pt.json @@ -1,23 +1,23 @@ { - "RTSP url": "url RTSP", - "RTSP Camera": "Câmera RTSP", - "cameras_set_label": "Câmera RTSP", - "name": "Nome", - "without_card": "sem moldura", - "Cameras": "Câmeras", - "Camera": "Câmera", - "Close": "Fechar", - "videoWidth": "Largura do vídeo", - "tooltip_videoWidth": "Não defina a largura do vídeo muito alta para economizar banda larga. É para ambos: vídeos pequenos e completos. Se não for definido, o tamanho inicial do widget será usado.", - "Camera instance %s inactive": "Instância da câmera %s inativa", - "disabled": "desabilitado", - "pollingInterval": "Intervalo de votação", - "pollingIntervalFull": "Intervalo de pesquisa na caixa de diálogo", - "tooltip_ms": "milissegundos", - "rotate": "Girar imagem", - "Polling Camera": "Câmera de votação", - "Cannot load URL": "Não é possível carregar o URL", - "noCacheByFull": "Sem cache na caixa de diálogo", - "camera_in_dialog": "Câmera na caixa de diálogo", - "No camera selected": "Nenhuma câmera selecionada" -} \ No newline at end of file + "RTSP url": "url RTSP", + "RTSP Camera": "Câmera RTSP", + "cameras_set_label": "Câmera RTSP", + "name": "Nome", + "without_card": "sem moldura", + "Cameras": "Câmeras", + "Camera": "Câmera", + "Close": "Fechar", + "videoWidth": "Largura do vídeo", + "tooltip_videoWidth": "Não defina a largura do vídeo muito alta para economizar banda larga. É para ambos: vídeos pequenos e completos. Se não for definido, o tamanho inicial do widget será usado.", + "Camera instance %s inactive": "Instância da câmera %s inativa", + "disabled": "desabilitado", + "pollingInterval": "Intervalo de votação", + "pollingIntervalFull": "Intervalo de pesquisa na caixa de diálogo", + "tooltip_ms": "milissegundos", + "rotate": "Girar imagem", + "Polling Camera": "Câmera de votação", + "Cannot load URL": "Não é possível carregar o URL", + "noCacheByFull": "Sem cache na caixa de diálogo", + "camera_in_dialog": "Câmera na caixa de diálogo", + "No camera selected": "Nenhuma câmera selecionada" +} diff --git a/src-widgets/src/i18n/ru.json b/src-widgets/src/i18n/ru.json index ff1f888..7eafdf4 100644 --- a/src-widgets/src/i18n/ru.json +++ b/src-widgets/src/i18n/ru.json @@ -1,23 +1,23 @@ { - "RTSP url": "RTSP-адрес", - "RTSP Camera": "RTSP-камера", - "cameras_set_label": "RTSP-камера", - "name": "Имя", - "without_card": "Без рамки", - "Cameras": "Камеры", - "Camera": "Камера", - "Close": "Закрывать", - "videoWidth": "Ширина видео", - "tooltip_videoWidth": "Не устанавливайте слишком большую ширину видео, чтобы сохранить широкополосный доступ. Он подходит как для маленьких, так и для полных видео. Если не установлено, будет взят начальный размер виджета.", - "Camera instance %s inactive": "Экземпляр камеры %s неактивен", - "disabled": "неполноценный", - "pollingInterval": "Интервал опроса", - "pollingIntervalFull": "Интервал опроса в диалоге", - "tooltip_ms": "миллисекунды", - "rotate": "Поворот изображения", - "Polling Camera": "URL-Камера", - "Cannot load URL": "Невозможно загрузить URL", - "noCacheByFull": "Нет кеша в диалоге", - "camera_in_dialog": "Камера в диалоге", - "No camera selected": "Камера не выбрана" -} \ No newline at end of file + "RTSP url": "RTSP-адрес", + "RTSP Camera": "RTSP-камера", + "cameras_set_label": "RTSP-камера", + "name": "Имя", + "without_card": "Без рамки", + "Cameras": "Камеры", + "Camera": "Камера", + "Close": "Закрывать", + "videoWidth": "Ширина видео", + "tooltip_videoWidth": "Не устанавливайте слишком большую ширину видео, чтобы сохранить широкополосный доступ. Он подходит как для маленьких, так и для полных видео. Если не установлено, будет взят начальный размер виджета.", + "Camera instance %s inactive": "Экземпляр камеры %s неактивен", + "disabled": "неполноценный", + "pollingInterval": "Интервал опроса", + "pollingIntervalFull": "Интервал опроса в диалоге", + "tooltip_ms": "миллисекунды", + "rotate": "Поворот изображения", + "Polling Camera": "URL-Камера", + "Cannot load URL": "Невозможно загрузить URL", + "noCacheByFull": "Нет кеша в диалоге", + "camera_in_dialog": "Камера в диалоге", + "No camera selected": "Камера не выбрана" +} diff --git a/src-widgets/src/i18n/uk.json b/src-widgets/src/i18n/uk.json index 7facce7..ad6806a 100644 --- a/src-widgets/src/i18n/uk.json +++ b/src-widgets/src/i18n/uk.json @@ -1,23 +1,23 @@ { - "RTSP url": "URL-адреса RTSP", - "RTSP Camera": "Камера RTSP", - "cameras_set_label": "Камера RTSP", - "name": "Ім'я", - "without_card": "Без рами", - "Cameras": "Фотоапарати", - "Camera": "Камера", - "Close": "Закрити", - "videoWidth": "Ширина відео", - "tooltip_videoWidth": "Не встановлюйте занадто високу ширину відео, щоб зберегти широкосмуговий зв’язок. Це як для маленьких, так і для повних відео. Якщо не встановлено, буде взято початковий розмір віджета.", - "Camera instance %s inactive": "Примірник камери %s неактивний", - "disabled": "вимкнено", - "pollingInterval": "Інтервал опитування", - "pollingIntervalFull": "Інтервал опитування в діалоговому вікні", - "tooltip_ms": "мілісекунд", - "rotate": "Повернути зображення", - "Polling Camera": "Камера опитування", - "Cannot load URL": "Не вдається завантажити URL", - "noCacheByFull": "У діалоговому вікні немає кешу", - "camera_in_dialog": "Камера в діалозі", - "No camera selected": "Камера не вибрана" -} \ No newline at end of file + "RTSP url": "URL-адреса RTSP", + "RTSP Camera": "Камера RTSP", + "cameras_set_label": "Камера RTSP", + "name": "Ім'я", + "without_card": "Без рами", + "Cameras": "Фотоапарати", + "Camera": "Камера", + "Close": "Закрити", + "videoWidth": "Ширина відео", + "tooltip_videoWidth": "Не встановлюйте занадто високу ширину відео, щоб зберегти широкосмуговий зв’язок. Це як для маленьких, так і для повних відео. Якщо не встановлено, буде взято початковий розмір віджета.", + "Camera instance %s inactive": "Примірник камери %s неактивний", + "disabled": "вимкнено", + "pollingInterval": "Інтервал опитування", + "pollingIntervalFull": "Інтервал опитування в діалоговому вікні", + "tooltip_ms": "мілісекунд", + "rotate": "Повернути зображення", + "Polling Camera": "Камера опитування", + "Cannot load URL": "Не вдається завантажити URL", + "noCacheByFull": "У діалоговому вікні немає кешу", + "camera_in_dialog": "Камера в діалозі", + "No camera selected": "Камера не вибрана" +} diff --git a/src-widgets/src/i18n/zh-cn.json b/src-widgets/src/i18n/zh-cn.json index 5b78898..23ea1a3 100644 --- a/src-widgets/src/i18n/zh-cn.json +++ b/src-widgets/src/i18n/zh-cn.json @@ -1,23 +1,23 @@ { - "RTSP url": "RTSP 网址", - "RTSP Camera": "RTSP摄像头", - "cameras_set_label": "RTSP摄像头", - "name": "姓名", - "without_card": "无框", - "Cameras": "相机", - "Camera": "相机", - "Close": "关闭", - "videoWidth": "视频宽度", - "tooltip_videoWidth": "不要将视频宽度设置得太高,以节省宽带。它适用于:小视频和完整视频。如果未设置,则将采用小部件的初始大小。", - "Camera instance %s inactive": "相机实例 %s 不活动", - "disabled": "残疾人", - "pollingInterval": "轮询间隔", - "pollingIntervalFull": "对话框中的轮询间隔", - "tooltip_ms": "毫秒", - "rotate": "旋转图像", - "Polling Camera": "投票摄像头", - "Cannot load URL": "无法加载网址", - "noCacheByFull": "对话框中没有缓存", - "camera_in_dialog": "相机对话", - "No camera selected": "未选择相机" -} \ No newline at end of file + "RTSP url": "RTSP 网址", + "RTSP Camera": "RTSP摄像头", + "cameras_set_label": "RTSP摄像头", + "name": "姓名", + "without_card": "无框", + "Cameras": "相机", + "Camera": "相机", + "Close": "关闭", + "videoWidth": "视频宽度", + "tooltip_videoWidth": "不要将视频宽度设置得太高,以节省宽带。它适用于:小视频和完整视频。如果未设置,则将采用小部件的初始大小。", + "Camera instance %s inactive": "相机实例 %s 不活动", + "disabled": "残疾人", + "pollingInterval": "轮询间隔", + "pollingIntervalFull": "对话框中的轮询间隔", + "tooltip_ms": "毫秒", + "rotate": "旋转图像", + "Polling Camera": "投票摄像头", + "Cannot load URL": "无法加载网址", + "noCacheByFull": "对话框中没有缓存", + "camera_in_dialog": "相机对话", + "No camera selected": "未选择相机" +} diff --git a/src-widgets/src/index.jsx b/src-widgets/src/index.tsx similarity index 100% rename from src-widgets/src/index.jsx rename to src-widgets/src/index.tsx diff --git a/src-widgets/src/translations.js b/src-widgets/src/translations.js deleted file mode 100644 index 6da5683..0000000 --- a/src-widgets/src/translations.js +++ /dev/null @@ -1,28 +0,0 @@ -import en from './i18n/en'; -import de from './i18n/de'; -import ru from './i18n/ru'; -import pt from './i18n/pt'; -import nl from './i18n/nl'; -import fr from './i18n/fr'; -import it from './i18n/it'; -import es from './i18n/es'; -import pl from './i18n/pl'; -import uk from './i18n/uk'; -import zhcn from './i18n/zh-cn'; - -const translations = { - en, - de, - ru, - pt, - nl, - fr, - it, - es, - pl, - 'zh-cn': zhcn, - uk, - prefix: 'cameras_', -}; - -export default translations; diff --git a/src-widgets/src/translations.tsx b/src-widgets/src/translations.tsx new file mode 100644 index 0000000..314ee95 --- /dev/null +++ b/src-widgets/src/translations.tsx @@ -0,0 +1,28 @@ +import en from './i18n/en.json'; +import de from './i18n/de.json'; +import ru from './i18n/ru.json'; +import pt from './i18n/pt.json'; +import nl from './i18n/nl.json'; +import fr from './i18n/fr.json'; +import it from './i18n/it.json'; +import es from './i18n/es.json'; +import pl from './i18n/pl.json'; +import uk from './i18n/uk.json'; +import zhcn from './i18n/zh-cn.json'; + +const translations = { + en, + de, + ru, + pt, + nl, + fr, + it, + es, + pl, + 'zh-cn': zhcn, + uk, + prefix: 'cameras_', +}; + +export default translations; diff --git a/src-widgets/tsconfig.json b/src-widgets/tsconfig.json new file mode 100644 index 0000000..140a6be --- /dev/null +++ b/src-widgets/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "baseUrl": "./", + "types": ["@iobroker/types", "vite/client"] + }, + "include": ["src"], + "exclude": ["node_modules", "build"] +} diff --git a/src-widgets/vite.config.ts b/src-widgets/vite.config.ts new file mode 100644 index 0000000..35a6787 --- /dev/null +++ b/src-widgets/vite.config.ts @@ -0,0 +1,93 @@ +import federation from '@originjs/vite-plugin-federation'; +import type { UserConfig } from 'vite'; +import topLevelAwait from 'vite-plugin-top-level-await'; + +export interface RollupLog { + binding?: string; + cause?: unknown; + code?: string; + exporter?: string; + frame?: string; + hook?: string; + id?: string; + ids?: string[]; + loc?: { + column: number; + file?: string; + line: number; + }; + message: string; + meta?: any; + names?: string[]; + plugin?: string; + pluginCode?: unknown; + pos?: number; + reexporter?: string; + stack?: string; + url?: string; +} + +export default { + plugins: [ + topLevelAwait({ + // The export name of top-level await promise for each chunk module + promiseExportName: 'vis2CameraWidgets', + // The function to generate import names of top-level await promise in each chunk module + promiseImportName: i => `vis2CameraWidgets_${i}` + }), + federation({ + name: 'vis2CameraWidgets', + filename: 'customWidgets.js', + exposes: { + './RtspCamera': './src/RtspCamera', + './SnapshotCamera': './src/SnapshotCamera', + './translations': './src/translations', + }, + shared: [ + '@iobroker/adapter-react-v5', + '@iobroker/adapter-react-v5/i18n/de.json', + '@iobroker/adapter-react-v5/i18n/en.json', + '@iobroker/adapter-react-v5/i18n/es.json', + '@iobroker/adapter-react-v5/i18n/ru.json', + '@iobroker/adapter-react-v5/i18n/nl.json', + '@iobroker/adapter-react-v5/i18n/it.json', + '@iobroker/adapter-react-v5/i18n/pl.json', + '@iobroker/adapter-react-v5/i18n/pt.json', + '@iobroker/adapter-react-v5/i18n/fr.json', + '@iobroker/adapter-react-v5/i18n/uk.json', + '@iobroker/adapter-react-v5/i18n/zh-cn.json', + '@mui/icons-material', + '@mui/material', + '@mui/system', + 'prop-types', + 'react', + 'react-ace', + 'react-dom', + 'react-dom/client', + ], + }), + ], + build: { + modulePreload: false, + target: 'es2015', + minify: false, + cssCodeSplit: false, + outDir: 'build', + sourcemap: true, + rollupOptions: { + onwarn: (warning: RollupLog, warn: (log: RollupLog) => void) => { + if (warning.code === 'MODULE_LEVEL_DIRECTIVE' || warning.code === 'SOURCEMAP_ERROR') { + return; + } + if (warning.message?.includes('_socket/info.js')) { + return; + } + warn(warning); + }, + }, + }, + base: './', + server: { + port: 3000, + }, +} satisfies UserConfig; diff --git a/src/package.json b/src/package.json index 09191a3..73a57c5 100644 --- a/src/package.json +++ b/src/package.json @@ -23,7 +23,8 @@ "build": "tsc && vite build", "preview": "vite preview", "lint": "eslint -c eslint.config.mjs src", - "tsc": "tsc -p tsconfig.json src" + "tsc": "tsc -p tsconfig.json", + "prettier": "prettier -c prettier.config.mjs --write src" }, "eslintConfig": { "extends": "react-app" @@ -39,4 +40,4 @@ "not ie <= 11", "not op_mini all" ] -} \ No newline at end of file +} diff --git a/src/src/App.tsx b/src/src/App.tsx index 1bb3221..6686a36 100644 --- a/src/src/App.tsx +++ b/src/src/App.tsx @@ -142,7 +142,7 @@ class App extends GenericApp { > this.setState({ selectedTab })} sx={{ '& .MuiTabs-indicator': styles.indicator }} > diff --git a/src/src/i18n/de.json b/src/src/i18n/de.json index 90c2272..3f5bfac 100644 --- a/src/src/i18n/de.json +++ b/src/src/i18n/de.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Bist du sicher?", - "Cancel": "Abbrechen", - "Close": "Schließen", - "Error": "Fehler", - "Load configuration from file": "Konfiguration aus Datei laden", - "Ok": "Ok", - "Options": "Optionen", - "Save": "speichern", - "Save and close": "Speichern und schließen", - "Save configuration to file": "Konfiguration in Datei speichern", - "Success": "Erfolgreich", - "Test connection": "Testverbindung", - "Type": "Typ", - "not connected": "nicht verbunden", - "Description": "Beschreibung", - "Name": "Name", - "Test": "Prüfen", - "Apply": "Speichern", - "Username": "Nutzername", - "Password": "Passwort", - "Edit camera %s [%s]": "Kamera bearbeiten %s [%s]", - "Cameras": "Kameras", - "Local IP address": "Lokale IP-Adresse", - "Local port": "Lokaler Hafen", - "Default timeout (ms)": "Standard-Timeout (ms)", - "WEB Instance": "WEB-Instanz", - "All": "Alles", - "Path to ffpmeg executable": "Pfad zur ausführbaren ffpmeg-Datei", - "Like /usr/bin/ffmpeg": "Wie \"/usr/bin/ffmpeg\"", - "Test path": "Testpfad", - "Path to store temporary images": "Pfad zum Speichern temporärer Bilder", - "If empty then in adapter folder": "Wenn leer, dann im Adapterordner", - "Default cache timeout (ms)": "Standard-Cache-Timeout (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Wie oft die Kameras nach neuen Schnappschüssen gefragt werden. Wenn 0, dann bei jeder Anfrage", - "URL": "URL", - "URL with basic auth": "URL mit einfacher Auth", - "RTSP Snapshot": "RTSP-Snapshot", - "Local URL": "Lokale URL", - "Web URL": "Web-URL", - "Camera IP": "Kamera-IP", - "Port": "Hafen", - "Path": "Weg", - "Cache timeout (ms)": "Cache-Timeout (ms)", - "If empty, use default settings. If 0, cache disabled": "Wenn leer, Standardeinstellungen verwenden. Wenn 0, Cache deaktiviert", - "Camera URL": "Kamera-URL", - "Add new camera": "Neue Kamera hinzufügen", - "Add time to screenshot": "Zeit zum Screenshot hinzufügen", - "Time format": "Zeitformat", - "See here:": "Siehe hier:", - "Add title": "Titel hinzufügen", - "Height": "Höhe", - "Width": "Breite", - "in pixels": "in Pixel", - "Expert settings": "Experteneinstellungen", - "Reolink E1 Snapshot": "Reolink E1 Schnappschuss", - "Eufy Security": "Eufy-Sicherheit", - "From eusec adapter": "Vom Eusec-Adapter", - "By IP Address": "Nach IP-Adresse", - "Camera OID": "Kameraobjekt-ID", - "Protocol": "Protokoll", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Anforderungszeitüberschreitung (ms)", - "If empty or 0, use default settings.": "Wenn leer oder 0, verwenden Sie die Standardeinstellungen." + "Are you sure?": "Bist du sicher?", + "Cancel": "Abbrechen", + "Close": "Schließen", + "Error": "Fehler", + "Load configuration from file": "Konfiguration aus Datei laden", + "Ok": "Ok", + "Options": "Optionen", + "Save": "speichern", + "Save and close": "Speichern und schließen", + "Save configuration to file": "Konfiguration in Datei speichern", + "Success": "Erfolgreich", + "Test connection": "Testverbindung", + "Type": "Typ", + "not connected": "nicht verbunden", + "Description": "Beschreibung", + "Name": "Name", + "Test": "Prüfen", + "Apply": "Speichern", + "Username": "Nutzername", + "Password": "Passwort", + "Edit camera %s [%s]": "Kamera bearbeiten %s [%s]", + "Cameras": "Kameras", + "Local IP address": "Lokale IP-Adresse", + "Local port": "Lokaler Hafen", + "Default timeout (ms)": "Standard-Timeout (ms)", + "WEB Instance": "WEB-Instanz", + "All": "Alles", + "Path to ffpmeg executable": "Pfad zur ausführbaren ffpmeg-Datei", + "Like /usr/bin/ffmpeg": "Wie \"/usr/bin/ffmpeg\"", + "Test path": "Testpfad", + "Path to store temporary images": "Pfad zum Speichern temporärer Bilder", + "If empty then in adapter folder": "Wenn leer, dann im Adapterordner", + "Default cache timeout (ms)": "Standard-Cache-Timeout (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Wie oft die Kameras nach neuen Schnappschüssen gefragt werden. Wenn 0, dann bei jeder Anfrage", + "URL": "URL", + "URL with basic auth": "URL mit einfacher Auth", + "RTSP Snapshot": "RTSP-Snapshot", + "Local URL": "Lokale URL", + "Web URL": "Web-URL", + "Camera IP": "Kamera-IP", + "Port": "Hafen", + "Path": "Weg", + "Cache timeout (ms)": "Cache-Timeout (ms)", + "If empty, use default settings. If 0, cache disabled": "Wenn leer, Standardeinstellungen verwenden. Wenn 0, Cache deaktiviert", + "Camera URL": "Kamera-URL", + "Add new camera": "Neue Kamera hinzufügen", + "Add time to screenshot": "Zeit zum Screenshot hinzufügen", + "Time format": "Zeitformat", + "See here:": "Siehe hier:", + "Add title": "Titel hinzufügen", + "Height": "Höhe", + "Width": "Breite", + "in pixels": "in Pixel", + "Expert settings": "Experteneinstellungen", + "Reolink E1 Snapshot": "Reolink E1 Schnappschuss", + "Eufy Security": "Eufy-Sicherheit", + "From eusec adapter": "Vom Eusec-Adapter", + "By IP Address": "Nach IP-Adresse", + "Camera OID": "Kameraobjekt-ID", + "Protocol": "Protokoll", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Anforderungszeitüberschreitung (ms)", + "If empty or 0, use default settings.": "Wenn leer oder 0, verwenden Sie die Standardeinstellungen." } diff --git a/src/src/i18n/en.json b/src/src/i18n/en.json index 4251b4d..aa24307 100644 --- a/src/src/i18n/en.json +++ b/src/src/i18n/en.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Are you sure?", - "Cancel": "Cancel", - "Close": "Close", - "Error": "Error", - "Load configuration from file": "Load configuration from file", - "Ok": "Ok", - "Options": "Options", - "Save": "Save", - "Save and close": "Save and close", - "Save configuration to file": "Save configuration to file", - "Success": "Success", - "Test connection": "Test connection", - "Type": "Type", - "not connected": "not connected", - "Description": "Description", - "Name": "Name", - "Test": "Test", - "Apply": "Apply", - "Username": "User name", - "Password": "Password", - "Edit camera %s [%s]": "Edit camera %s [%s]", - "Cameras": "Cameras", - "Local IP address": "Local IP address", - "Local port": "Local port", - "Default timeout (ms)": "Default timeout (ms)", - "WEB Instance": "WEB Instance", - "All": "All", - "Path to ffpmeg executable": "Path to ffpmeg executable", - "Like /usr/bin/ffmpeg": "Like \"/usr/bin/ffmpeg\"", - "Test path": "Test path", - "Path to store temporary images": "Path to store temporary images", - "If empty then in adapter folder": "If empty then in adapter folder", - "Default cache timeout (ms)": "Default cache timeout (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "How often the cameras will be asked for new snapshot. If 0, then by every request", - "URL": "URL", - "URL with basic auth": "URL with basic auth", - "RTSP Snapshot": "RTSP Snapshot", - "Local URL": "Local URL", - "Web URL": "Web URL", - "Camera IP": "Camera IP", - "Port": "Port", - "Path": "Path", - "Cache timeout (ms)": "Cache timeout (ms)", - "If empty, use default settings. If 0, cache disabled": "If empty, use default settings. If 0, cache disabled", - "Camera URL": "Camera URL", - "Add new camera": "Add new camera", - "Add time to screenshot": "Add time to screenshot", - "Time format": "Time format", - "See here:": "See here:", - "Height": "Height", - "Add title": "Add title", - "Width": "Width", - "in pixels": "in pixels", - "Expert settings": "Expert settings", - "Reolink E1 Snapshot": "Reolink E1 Snapshot", - "Eufy Security": "Eufy Security", - "From eusec adapter": "From eusec adapter", - "By IP Address": "By IP Address", - "Camera OID": "Camera object ID", - "Protocol": "Protocol", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Request timeout (ms)", - "If empty or 0, use default settings.": "If empty or 0, use default settings." + "Are you sure?": "Are you sure?", + "Cancel": "Cancel", + "Close": "Close", + "Error": "Error", + "Load configuration from file": "Load configuration from file", + "Ok": "Ok", + "Options": "Options", + "Save": "Save", + "Save and close": "Save and close", + "Save configuration to file": "Save configuration to file", + "Success": "Success", + "Test connection": "Test connection", + "Type": "Type", + "not connected": "not connected", + "Description": "Description", + "Name": "Name", + "Test": "Test", + "Apply": "Apply", + "Username": "User name", + "Password": "Password", + "Edit camera %s [%s]": "Edit camera %s [%s]", + "Cameras": "Cameras", + "Local IP address": "Local IP address", + "Local port": "Local port", + "Default timeout (ms)": "Default timeout (ms)", + "WEB Instance": "WEB Instance", + "All": "All", + "Path to ffpmeg executable": "Path to ffpmeg executable", + "Like /usr/bin/ffmpeg": "Like \"/usr/bin/ffmpeg\"", + "Test path": "Test path", + "Path to store temporary images": "Path to store temporary images", + "If empty then in adapter folder": "If empty then in adapter folder", + "Default cache timeout (ms)": "Default cache timeout (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "How often the cameras will be asked for new snapshot. If 0, then by every request", + "URL": "URL", + "URL with basic auth": "URL with basic auth", + "RTSP Snapshot": "RTSP Snapshot", + "Local URL": "Local URL", + "Web URL": "Web URL", + "Camera IP": "Camera IP", + "Port": "Port", + "Path": "Path", + "Cache timeout (ms)": "Cache timeout (ms)", + "If empty, use default settings. If 0, cache disabled": "If empty, use default settings. If 0, cache disabled", + "Camera URL": "Camera URL", + "Add new camera": "Add new camera", + "Add time to screenshot": "Add time to screenshot", + "Time format": "Time format", + "See here:": "See here:", + "Height": "Height", + "Add title": "Add title", + "Width": "Width", + "in pixels": "in pixels", + "Expert settings": "Expert settings", + "Reolink E1 Snapshot": "Reolink E1 Snapshot", + "Eufy Security": "Eufy Security", + "From eusec adapter": "From eusec adapter", + "By IP Address": "By IP Address", + "Camera OID": "Camera object ID", + "Protocol": "Protocol", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Request timeout (ms)", + "If empty or 0, use default settings.": "If empty or 0, use default settings." } diff --git a/src/src/i18n/es.json b/src/src/i18n/es.json index 537c7e9..c1c520f 100644 --- a/src/src/i18n/es.json +++ b/src/src/i18n/es.json @@ -1,65 +1,65 @@ { - "Are you sure?": "¿Estás seguro?", - "Cancel": "Cancelar", - "Close": "Cerca", - "Error": "Error", - "Load configuration from file": "Cargar configuración desde archivo", - "Ok": "Ok", - "Options": "Opciones", - "Save": "Salvar", - "Save and close": "Guardar y cerrar", - "Save configuration to file": "Guardar configuración en archivo", - "Success": "Éxito", - "Test connection": "Conexión de prueba", - "Type": "Tipo", - "not connected": "no conectado", - "Description": "Descripción", - "Name": "Nombre", - "Test": "Prueba", - "Apply": "Aplicar", - "Username": "Nombre de usuario", - "Password": "Contraseña", - "Edit camera %s [%s]": "Editar cámara %s [%s]", - "Cameras": "Cámaras", - "Local IP address": "Dirección IP local", - "Local port": "puerto local", - "Default timeout (ms)": "Tiempo de espera predeterminado (ms)", - "WEB Instance": "Instancia WEB", - "All": "Todos", - "Path to ffpmeg executable": "Ruta al ejecutable ffpmeg", - "Like /usr/bin/ffmpeg": "Como \"/usr/bin/ffmpeg\"", - "Test path": "ruta de prueba", - "Path to store temporary images": "Ruta para almacenar imágenes temporales", - "If empty then in adapter folder": "Si está vacío, entonces en la carpeta del adaptador", - "Default cache timeout (ms)": "Tiempo de espera de caché predeterminado (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Con qué frecuencia se pedirá a las cámaras una nueva instantánea. Si es 0, entonces por cada solicitud", - "URL": "URL", - "URL with basic auth": "URL con autenticación básica", - "RTSP Snapshot": "Instantánea de RTSP", - "Local URL": "URL local", - "Web URL": "URL web", - "Camera IP": "IP de la cámara", - "Port": "Puerto", - "Path": "Camino", - "Cache timeout (ms)": "Tiempo de espera de caché (ms)", - "If empty, use default settings. If 0, cache disabled": "Si está vacío, utilice la configuración predeterminada. Si es 0, cache deshabilitado", - "Camera URL": "URL de la cámara", - "Add new camera": "Añadir nueva cámara", - "Add time to screenshot": "Añadir tiempo a la captura de pantalla", - "Time format": "Formato de tiempo", - "See here:": "Mira aquí:", - "Add title": "Añadir título", - "Height": "Altura", - "Width": "Ancho", - "in pixels": "en píxeles", - "Expert settings": "Configuración experta", - "Reolink E1 Snapshot": "Instantánea de Reolink E1", - "Eufy Security": "Eufy Seguridad", - "From eusec adapter": "Del adaptador eusec", - "By IP Address": "Por dirección IP", - "Camera OID": "ID de objeto de la cámara", - "Protocol": "Protocolo", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Solicitar tiempo de espera (ms)", - "If empty or 0, use default settings.": "Si está vacío o es 0, utilice la configuración predeterminada." + "Are you sure?": "¿Estás seguro?", + "Cancel": "Cancelar", + "Close": "Cerca", + "Error": "Error", + "Load configuration from file": "Cargar configuración desde archivo", + "Ok": "Ok", + "Options": "Opciones", + "Save": "Salvar", + "Save and close": "Guardar y cerrar", + "Save configuration to file": "Guardar configuración en archivo", + "Success": "Éxito", + "Test connection": "Conexión de prueba", + "Type": "Tipo", + "not connected": "no conectado", + "Description": "Descripción", + "Name": "Nombre", + "Test": "Prueba", + "Apply": "Aplicar", + "Username": "Nombre de usuario", + "Password": "Contraseña", + "Edit camera %s [%s]": "Editar cámara %s [%s]", + "Cameras": "Cámaras", + "Local IP address": "Dirección IP local", + "Local port": "puerto local", + "Default timeout (ms)": "Tiempo de espera predeterminado (ms)", + "WEB Instance": "Instancia WEB", + "All": "Todos", + "Path to ffpmeg executable": "Ruta al ejecutable ffpmeg", + "Like /usr/bin/ffmpeg": "Como \"/usr/bin/ffmpeg\"", + "Test path": "ruta de prueba", + "Path to store temporary images": "Ruta para almacenar imágenes temporales", + "If empty then in adapter folder": "Si está vacío, entonces en la carpeta del adaptador", + "Default cache timeout (ms)": "Tiempo de espera de caché predeterminado (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Con qué frecuencia se pedirá a las cámaras una nueva instantánea. Si es 0, entonces por cada solicitud", + "URL": "URL", + "URL with basic auth": "URL con autenticación básica", + "RTSP Snapshot": "Instantánea de RTSP", + "Local URL": "URL local", + "Web URL": "URL web", + "Camera IP": "IP de la cámara", + "Port": "Puerto", + "Path": "Camino", + "Cache timeout (ms)": "Tiempo de espera de caché (ms)", + "If empty, use default settings. If 0, cache disabled": "Si está vacío, utilice la configuración predeterminada. Si es 0, cache deshabilitado", + "Camera URL": "URL de la cámara", + "Add new camera": "Añadir nueva cámara", + "Add time to screenshot": "Añadir tiempo a la captura de pantalla", + "Time format": "Formato de tiempo", + "See here:": "Mira aquí:", + "Add title": "Añadir título", + "Height": "Altura", + "Width": "Ancho", + "in pixels": "en píxeles", + "Expert settings": "Configuración experta", + "Reolink E1 Snapshot": "Instantánea de Reolink E1", + "Eufy Security": "Eufy Seguridad", + "From eusec adapter": "Del adaptador eusec", + "By IP Address": "Por dirección IP", + "Camera OID": "ID de objeto de la cámara", + "Protocol": "Protocolo", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Solicitar tiempo de espera (ms)", + "If empty or 0, use default settings.": "Si está vacío o es 0, utilice la configuración predeterminada." } diff --git a/src/src/i18n/fr.json b/src/src/i18n/fr.json index 22eb2c9..a87ee9e 100644 --- a/src/src/i18n/fr.json +++ b/src/src/i18n/fr.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Êtes-vous sûr?", - "Cancel": "Annuler", - "Close": "Fermer", - "Error": "Erreur", - "Load configuration from file": "Charger la configuration à partir du fichier", - "Ok": "D'accord", - "Options": "Les options", - "Save": "sauvegarder", - "Save and close": "Sauver et fermer", - "Save configuration to file": "Enregistrer la configuration dans un fichier", - "Success": "Succès", - "Test connection": "Tester la connexion", - "Type": "Type", - "not connected": "pas connecté", - "Description": "La description", - "Name": "Nom", - "Test": "Tester", - "Apply": "Appliquer", - "Username": "Nom d'utilisateur", - "Password": "Mot de passe", - "Edit camera %s [%s]": "Modifier la caméra %s [%s]", - "Cameras": "Appareils photo", - "Local IP address": "Adresse IP locale", - "Local port": "Port local", - "Default timeout (ms)": "Délai d'attente par défaut (ms)", - "WEB Instance": "Instance WEB", - "All": "Tout", - "Path to ffpmeg executable": "Chemin vers l'exécutable ffpmeg", - "Like /usr/bin/ffmpeg": "Comme \"/usr/bin/ffmpeg\"", - "Test path": "Chemin d'essai", - "Path to store temporary images": "Chemin de stockage des images temporaires", - "If empty then in adapter folder": "Si vide, alors dans le dossier de l'adaptateur", - "Default cache timeout (ms)": "Délai d'expiration du cache par défaut (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "La fréquence à laquelle les caméras seront demandées pour un nouvel instantané. Si 0, alors à chaque requête", - "URL": "URL", - "URL with basic auth": "URL avec authentification de base", - "RTSP Snapshot": "Instantané RTSP", - "Local URL": "URL locale", - "Web URL": "URL Web", - "Camera IP": "Caméra IP", - "Port": "Port", - "Path": "Chemin", - "Cache timeout (ms)": "Délai d'expiration du cache (ms)", - "If empty, use default settings. If 0, cache disabled": "Si vide, utilisez les paramètres par défaut. Si 0, cache désactivé", - "Camera URL": "URL de la caméra", - "Add new camera": "Ajouter une nouvelle caméra", - "Add time to screenshot": "Ajouter du temps à la capture d'écran", - "Time format": "Format de l'heure", - "See here:": "Vois ici:", - "Add title": "Ajouter un titre", - "Height": "Hauteur", - "Width": "Largeur", - "in pixels": "en pixels", - "Expert settings": "Paramètres experts", - "Reolink E1 Snapshot": "Instantané Reolink E1", - "Eufy Security": "Sécurité Eufy", - "From eusec adapter": "De l'adaptateur eusec", - "By IP Address": "Par adresse IP", - "Camera OID": "ID d'objet caméra", - "Protocol": "Protocole", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Délai d'expiration de la demande (ms)", - "If empty or 0, use default settings.": "S'il est vide ou 0, utilisez les paramètres par défaut." + "Are you sure?": "Êtes-vous sûr?", + "Cancel": "Annuler", + "Close": "Fermer", + "Error": "Erreur", + "Load configuration from file": "Charger la configuration à partir du fichier", + "Ok": "D'accord", + "Options": "Les options", + "Save": "sauvegarder", + "Save and close": "Sauver et fermer", + "Save configuration to file": "Enregistrer la configuration dans un fichier", + "Success": "Succès", + "Test connection": "Tester la connexion", + "Type": "Type", + "not connected": "pas connecté", + "Description": "La description", + "Name": "Nom", + "Test": "Tester", + "Apply": "Appliquer", + "Username": "Nom d'utilisateur", + "Password": "Mot de passe", + "Edit camera %s [%s]": "Modifier la caméra %s [%s]", + "Cameras": "Appareils photo", + "Local IP address": "Adresse IP locale", + "Local port": "Port local", + "Default timeout (ms)": "Délai d'attente par défaut (ms)", + "WEB Instance": "Instance WEB", + "All": "Tout", + "Path to ffpmeg executable": "Chemin vers l'exécutable ffpmeg", + "Like /usr/bin/ffmpeg": "Comme \"/usr/bin/ffmpeg\"", + "Test path": "Chemin d'essai", + "Path to store temporary images": "Chemin de stockage des images temporaires", + "If empty then in adapter folder": "Si vide, alors dans le dossier de l'adaptateur", + "Default cache timeout (ms)": "Délai d'expiration du cache par défaut (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "La fréquence à laquelle les caméras seront demandées pour un nouvel instantané. Si 0, alors à chaque requête", + "URL": "URL", + "URL with basic auth": "URL avec authentification de base", + "RTSP Snapshot": "Instantané RTSP", + "Local URL": "URL locale", + "Web URL": "URL Web", + "Camera IP": "Caméra IP", + "Port": "Port", + "Path": "Chemin", + "Cache timeout (ms)": "Délai d'expiration du cache (ms)", + "If empty, use default settings. If 0, cache disabled": "Si vide, utilisez les paramètres par défaut. Si 0, cache désactivé", + "Camera URL": "URL de la caméra", + "Add new camera": "Ajouter une nouvelle caméra", + "Add time to screenshot": "Ajouter du temps à la capture d'écran", + "Time format": "Format de l'heure", + "See here:": "Vois ici:", + "Add title": "Ajouter un titre", + "Height": "Hauteur", + "Width": "Largeur", + "in pixels": "en pixels", + "Expert settings": "Paramètres experts", + "Reolink E1 Snapshot": "Instantané Reolink E1", + "Eufy Security": "Sécurité Eufy", + "From eusec adapter": "De l'adaptateur eusec", + "By IP Address": "Par adresse IP", + "Camera OID": "ID d'objet caméra", + "Protocol": "Protocole", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Délai d'expiration de la demande (ms)", + "If empty or 0, use default settings.": "S'il est vide ou 0, utilisez les paramètres par défaut." } diff --git a/src/src/i18n/it.json b/src/src/i18n/it.json index 4733309..d22387a 100644 --- a/src/src/i18n/it.json +++ b/src/src/i18n/it.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Sei sicuro?", - "Cancel": "Annulla", - "Close": "Vicino", - "Error": "Errore", - "Load configuration from file": "Carica la configurazione dal file", - "Ok": "Ok", - "Options": "Opzioni", - "Save": "Salva", - "Save and close": "Salva e chiudi", - "Save configuration to file": "Salva la configurazione in un file", - "Success": "Successo", - "Test connection": "Test di connessione", - "Type": "Genere", - "not connected": "non connesso", - "Description": "Descrizione", - "Name": "Nome", - "Test": "Test", - "Apply": "Applicare", - "Username": "Nome utente", - "Password": "Parola d'ordine", - "Edit camera %s [%s]": "Modifica fotocamera %s [%s]", - "Cameras": "Macchine fotografiche", - "Local IP address": "Indirizzo IP locale", - "Local port": "Porto locale", - "Default timeout (ms)": "Timeout predefinito (ms)", - "WEB Instance": "Istanza WEB", - "All": "Tutti", - "Path to ffpmeg executable": "Percorso dell'eseguibile ffpmeg", - "Like /usr/bin/ffmpeg": "Come \"/usr/bin/ffmpeg\"", - "Test path": "Percorso di prova", - "Path to store temporary images": "Percorso per memorizzare le immagini temporanee", - "If empty then in adapter folder": "Se vuoto, nella cartella dell'adattatore", - "Default cache timeout (ms)": "Timeout cache predefinito (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "La frequenza con cui le telecamere riceveranno una nuova istantanea. Se 0, allora da ogni richiesta", - "URL": "URL", - "URL with basic auth": "URL con autenticazione di base", - "RTSP Snapshot": "Istantanea RTSP", - "Local URL": "URL locale", - "Web URL": "URL web", - "Camera IP": "IP della telecamera", - "Port": "Porta", - "Path": "Il percorso", - "Cache timeout (ms)": "Timeout della cache (ms)", - "If empty, use default settings. If 0, cache disabled": "Se vuoto, utilizza le impostazioni predefinite. Se 0, cache disabilitata", - "Camera URL": "URL della fotocamera", - "Add new camera": "Aggiungi nuova fotocamera", - "Add time to screenshot": "Aggiungi tempo allo screenshot", - "Time format": "Formato orario", - "See here:": "Vedere qui:", - "Add title": "Aggiungi titolo", - "Height": "Altezza", - "Width": "Larghezza", - "in pixels": "in pixel", - "Expert settings": "Impostazioni avanzate", - "Reolink E1 Snapshot": "Istantanea Reolink E1", - "Eufy Security": "Eufy Security", - "From eusec adapter": "Dall'adattatore eusec", - "By IP Address": "Per indirizzo IP", - "Camera OID": "ID oggetto telecamera", - "Protocol": "Protocollo", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Richiedi timeout (ms)", - "If empty or 0, use default settings.": "Se vuoto o 0, utilizza le impostazioni predefinite." + "Are you sure?": "Sei sicuro?", + "Cancel": "Annulla", + "Close": "Vicino", + "Error": "Errore", + "Load configuration from file": "Carica la configurazione dal file", + "Ok": "Ok", + "Options": "Opzioni", + "Save": "Salva", + "Save and close": "Salva e chiudi", + "Save configuration to file": "Salva la configurazione in un file", + "Success": "Successo", + "Test connection": "Test di connessione", + "Type": "Genere", + "not connected": "non connesso", + "Description": "Descrizione", + "Name": "Nome", + "Test": "Test", + "Apply": "Applicare", + "Username": "Nome utente", + "Password": "Parola d'ordine", + "Edit camera %s [%s]": "Modifica fotocamera %s [%s]", + "Cameras": "Macchine fotografiche", + "Local IP address": "Indirizzo IP locale", + "Local port": "Porto locale", + "Default timeout (ms)": "Timeout predefinito (ms)", + "WEB Instance": "Istanza WEB", + "All": "Tutti", + "Path to ffpmeg executable": "Percorso dell'eseguibile ffpmeg", + "Like /usr/bin/ffmpeg": "Come \"/usr/bin/ffmpeg\"", + "Test path": "Percorso di prova", + "Path to store temporary images": "Percorso per memorizzare le immagini temporanee", + "If empty then in adapter folder": "Se vuoto, nella cartella dell'adattatore", + "Default cache timeout (ms)": "Timeout cache predefinito (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "La frequenza con cui le telecamere riceveranno una nuova istantanea. Se 0, allora da ogni richiesta", + "URL": "URL", + "URL with basic auth": "URL con autenticazione di base", + "RTSP Snapshot": "Istantanea RTSP", + "Local URL": "URL locale", + "Web URL": "URL web", + "Camera IP": "IP della telecamera", + "Port": "Porta", + "Path": "Il percorso", + "Cache timeout (ms)": "Timeout della cache (ms)", + "If empty, use default settings. If 0, cache disabled": "Se vuoto, utilizza le impostazioni predefinite. Se 0, cache disabilitata", + "Camera URL": "URL della fotocamera", + "Add new camera": "Aggiungi nuova fotocamera", + "Add time to screenshot": "Aggiungi tempo allo screenshot", + "Time format": "Formato orario", + "See here:": "Vedere qui:", + "Add title": "Aggiungi titolo", + "Height": "Altezza", + "Width": "Larghezza", + "in pixels": "in pixel", + "Expert settings": "Impostazioni avanzate", + "Reolink E1 Snapshot": "Istantanea Reolink E1", + "Eufy Security": "Eufy Security", + "From eusec adapter": "Dall'adattatore eusec", + "By IP Address": "Per indirizzo IP", + "Camera OID": "ID oggetto telecamera", + "Protocol": "Protocollo", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Richiedi timeout (ms)", + "If empty or 0, use default settings.": "Se vuoto o 0, utilizza le impostazioni predefinite." } diff --git a/src/src/i18n/nl.json b/src/src/i18n/nl.json index 3cfc0a1..d940135 100644 --- a/src/src/i18n/nl.json +++ b/src/src/i18n/nl.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Weet je het zeker?", - "Cancel": "annuleren", - "Close": "Dichtbij", - "Error": "Fout", - "Load configuration from file": "Laad de configuratie uit het bestand", - "Ok": "OK", - "Options": "opties", - "Save": "Opslaan", - "Save and close": "Opslaan en afsluiten", - "Save configuration to file": "Configuratie opslaan in bestand", - "Success": "Succes", - "Test connection": "Test verbinding", - "Type": "Type", - "not connected": "niet verbonden", - "Description": "Omschrijving", - "Name": "Naam", - "Test": "Test", - "Apply": "Van toepassing zijn", - "Username": "Gebruikersnaam", - "Password": "Wachtwoord", - "Edit camera %s [%s]": "Camera %s [%s] bewerken", - "Cameras": "Camera's", - "Local IP address": "Lokaal IP-adres", - "Local port": "Lokale haven", - "Default timeout (ms)": "Standaard time-out (ms)", - "WEB Instance": "WEB-instantie", - "All": "Alle", - "Path to ffpmeg executable": "Pad naar uitvoerbaar ffpmeg", - "Like /usr/bin/ffmpeg": "Zoals \"/usr/bin/ffmpeg\"", - "Test path": "Test pad", - "Path to store temporary images": "Pad om tijdelijke afbeeldingen op te slaan", - "If empty then in adapter folder": "Indien leeg dan in adaptermap", - "Default cache timeout (ms)": "Standaard cachetime-out (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Hoe vaak de camera's worden gevraagd om een nieuwe momentopname. Indien 0, dan bij elk verzoek", - "URL": "URL", - "URL with basic auth": "URL met basisauthenticatie", - "RTSP Snapshot": "RTSP-momentopname", - "Local URL": "Lokale URL", - "Web URL": "Web-URL", - "Camera IP": "Camera-IP", - "Port": "Haven", - "Path": "Pad", - "Cache timeout (ms)": "Cachetime-out (ms)", - "If empty, use default settings. If 0, cache disabled": "Gebruik de standaardinstellingen als deze leeg zijn. Indien 0, cache uitgeschakeld", - "Camera URL": "Camera-URL", - "Add new camera": "Nieuwe camera toevoegen", - "Add time to screenshot": "Voeg tijd toe aan screenshot", - "Time format": "Tijd formaat", - "See here:": "Kijk hier:", - "Add title": "Titel toevoegen", - "Height": "Hoogte", - "Width": "Breedte", - "in pixels": "in pixels", - "Expert settings": "Expert instellingen", - "Reolink E1 Snapshot": "Reolink E1 momentopname", - "Eufy Security": "Eufy-beveiliging", - "From eusec adapter": "Van eusec-adapter", - "By IP Address": "Op IP-adres", - "Camera OID": "Camera-object-ID", - "Protocol": "Protocol", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Time-out aanvragen (ms)", - "If empty or 0, use default settings.": "Indien leeg of 0, gebruik dan de standaardinstellingen." + "Are you sure?": "Weet je het zeker?", + "Cancel": "annuleren", + "Close": "Dichtbij", + "Error": "Fout", + "Load configuration from file": "Laad de configuratie uit het bestand", + "Ok": "OK", + "Options": "opties", + "Save": "Opslaan", + "Save and close": "Opslaan en afsluiten", + "Save configuration to file": "Configuratie opslaan in bestand", + "Success": "Succes", + "Test connection": "Test verbinding", + "Type": "Type", + "not connected": "niet verbonden", + "Description": "Omschrijving", + "Name": "Naam", + "Test": "Test", + "Apply": "Van toepassing zijn", + "Username": "Gebruikersnaam", + "Password": "Wachtwoord", + "Edit camera %s [%s]": "Camera %s [%s] bewerken", + "Cameras": "Camera's", + "Local IP address": "Lokaal IP-adres", + "Local port": "Lokale haven", + "Default timeout (ms)": "Standaard time-out (ms)", + "WEB Instance": "WEB-instantie", + "All": "Alle", + "Path to ffpmeg executable": "Pad naar uitvoerbaar ffpmeg", + "Like /usr/bin/ffmpeg": "Zoals \"/usr/bin/ffmpeg\"", + "Test path": "Test pad", + "Path to store temporary images": "Pad om tijdelijke afbeeldingen op te slaan", + "If empty then in adapter folder": "Indien leeg dan in adaptermap", + "Default cache timeout (ms)": "Standaard cachetime-out (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Hoe vaak de camera's worden gevraagd om een nieuwe momentopname. Indien 0, dan bij elk verzoek", + "URL": "URL", + "URL with basic auth": "URL met basisauthenticatie", + "RTSP Snapshot": "RTSP-momentopname", + "Local URL": "Lokale URL", + "Web URL": "Web-URL", + "Camera IP": "Camera-IP", + "Port": "Haven", + "Path": "Pad", + "Cache timeout (ms)": "Cachetime-out (ms)", + "If empty, use default settings. If 0, cache disabled": "Gebruik de standaardinstellingen als deze leeg zijn. Indien 0, cache uitgeschakeld", + "Camera URL": "Camera-URL", + "Add new camera": "Nieuwe camera toevoegen", + "Add time to screenshot": "Voeg tijd toe aan screenshot", + "Time format": "Tijd formaat", + "See here:": "Kijk hier:", + "Add title": "Titel toevoegen", + "Height": "Hoogte", + "Width": "Breedte", + "in pixels": "in pixels", + "Expert settings": "Expert instellingen", + "Reolink E1 Snapshot": "Reolink E1 momentopname", + "Eufy Security": "Eufy-beveiliging", + "From eusec adapter": "Van eusec-adapter", + "By IP Address": "Op IP-adres", + "Camera OID": "Camera-object-ID", + "Protocol": "Protocol", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Time-out aanvragen (ms)", + "If empty or 0, use default settings.": "Indien leeg of 0, gebruik dan de standaardinstellingen." } diff --git a/src/src/i18n/pl.json b/src/src/i18n/pl.json index 78048f1..e81a712 100644 --- a/src/src/i18n/pl.json +++ b/src/src/i18n/pl.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Jesteś pewny?", - "Cancel": "Anuluj", - "Close": "Blisko", - "Error": "Błąd", - "Load configuration from file": "Załaduj konfigurację z pliku", - "Ok": "Dobrze", - "Options": "Opcje", - "Save": "Zapisać", - "Save and close": "Zapisz i zamknij", - "Save configuration to file": "Zapisz konfigurację do pliku", - "Success": "Sukces", - "Test connection": "Testuj połączenie", - "Type": "Rodzaj", - "not connected": "nie połączony", - "Description": "Opis", - "Name": "Imię", - "Test": "Test", - "Apply": "Zastosować", - "Username": "Nazwa Użytkownika", - "Password": "Hasło", - "Edit camera %s [%s]": "Edytuj kamerę %s [%s]", - "Cameras": "kamery", - "Local IP address": "Lokalny adres IP", - "Local port": "Port lokalny", - "Default timeout (ms)": "Domyślny limit czasu (ms)", - "WEB Instance": "Instancja WWW", - "All": "Wszystko", - "Path to ffpmeg executable": "Ścieżka do pliku wykonywalnego ffpmeg", - "Like /usr/bin/ffmpeg": "Jak „/usr/bin/ffmpeg”", - "Test path": "Ścieżka testowa", - "Path to store temporary images": "Ścieżka do przechowywania obrazów tymczasowych", - "If empty then in adapter folder": "Jeśli jest pusty, to w folderze adaptera", - "Default cache timeout (ms)": "Domyślny limit czasu pamięci podręcznej (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Jak często kamery będą pytane o nowe zdjęcie. Jeśli 0, to przy każdym żądaniu", - "URL": "Adres URL", - "URL with basic auth": "Adres URL z podstawowym uwierzytelnieniem", - "RTSP Snapshot": "Migawka RTSP", - "Local URL": "Lokalny adres URL", - "Web URL": "Adres internetowy", - "Camera IP": "IP kamery", - "Port": "Port", - "Path": "Ścieżka", - "Cache timeout (ms)": "Limit czasu pamięci podręcznej (ms)", - "If empty, use default settings. If 0, cache disabled": "Jeśli puste, użyj ustawień domyślnych. Jeśli 0, cache wyłączone", - "Camera URL": "URL aparatu", - "Add new camera": "Dodaj nową kamerę", - "Add time to screenshot": "Dodaj czas do zrzutu ekranu", - "Time format": "Format czasu", - "See here:": "Spójrz tutaj:", - "Add title": "Dodaj tytuł", - "Height": "Wysokość", - "Width": "Szerokość", - "in pixels": "w pikselach", - "Expert settings": "Ustawienia eksperckie", - "Reolink E1 Snapshot": "Połącz ponownie migawkę E1", - "Eufy Security": "Bezpieczeństwo Eufy", - "From eusec adapter": "Z adaptera eusec", - "By IP Address": "Według adresu IP", - "Camera OID": "Identyfikator obiektu kamery", - "Protocol": "Protokół", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Limit czasu żądania (ms)", - "If empty or 0, use default settings.": "Jeśli jest pusty lub 0, użyj ustawień domyślnych." + "Are you sure?": "Jesteś pewny?", + "Cancel": "Anuluj", + "Close": "Blisko", + "Error": "Błąd", + "Load configuration from file": "Załaduj konfigurację z pliku", + "Ok": "Dobrze", + "Options": "Opcje", + "Save": "Zapisać", + "Save and close": "Zapisz i zamknij", + "Save configuration to file": "Zapisz konfigurację do pliku", + "Success": "Sukces", + "Test connection": "Testuj połączenie", + "Type": "Rodzaj", + "not connected": "nie połączony", + "Description": "Opis", + "Name": "Imię", + "Test": "Test", + "Apply": "Zastosować", + "Username": "Nazwa Użytkownika", + "Password": "Hasło", + "Edit camera %s [%s]": "Edytuj kamerę %s [%s]", + "Cameras": "kamery", + "Local IP address": "Lokalny adres IP", + "Local port": "Port lokalny", + "Default timeout (ms)": "Domyślny limit czasu (ms)", + "WEB Instance": "Instancja WWW", + "All": "Wszystko", + "Path to ffpmeg executable": "Ścieżka do pliku wykonywalnego ffpmeg", + "Like /usr/bin/ffmpeg": "Jak „/usr/bin/ffmpeg”", + "Test path": "Ścieżka testowa", + "Path to store temporary images": "Ścieżka do przechowywania obrazów tymczasowych", + "If empty then in adapter folder": "Jeśli jest pusty, to w folderze adaptera", + "Default cache timeout (ms)": "Domyślny limit czasu pamięci podręcznej (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Jak często kamery będą pytane o nowe zdjęcie. Jeśli 0, to przy każdym żądaniu", + "URL": "Adres URL", + "URL with basic auth": "Adres URL z podstawowym uwierzytelnieniem", + "RTSP Snapshot": "Migawka RTSP", + "Local URL": "Lokalny adres URL", + "Web URL": "Adres internetowy", + "Camera IP": "IP kamery", + "Port": "Port", + "Path": "Ścieżka", + "Cache timeout (ms)": "Limit czasu pamięci podręcznej (ms)", + "If empty, use default settings. If 0, cache disabled": "Jeśli puste, użyj ustawień domyślnych. Jeśli 0, cache wyłączone", + "Camera URL": "URL aparatu", + "Add new camera": "Dodaj nową kamerę", + "Add time to screenshot": "Dodaj czas do zrzutu ekranu", + "Time format": "Format czasu", + "See here:": "Spójrz tutaj:", + "Add title": "Dodaj tytuł", + "Height": "Wysokość", + "Width": "Szerokość", + "in pixels": "w pikselach", + "Expert settings": "Ustawienia eksperckie", + "Reolink E1 Snapshot": "Połącz ponownie migawkę E1", + "Eufy Security": "Bezpieczeństwo Eufy", + "From eusec adapter": "Z adaptera eusec", + "By IP Address": "Według adresu IP", + "Camera OID": "Identyfikator obiektu kamery", + "Protocol": "Protokół", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Limit czasu żądania (ms)", + "If empty or 0, use default settings.": "Jeśli jest pusty lub 0, użyj ustawień domyślnych." } diff --git a/src/src/i18n/pt.json b/src/src/i18n/pt.json index 73b00f2..cb98ad5 100644 --- a/src/src/i18n/pt.json +++ b/src/src/i18n/pt.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Você tem certeza?", - "Cancel": "Cancelar", - "Close": "Fechar", - "Error": "Erro", - "Load configuration from file": "Carregar configuração do arquivo", - "Ok": "Está bem", - "Options": "Opções", - "Save": "Salve ", - "Save and close": "Salvar e fechar", - "Save configuration to file": "Salvar configuração no arquivo", - "Success": "Sucesso", - "Test connection": "Conexão de teste", - "Type": "Tipo", - "not connected": "não conectado", - "Description": "Descrição", - "Name": "Nome", - "Test": "Teste", - "Apply": "Aplique", - "Username": "Nome do usuário", - "Password": "Senha", - "Edit camera %s [%s]": "Editar câmera %s [%s]", - "Cameras": "Câmeras", - "Local IP address": "Endereço IP local", - "Local port": "porta local", - "Default timeout (ms)": "Tempo limite padrão (ms)", - "WEB Instance": "Instância WEB", - "All": "Todo", - "Path to ffpmeg executable": "Caminho para o executável do ffpmeg", - "Like /usr/bin/ffmpeg": "Como \"/usr/bin/ffmpeg\"", - "Test path": "caminho de teste", - "Path to store temporary images": "Caminho para armazenar imagens temporárias", - "If empty then in adapter folder": "Se estiver vazio, então na pasta do adaptador", - "Default cache timeout (ms)": "Tempo limite de cache padrão (ms)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Com que frequência as câmeras serão solicitadas para um novo instantâneo. Se 0, então a cada solicitação", - "URL": "URL", - "URL with basic auth": "URL com autenticação básica", - "RTSP Snapshot": "Instantâneo RTSP", - "Local URL": "URL local", - "Web URL": "URL da Web", - "Camera IP": "IP da câmera", - "Port": "Porta", - "Path": "Caminho", - "Cache timeout (ms)": "Tempo limite de cache (ms)", - "If empty, use default settings. If 0, cache disabled": "Se estiver vazio, use as configurações padrão. Se 0, cache desabilitado", - "Camera URL": "URL da câmera", - "Add new camera": "Adicionar nova câmera", - "Add time to screenshot": "Adicionar tempo à captura de tela", - "Time format": "Formato de hora", - "See here:": "Veja aqui:", - "Add title": "Adicionar título", - "Height": "Altura", - "Width": "Largura", - "in pixels": "em pixels", - "Expert settings": "Configurações avançadas", - "Reolink E1 Snapshot": "Instantâneo Reolink E1", - "Eufy Security": "Eufy Segurança", - "From eusec adapter": "Do adaptador eusec", - "By IP Address": "Por endereço IP", - "Camera OID": "ID do objeto da câmera", - "Protocol": "Protocolo", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Tempo limite da solicitação (ms)", - "If empty or 0, use default settings.": "Se estiver vazio ou 0, use as configurações padrão." + "Are you sure?": "Você tem certeza?", + "Cancel": "Cancelar", + "Close": "Fechar", + "Error": "Erro", + "Load configuration from file": "Carregar configuração do arquivo", + "Ok": "Está bem", + "Options": "Opções", + "Save": "Salve ", + "Save and close": "Salvar e fechar", + "Save configuration to file": "Salvar configuração no arquivo", + "Success": "Sucesso", + "Test connection": "Conexão de teste", + "Type": "Tipo", + "not connected": "não conectado", + "Description": "Descrição", + "Name": "Nome", + "Test": "Teste", + "Apply": "Aplique", + "Username": "Nome do usuário", + "Password": "Senha", + "Edit camera %s [%s]": "Editar câmera %s [%s]", + "Cameras": "Câmeras", + "Local IP address": "Endereço IP local", + "Local port": "porta local", + "Default timeout (ms)": "Tempo limite padrão (ms)", + "WEB Instance": "Instância WEB", + "All": "Todo", + "Path to ffpmeg executable": "Caminho para o executável do ffpmeg", + "Like /usr/bin/ffmpeg": "Como \"/usr/bin/ffmpeg\"", + "Test path": "caminho de teste", + "Path to store temporary images": "Caminho para armazenar imagens temporárias", + "If empty then in adapter folder": "Se estiver vazio, então na pasta do adaptador", + "Default cache timeout (ms)": "Tempo limite de cache padrão (ms)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Com que frequência as câmeras serão solicitadas para um novo instantâneo. Se 0, então a cada solicitação", + "URL": "URL", + "URL with basic auth": "URL com autenticação básica", + "RTSP Snapshot": "Instantâneo RTSP", + "Local URL": "URL local", + "Web URL": "URL da Web", + "Camera IP": "IP da câmera", + "Port": "Porta", + "Path": "Caminho", + "Cache timeout (ms)": "Tempo limite de cache (ms)", + "If empty, use default settings. If 0, cache disabled": "Se estiver vazio, use as configurações padrão. Se 0, cache desabilitado", + "Camera URL": "URL da câmera", + "Add new camera": "Adicionar nova câmera", + "Add time to screenshot": "Adicionar tempo à captura de tela", + "Time format": "Formato de hora", + "See here:": "Veja aqui:", + "Add title": "Adicionar título", + "Height": "Altura", + "Width": "Largura", + "in pixels": "em pixels", + "Expert settings": "Configurações avançadas", + "Reolink E1 Snapshot": "Instantâneo Reolink E1", + "Eufy Security": "Eufy Segurança", + "From eusec adapter": "Do adaptador eusec", + "By IP Address": "Por endereço IP", + "Camera OID": "ID do objeto da câmera", + "Protocol": "Protocolo", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Tempo limite da solicitação (ms)", + "If empty or 0, use default settings.": "Se estiver vazio ou 0, use as configurações padrão." } diff --git a/src/src/i18n/ru.json b/src/src/i18n/ru.json index 483f293..d15636e 100644 --- a/src/src/i18n/ru.json +++ b/src/src/i18n/ru.json @@ -1,65 +1,65 @@ { - "Are you sure?": "Вы уверены?", - "Cancel": "Отмена", - "Close": "Закрыть", - "Error": "Ошибка", - "Load configuration from file": "Загрузить конфигурацию из файла", - "Ok": "Ok", - "Options": "Параметры", - "Save": "Сохранить", - "Save and close": "Сохранить и закрыть", - "Save configuration to file": "Сохранить конфигурацию в файл", - "Success": "Успешно", - "Test connection": "Тестовое соединение", - "Type": "Тип", - "not connected": "Не подключен", - "Description": "Описание", - "Name": "имя", - "Test": "Тестовое задание", - "Apply": "Сохранить", - "Username": "Имя пользователя", - "Password": "пароль", - "Edit camera %s [%s]": "Редактировать камеру %s [%s]", - "Cameras": "Камеры", - "Local IP address": "Локальный IP-адрес", - "Local port": "Местный порт", - "Default timeout (ms)": "Время ожидания по умолчанию (мс)", - "WEB Instance": "ВЕБ-экземпляр", - "All": "Все", - "Path to ffpmeg executable": "Путь к исполняемому файлу ffpmeg", - "Like /usr/bin/ffmpeg": "Например, \"/usr/bin/ffmpeg\"", - "Test path": "Тестовый путь", - "Path to store temporary images": "Путь для хранения временных изображений", - "If empty then in adapter folder": "Если пусто, то в папке адаптера", - "Default cache timeout (ms)": "Время ожидания кэша по умолчанию (мс)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Как часто камеры будут запрашивать новый снимок. Если 0, то при каждом запросе", - "URL": "URL-адрес", - "URL with basic auth": "URL с базовой авторизацией", - "RTSP Snapshot": "Снимок RTSP", - "Local URL": "Локальный URL-адрес", - "Web URL": "Веб-URL", - "Camera IP": "IP камеры", - "Port": "Порт", - "Path": "Дорожка", - "Cache timeout (ms)": "Время ожидания кэша (мс)", - "If empty, use default settings. If 0, cache disabled": "Если пусто, используйте настройки по умолчанию. Если 0, кеширование отключено", - "Camera URL": "URL-адрес камеры", - "Add new camera": "Добавить новую камеру", - "Add time to screenshot": "Добавить время на скриншот", - "Time format": "Формат времени", - "See here:": "Глянь сюда:", - "Add title": "Добавить заголовок", - "Height": "Высота", - "Width": "Ширина", - "in pixels": "в пикселях", - "Expert settings": "Экспертные настройки", - "Reolink E1 Snapshot": "Снимок Reolink E1", - "Eufy Security": "Юфи Безопасность", - "From eusec adapter": "От адаптера eusec", - "By IP Address": "По IP-адресу", - "Camera OID": "Идентификатор объекта камеры", - "Protocol": "Протокол", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Таймаут запроса (мс)", - "If empty or 0, use default settings.": "Если пусто или равно 0, используйте настройки по умолчанию." + "Are you sure?": "Вы уверены?", + "Cancel": "Отмена", + "Close": "Закрыть", + "Error": "Ошибка", + "Load configuration from file": "Загрузить конфигурацию из файла", + "Ok": "Ok", + "Options": "Параметры", + "Save": "Сохранить", + "Save and close": "Сохранить и закрыть", + "Save configuration to file": "Сохранить конфигурацию в файл", + "Success": "Успешно", + "Test connection": "Тестовое соединение", + "Type": "Тип", + "not connected": "Не подключен", + "Description": "Описание", + "Name": "имя", + "Test": "Тестовое задание", + "Apply": "Сохранить", + "Username": "Имя пользователя", + "Password": "пароль", + "Edit camera %s [%s]": "Редактировать камеру %s [%s]", + "Cameras": "Камеры", + "Local IP address": "Локальный IP-адрес", + "Local port": "Местный порт", + "Default timeout (ms)": "Время ожидания по умолчанию (мс)", + "WEB Instance": "ВЕБ-экземпляр", + "All": "Все", + "Path to ffpmeg executable": "Путь к исполняемому файлу ffpmeg", + "Like /usr/bin/ffmpeg": "Например, \"/usr/bin/ffmpeg\"", + "Test path": "Тестовый путь", + "Path to store temporary images": "Путь для хранения временных изображений", + "If empty then in adapter folder": "Если пусто, то в папке адаптера", + "Default cache timeout (ms)": "Время ожидания кэша по умолчанию (мс)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Как часто камеры будут запрашивать новый снимок. Если 0, то при каждом запросе", + "URL": "URL-адрес", + "URL with basic auth": "URL с базовой авторизацией", + "RTSP Snapshot": "Снимок RTSP", + "Local URL": "Локальный URL-адрес", + "Web URL": "Веб-URL", + "Camera IP": "IP камеры", + "Port": "Порт", + "Path": "Дорожка", + "Cache timeout (ms)": "Время ожидания кэша (мс)", + "If empty, use default settings. If 0, cache disabled": "Если пусто, используйте настройки по умолчанию. Если 0, кеширование отключено", + "Camera URL": "URL-адрес камеры", + "Add new camera": "Добавить новую камеру", + "Add time to screenshot": "Добавить время на скриншот", + "Time format": "Формат времени", + "See here:": "Глянь сюда:", + "Add title": "Добавить заголовок", + "Height": "Высота", + "Width": "Ширина", + "in pixels": "в пикселях", + "Expert settings": "Экспертные настройки", + "Reolink E1 Snapshot": "Снимок Reolink E1", + "Eufy Security": "Юфи Безопасность", + "From eusec adapter": "От адаптера eusec", + "By IP Address": "По IP-адресу", + "Camera OID": "Идентификатор объекта камеры", + "Protocol": "Протокол", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Таймаут запроса (мс)", + "If empty or 0, use default settings.": "Если пусто или равно 0, используйте настройки по умолчанию." } diff --git a/src/src/i18n/uk.json b/src/src/i18n/uk.json index dbe1d11..e4e15c3 100644 --- a/src/src/i18n/uk.json +++ b/src/src/i18n/uk.json @@ -1,65 +1,65 @@ { - "Apply": "Застосувати", - "Are you sure?": "Ти впевнений?", - "Cancel": "Скасувати", - "Close": "Закрити", - "Description": "опис", - "Edit camera %s [%s]": "Редагувати камеру %s [%s]", - "Error": "Помилка", - "Load configuration from file": "Завантажити конфігурацію з файлу", - "Name": "Ім'я", - "Ok": "Гаразд", - "Options": "Опції", - "Password": "Пароль", - "Save": "зберегти", - "Save and close": "Зберегти та закрити", - "Save configuration to file": "Зберегти конфігурацію у файл", - "Success": "Успіх", - "Test": "Тест", - "Test connection": "Тестове підключення", - "Type": "Тип", - "Username": "Ім'я користувача", - "not connected": "не з'єднано", - "Cameras": "Фотоапарати", - "Local IP address": "Локальна IP-адреса", - "Local port": "Місцевий порт", - "Default timeout (ms)": "Час очікування за умовчанням (мс)", - "WEB Instance": "ВЕБ екземпляр", - "All": "все", - "Path to ffpmeg executable": "Шлях до виконуваного файлу ffpmeg", - "Like /usr/bin/ffmpeg": "Як \"/usr/bin/ffmpeg\"", - "Test path": "Тестовий шлях", - "Path to store temporary images": "Шлях для зберігання тимчасових зображень", - "If empty then in adapter folder": "Якщо порожній, то в папці адаптера", - "Default cache timeout (ms)": "Час очікування кешу за умовчанням (мс)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "Як часто камери запитуватимуть новий знімок. Якщо 0, то за кожним запитом", - "URL": "URL", - "URL with basic auth": "URL-адреса з базовою авторизацією", - "RTSP Snapshot": "Знімок RTSP", - "Local URL": "Локальна URL-адреса", - "Web URL": "Веб-адреса", - "Camera IP": "Камера IP", - "Port": "Порт", - "Path": "шлях", - "Cache timeout (ms)": "Час очікування кешу (мс)", - "If empty, use default settings. If 0, cache disabled": "Якщо пусто, використовувати налаштування за замовчуванням. Якщо 0, cache вимкнено", - "Camera URL": "URL-адреса камери", - "Add new camera": "Додати нову камеру", - "Add time to screenshot": "Додати час до знімка екрана", - "Time format": "Формат часу", - "See here:": "Дивіться тут:", - "Add title": "Додайте заголовок", - "Height": "Висота", - "Width": "Ширина", - "in pixels": "в пікселях", - "Expert settings": "Експертні налаштування", - "Reolink E1 Snapshot": "Знімок Reolink E1", - "Eufy Security": "Безпека Eufy", - "From eusec adapter": "Від адаптера eusec", - "By IP Address": "За IP-адресою", - "Camera OID": "ID об'єкта камери", - "Protocol": "Протокол", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "Час очікування запиту (мс)", - "If empty or 0, use default settings.": "Якщо пусто або 0, використовувати налаштування за замовчуванням." + "Apply": "Застосувати", + "Are you sure?": "Ти впевнений?", + "Cancel": "Скасувати", + "Close": "Закрити", + "Description": "опис", + "Edit camera %s [%s]": "Редагувати камеру %s [%s]", + "Error": "Помилка", + "Load configuration from file": "Завантажити конфігурацію з файлу", + "Name": "Ім'я", + "Ok": "Гаразд", + "Options": "Опції", + "Password": "Пароль", + "Save": "зберегти", + "Save and close": "Зберегти та закрити", + "Save configuration to file": "Зберегти конфігурацію у файл", + "Success": "Успіх", + "Test": "Тест", + "Test connection": "Тестове підключення", + "Type": "Тип", + "Username": "Ім'я користувача", + "not connected": "не з'єднано", + "Cameras": "Фотоапарати", + "Local IP address": "Локальна IP-адреса", + "Local port": "Місцевий порт", + "Default timeout (ms)": "Час очікування за умовчанням (мс)", + "WEB Instance": "ВЕБ екземпляр", + "All": "все", + "Path to ffpmeg executable": "Шлях до виконуваного файлу ffpmeg", + "Like /usr/bin/ffmpeg": "Як \"/usr/bin/ffmpeg\"", + "Test path": "Тестовий шлях", + "Path to store temporary images": "Шлях для зберігання тимчасових зображень", + "If empty then in adapter folder": "Якщо порожній, то в папці адаптера", + "Default cache timeout (ms)": "Час очікування кешу за умовчанням (мс)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "Як часто камери запитуватимуть новий знімок. Якщо 0, то за кожним запитом", + "URL": "URL", + "URL with basic auth": "URL-адреса з базовою авторизацією", + "RTSP Snapshot": "Знімок RTSP", + "Local URL": "Локальна URL-адреса", + "Web URL": "Веб-адреса", + "Camera IP": "Камера IP", + "Port": "Порт", + "Path": "шлях", + "Cache timeout (ms)": "Час очікування кешу (мс)", + "If empty, use default settings. If 0, cache disabled": "Якщо пусто, використовувати налаштування за замовчуванням. Якщо 0, cache вимкнено", + "Camera URL": "URL-адреса камери", + "Add new camera": "Додати нову камеру", + "Add time to screenshot": "Додати час до знімка екрана", + "Time format": "Формат часу", + "See here:": "Дивіться тут:", + "Add title": "Додайте заголовок", + "Height": "Висота", + "Width": "Ширина", + "in pixels": "в пікселях", + "Expert settings": "Експертні налаштування", + "Reolink E1 Snapshot": "Знімок Reolink E1", + "Eufy Security": "Безпека Eufy", + "From eusec adapter": "Від адаптера eusec", + "By IP Address": "За IP-адресою", + "Camera OID": "ID об'єкта камери", + "Protocol": "Протокол", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "Час очікування запиту (мс)", + "If empty or 0, use default settings.": "Якщо пусто або 0, використовувати налаштування за замовчуванням." } diff --git a/src/src/i18n/zh-cn.json b/src/src/i18n/zh-cn.json index 17b8d6f..3ca2d1f 100644 --- a/src/src/i18n/zh-cn.json +++ b/src/src/i18n/zh-cn.json @@ -1,65 +1,65 @@ { - "Are you sure?": "你确定吗?", - "Cancel": "取消", - "Close": "关", - "Error": "错误", - "Load configuration from file": "从文件加载配置", - "Ok": "好", - "Options": "选件", - "Save": "保存", - "Save and close": "保存并关闭", - "Save configuration to file": "将配置保存到文件", - "Success": "成功", - "Test connection": "测试连接", - "Type": "类型", - "not connected": "未连接", - "Description": "描述", - "Name": "名称", - "Test": "测试", - "Apply": "应用", - "Username": "用户名", - "Password": "密码", - "Edit camera %s [%s]": "编辑相机%s [%s]", - "Cameras": "相机", - "Local IP address": "本地IP地址", - "Local port": "当地港口", - "Default timeout (ms)": "默认超时(毫秒)", - "WEB Instance": "网页实例", - "All": "全部", - "Path to ffpmeg executable": "ffpmeg 可执行文件的路径", - "Like /usr/bin/ffmpeg": "比如“/usr/bin/ffmpeg”", - "Test path": "测试路径", - "Path to store temporary images": "存放临时图片的路径", - "If empty then in adapter folder": "如果为空则在适配器文件夹中", - "Default cache timeout (ms)": "默认缓存超时(毫秒)", - "How often the cameras will be asked for new snapshot. If 0, then by every request": "相机被要求拍摄新快照的频率。如果为 0,则按每个请求", - "URL": "网址", - "URL with basic auth": "具有基本身份验证的 URL", - "RTSP Snapshot": "RTSP 快照", - "Local URL": "本地网址", - "Web URL": "网址", - "Camera IP": "摄像机IP", - "Port": "港口", - "Path": "小路", - "Cache timeout (ms)": "缓存超时(毫秒)", - "If empty, use default settings. If 0, cache disabled": "如果为空,则使用默认设置。如果为 0,则禁用缓存", - "Camera URL": "相机网址", - "Add new camera": "添加新相机", - "Add time to screenshot": "添加时间到屏幕截图", - "Time format": "时间格式", - "See here:": "看这里:", - "Add title": "添加标题", - "Height": "高度", - "Width": "宽度", - "in pixels": "以像素为单位", - "Expert settings": "专家设置", - "Reolink E1 Snapshot": "重新链接 E1 快照", - "Eufy Security": "尤菲安全", - "From eusec adapter": "来自eusec适配器", - "By IP Address": "通过IP地址", - "Camera OID": "相机对象ID", - "Protocol": "协议", - "HiKam / WiWiCam": "HiKam / WiWiCam", - "Request timeout (ms)": "请求超时(毫秒)", - "If empty or 0, use default settings.": "如果为空或 0,则使用默认设置。" + "Are you sure?": "你确定吗?", + "Cancel": "取消", + "Close": "关", + "Error": "错误", + "Load configuration from file": "从文件加载配置", + "Ok": "好", + "Options": "选件", + "Save": "保存", + "Save and close": "保存并关闭", + "Save configuration to file": "将配置保存到文件", + "Success": "成功", + "Test connection": "测试连接", + "Type": "类型", + "not connected": "未连接", + "Description": "描述", + "Name": "名称", + "Test": "测试", + "Apply": "应用", + "Username": "用户名", + "Password": "密码", + "Edit camera %s [%s]": "编辑相机%s [%s]", + "Cameras": "相机", + "Local IP address": "本地IP地址", + "Local port": "当地港口", + "Default timeout (ms)": "默认超时(毫秒)", + "WEB Instance": "网页实例", + "All": "全部", + "Path to ffpmeg executable": "ffpmeg 可执行文件的路径", + "Like /usr/bin/ffmpeg": "比如“/usr/bin/ffmpeg”", + "Test path": "测试路径", + "Path to store temporary images": "存放临时图片的路径", + "If empty then in adapter folder": "如果为空则在适配器文件夹中", + "Default cache timeout (ms)": "默认缓存超时(毫秒)", + "How often the cameras will be asked for new snapshot. If 0, then by every request": "相机被要求拍摄新快照的频率。如果为 0,则按每个请求", + "URL": "网址", + "URL with basic auth": "具有基本身份验证的 URL", + "RTSP Snapshot": "RTSP 快照", + "Local URL": "本地网址", + "Web URL": "网址", + "Camera IP": "摄像机IP", + "Port": "港口", + "Path": "小路", + "Cache timeout (ms)": "缓存超时(毫秒)", + "If empty, use default settings. If 0, cache disabled": "如果为空,则使用默认设置。如果为 0,则禁用缓存", + "Camera URL": "相机网址", + "Add new camera": "添加新相机", + "Add time to screenshot": "添加时间到屏幕截图", + "Time format": "时间格式", + "See here:": "看这里:", + "Add title": "添加标题", + "Height": "高度", + "Width": "宽度", + "in pixels": "以像素为单位", + "Expert settings": "专家设置", + "Reolink E1 Snapshot": "重新链接 E1 快照", + "Eufy Security": "尤菲安全", + "From eusec adapter": "来自eusec适配器", + "By IP Address": "通过IP地址", + "Camera OID": "相机对象ID", + "Protocol": "协议", + "HiKam / WiWiCam": "HiKam / WiWiCam", + "Request timeout (ms)": "请求超时(毫秒)", + "If empty or 0, use default settings.": "如果为空或 0,则使用默认设置。" } diff --git a/src/vite.config.ts b/src/vite.config.ts index 81f69a1..350c758 100644 --- a/src/vite.config.ts +++ b/src/vite.config.ts @@ -1,11 +1,36 @@ import type { UserConfig } from 'vite'; +export interface RollupLog { + binding?: string; + cause?: unknown; + code?: string; + exporter?: string; + frame?: string; + hook?: string; + id?: string; + ids?: string[]; + loc?: { + column: number; + file?: string; + line: number; + }; + message: string; + meta?: any; + names?: string[]; + plugin?: string; + pluginCode?: unknown; + pos?: number; + reexporter?: string; + stack?: string; + url?: string; +} + export default { build: { outDir: 'build', sourcemap: true, rollupOptions: { - onwarn: (warning: any, warn: (log: any) => void) => { + onwarn: (warning: RollupLog, warn: (log: RollupLog) => void) => { if (warning.code === 'MODULE_LEVEL_DIRECTIVE' || warning.code === 'SOURCEMAP_ERROR') { return; } diff --git a/widgets/cameras/__federation_expose_RtspCamera-DEDIMDIg.js b/widgets/cameras/__federation_expose_RtspCamera-DEDIMDIg.js new file mode 100644 index 0000000..9f7b2ed --- /dev/null +++ b/widgets/cameras/__federation_expose_RtspCamera-DEDIMDIg.js @@ -0,0 +1,564 @@ +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +var __async = (__this, __arguments, generator) => { + return new Promise((resolve, reject) => { + var fulfilled = (value) => { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + }; + var rejected = (value) => { + try { + step(generator.throw(value)); + } catch (e) { + reject(e); + } + }; + var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); + step((generator = generator.apply(__this, __arguments)).next()); + }); +}; +import { importShared, __tla as __tla_0 } from "./__federation_fn_import-BINF-Dl9.js"; +import { j as jsxRuntimeExports } from "./jsx-runtime-XyhW1nAj.js"; +let CameraField, TRANSLATION_PREFIX, VisRxWidget, RtspCamera; +let __tla = Promise.all([ + (() => { + try { + return __tla_0; + } catch (e) { + } + })() +]).then(() => __async(void 0, null, function* () { + var _a; + const { Component } = yield importShared("react"); + VisRxWidget = (_a = class extends Component { + constructor(props) { + super(props); + this.state = {}; + } + componentDidMount() { + } + componentWillUnmount() { + } + static findField(_widgetInfo, _name) { + return null; + } + static getI18nPrefix() { + return ""; + } + static getText(_text) { + return ""; + } + static t(_key, ..._args) { + return ""; + } + static getLanguage() { + return "en"; + } + onCommand(_command, _option) { + return false; + } + onStateUpdated(_id, _state) { + } + onIoBrokerStateChanged(_id, _state) { + } + onStateChanged(_id, _state, _doNotApplyState) { + return {}; + } + applyBinding(_stateId, _newState) { + } + onRxDataChanged(_prevRxData) { + } + onRxStyleChanged(_prevRxStyle) { + } + onPropertiesUpdated() { + return Promise.resolve(); + } + formatValue(value, _round) { + return value.toString(); + } + wrapContent(_content, _addToHeader, _cardContentStyle, _headerStyle, _onCardClick, _components) { + return jsxRuntimeExports.jsx("div", {}); + } + renderWidgetBody(_props) { + return jsxRuntimeExports.jsx("div", {}); + } + getWidgetView(_view, _props) { + return jsxRuntimeExports.jsx("div", {}); + } + getWidgetInWidget(_view, _wid, _props) { + return jsxRuntimeExports.jsx("div", {}); + } + getWidgetInfo() { + return {}; + } + }, __publicField(_a, "POSSIBLE_MUI_STYLES"), _a); + const React = yield importShared("react"); + const { useEffect } = React; + const { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Select } = yield importShared("@mui/material"); + const { Close } = yield importShared("@mui/icons-material"); + const { I18n } = yield importShared("@iobroker/adapter-react-v5"); + TRANSLATION_PREFIX = "cameras_"; + const styles = { + camera: { + width: "100%", + height: "100%", + objectFit: "contain", + cursor: "pointer" + }, + fullCamera: { + width: "100%", + height: "100%", + objectFit: "contain" + }, + imageContainer: { + flex: 1, + overflow: "hidden", + position: "relative", + width: "100%", + height: "100%" + } + }; + CameraField = (props) => { + const [cameras, setCameras] = React.useState(null); + const [camera, setCamera] = React.useState(props.data[props.field.name] || ""); + useEffect(() => { + void (() => __async(void 0, null, function* () { + const _cameras = []; + const instances = yield props.context.socket.getAdapterInstances("cameras"); + instances.forEach((instance) => { + const instanceId = instance._id.split(".").pop(); + instance.native.cameras.filter((iCamera) => !props.rtsp || iCamera.type === "rtsp" || iCamera.rtsp).forEach((iCamera) => { + _cameras.push({ + enabled: iCamera.enabled !== false, + value: `${instanceId}/${iCamera.name}`, + label: `cameras.${instanceId}/${iCamera.name}`, + subLabel: iCamera.desc ? `${iCamera.desc}/${iCamera.ip}` : iCamera.ip || "" + }); + }); + }); + setCameras(_cameras); + }))(); + }, [ + props.context.socket, + props.rtsp + ]); + return cameras ? jsxRuntimeExports.jsx(Select, { + fullWidth: true, + variant: "standard", + value: camera, + onChange: (e) => { + props.onDataChange({ + [props.field.name]: e.target.value + }); + setCamera(e.target.value); + }, + children: cameras.map((iCamera) => jsxRuntimeExports.jsxs(MenuItem, { + value: iCamera.value, + style: { + display: "block", + opacity: iCamera.enabled ? 1 : 0.5 + }, + children: [ + jsxRuntimeExports.jsx("div", { + children: iCamera.label + }), + jsxRuntimeExports.jsx("div", { + style: { + fontSize: 10, + fontStyle: "italic", + opacity: 0.7 + }, + children: iCamera.subLabel + }), + !iCamera.enabled ? jsxRuntimeExports.jsx("div", { + style: { + fontSize: 10, + fontStyle: "italic", + opacity: 0.7, + color: "red" + }, + children: props.t("disabled") + }) : null + ] + }, iCamera.value)) + }) : jsxRuntimeExports.jsx(CircularProgress, {}); + }; + RtspCamera = class extends VisRxWidget { + constructor(props) { + super(props); + __publicField(this, "videoInterval", null); + __publicField(this, "videoRef"); + __publicField(this, "fullVideoRef"); + __publicField(this, "currentCam", null); + __publicField(this, "useMessages"); + __publicField(this, "subscribedOnAlive", ""); + __publicField(this, "updateStream", (_id, state) => { + if (state == null ? void 0 : state.val) { + if (this.state.loading) { + this.setState({ + loading: false + }); + } + RtspCamera.drawCamera(this.videoRef, state.val); + if (this.state.full) { + RtspCamera.drawCamera(this.fullVideoRef, state.val); + } + } + }); + __publicField(this, "onCameras", (data) => { + if (data) { + if (typeof data === "object" && (data.accepted || data.error)) { + if (data.error) { + console.error(data.error); + } + return; + } + if (this.state.loading) { + this.setState({ + loading: false + }); + } + RtspCamera.drawCamera(this.videoRef, data); + if (this.state.full) { + RtspCamera.drawCamera(this.fullVideoRef, data); + } + } + }); + __publicField(this, "onAliveChanged", (id, state) => { + const data = RtspCamera.getNameAndInstance(this.state.rxData.camera); + if (data && id === `system.adapter.cameras.${data.instanceId}.alive`) { + const alive = !!(state == null ? void 0 : state.val); + if (alive !== this.state.alive) { + this.setState({ + alive + }, () => this.propertiesUpdate()); + } + } + }); + this.videoRef = React.createRef(); + this.fullVideoRef = React.createRef(); + Object.assign(this.state, { + full: false, + alive: false + }); + } + static getI18nPrefix() { + return TRANSLATION_PREFIX; + } + static getWidgetInfo() { + return { + id: "tplCameras2RtspCamera", + visSet: "cameras", + visName: "RTSP Camera", + visWidgetLabel: "RTSP Camera", + visWidgetSetLabel: "Cameras", + visSetLabel: "Cameras", + visSetColor: "#9f0026", + visAttrs: [ + { + name: "common", + fields: [ + { + name: "noCard", + label: "without_card", + type: "checkbox" + }, + { + name: "widgetTitle", + label: "name", + hidden: "!!data.noCard" + }, + { + name: "width", + label: "videoWidth", + type: "number", + tooltip: "tooltip_videoWidth" + }, + { + label: "Camera", + name: "camera", + type: "custom", + component: (field, data, onDataChange, props) => jsxRuntimeExports.jsx(CameraField, { + field, + rtsp: true, + data, + onDataChange, + context: props.context, + t: (word) => I18n.t(TRANSLATION_PREFIX + word) + }) + } + ] + } + ], + visDefaultStyle: { + width: "100%", + height: 240, + position: "relative" + }, + visPrev: "widgets/cameras/img/prev_camera.png" + }; + } + getWidgetInfo() { + return RtspCamera.getWidgetInfo(); + } + static drawCamera(ref, data) { + const canvas = ref.current; + if (!canvas) { + return; + } + const context = canvas.getContext("2d"); + if (context) { + try { + const imageObj = new Image(); + imageObj.src = `data:image/jpeg;base64,${data}`; + imageObj.onload = () => { + canvas.width = imageObj.width; + canvas.height = imageObj.height; + context.drawImage(imageObj, 0, 0, imageObj.width, imageObj.height); + }; + imageObj.onerror = (e) => { + console.error(e); + }; + } catch (e) { + console.error(e); + } + } + } + static getNameAndInstance(value) { + if (!value) { + return null; + } + const pos = value.indexOf("/"); + if (pos === -1) { + return null; + } + return { + instanceId: value.substring(0, pos), + name: value.substring(pos + 1) + }; + } + propertiesUpdate() { + return __async(this, null, function* () { + if (this.useMessages === void 0) { + this.useMessages = yield this.props.context.socket.checkFeatureSupported("INSTANCE_MESSAGES"); + } + if (this.state.rxData.camera !== this.currentCam) { + if (this.state.alive) { + if (this.currentCam) { + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + yield this.props.context.socket.unsubscribeFromInstance(`cameras.${instanceId}`, `startCamera/${name}`, this.onCameras); + } else { + yield this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: false + }); + yield this.props.context.socket.unsubscribeState(`cameras.${instanceId}.${name}.stream`, this.updateStream); + } + } + } + if (this.state.rxData.camera) { + this.setState({ + loading: true + }); + const result = RtspCamera.getNameAndInstance(this.state.rxData.camera); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + yield this.props.context.socket.subscribeOnInstance(`cameras.${instanceId}`, `startCamera/${name}`, { + width: this.getImageWidth() + }, this.onCameras); + } else { + yield this.props.context.socket.subscribeState(`cameras.${instanceId}.${name}.stream`, this.updateStream); + } + } + } else { + const canvas = this.videoRef.current; + if (canvas) { + const context = canvas.getContext("2d"); + context == null ? void 0 : context.clearRect(0, 0, canvas.width, canvas.height); + } + } + this.currentCam = this.state.rxData.camera; + } else if (this.currentCam) { + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (!this.useMessages) { + yield this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: false + }); + yield this.props.context.socket.unsubscribeState(`cameras.${instanceId}.${name}.stream`, this.updateStream); + } + } + this.currentCam = null; + } + } else if (this.currentCam && this.state.alive) { + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + yield this.props.context.socket.subscribeOnInstance(`cameras.${instanceId}`, `startCamera/${name}`, { + width: this.getImageWidth() + }, this.onCameras); + } else { + yield this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: true, + expire: 30 + }); + } + } + } else if (this.currentCam && !this.state.alive) { + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (!this.useMessages) { + yield this.props.context.socket.setState(`cameras.${instanceId}.${name}.running`, { + val: false + }); + yield this.props.context.socket.unsubscribeState(`cameras.${instanceId}.${name}.stream`, this.updateStream); + } + } + this.currentCam = null; + } + }); + } + getImageWidth(isFull) { + var _a2, _b, _c; + isFull = isFull === void 0 ? this.state.full : isFull; + if (isFull && this.fullVideoRef.current) { + return ((_a2 = this.fullVideoRef.current.parentElement) == null ? void 0 : _a2.clientWidth) || 0; + } + return ((_c = (_b = this.videoRef.current) == null ? void 0 : _b.parentElement) == null ? void 0 : _c.clientWidth) || 0; + } + subscribeOnAlive() { + const data = RtspCamera.getNameAndInstance(this.state.rxData.camera); + if (this.subscribedOnAlive !== (data ? data.instanceId : null)) { + if (this.subscribedOnAlive) { + this.props.context.socket.unsubscribeState(`system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged); + this.subscribedOnAlive = ""; + } + if (data) { + this.props.context.socket.subscribeState(`system.adapter.cameras.${data.instanceId}.alive`, this.onAliveChanged); + this.subscribedOnAlive = data.instanceId; + } + } + } + componentDidMount() { + super.componentDidMount(); + setTimeout(() => this.propertiesUpdate(), 100); + this.subscribeOnAlive(); + this.videoInterval = setInterval(() => this.propertiesUpdate(), 14e3); + } + onRxDataChanged() { + return __async(this, null, function* () { + this.subscribeOnAlive(); + yield this.propertiesUpdate(); + }); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.videoInterval && clearInterval(this.videoInterval); + this.videoInterval = null; + if (this.subscribedOnAlive) { + this.props.context.socket.unsubscribeState(`system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged); + this.subscribedOnAlive = ""; + } + if (this.currentCam) { + const result = RtspCamera.getNameAndInstance(this.currentCam); + if (result) { + const { instanceId, name } = result; + if (this.useMessages) { + this.props.context.socket.unsubscribeFromInstance(`cameras.${instanceId}`, `startCamera/${name}`, this.onCameras).catch((e) => console.error(e)); + } + } + } + } + renderDialog() { + return this.state.full ? jsxRuntimeExports.jsxs(Dialog, { + fullWidth: true, + maxWidth: "lg", + open: true, + onClose: () => this.setState({ + full: false + }), + children: [ + jsxRuntimeExports.jsx(DialogTitle, { + children: this.state.rxData.widgetTitle + }), + jsxRuntimeExports.jsx(DialogContent, { + children: jsxRuntimeExports.jsx("div", { + style: styles.imageContainer, + children: jsxRuntimeExports.jsx("canvas", { + ref: this.fullVideoRef, + style: styles.fullCamera + }) + }) + }), + jsxRuntimeExports.jsx(DialogActions, { + children: jsxRuntimeExports.jsx(Button, { + onClick: (e) => { + e.stopPropagation(); + e.preventDefault(); + this.setState({ + full: false + }); + }, + startIcon: jsxRuntimeExports.jsx(Close, {}), + color: "primary", + variant: "contained", + children: I18n.t(`${TRANSLATION_PREFIX}Close`) + }) + }) + ] + }) : null; + } + renderWidgetBody(props) { + super.renderWidgetBody(props); + const content = jsxRuntimeExports.jsxs("div", { + style: styles.imageContainer, + onClick: () => this.setState({ + full: true + }), + children: [ + this.state.loading && this.state.alive && jsxRuntimeExports.jsx(CircularProgress, { + style: styles.progress + }), + !this.state.alive ? jsxRuntimeExports.jsx("div", { + style: { + position: "absolute", + top: 0, + left: 0 + }, + children: I18n.t(`${TRANSLATION_PREFIX}Camera instance %s inactive`, (this.state.rxData.camera || "").split("/")[0]) + }) : null, + jsxRuntimeExports.jsx("canvas", { + ref: this.videoRef, + style: styles.camera + }), + this.renderDialog() + ] + }); + if (this.state.rxData.noCard || props.widget.usedInWidget) { + return content; + } + return this.wrapContent(content, null, { + boxSizing: "border-box", + paddingBottom: 10, + height: "100%" + }); + } + }; +})); +export { + CameraField, + TRANSLATION_PREFIX, + VisRxWidget as V, + __tla, + RtspCamera as default +}; diff --git a/widgets/cameras/__federation_expose_SnapshotCamera-BDbQmboE.js b/widgets/cameras/__federation_expose_SnapshotCamera-BDbQmboE.js new file mode 100644 index 0000000..21fe2c9 --- /dev/null +++ b/widgets/cameras/__federation_expose_SnapshotCamera-BDbQmboE.js @@ -0,0 +1,413 @@ +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +var __async = (__this, __arguments, generator) => { + return new Promise((resolve, reject) => { + var fulfilled = (value) => { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + }; + var rejected = (value) => { + try { + step(generator.throw(value)); + } catch (e) { + reject(e); + } + }; + var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); + step((generator = generator.apply(__this, __arguments)).next()); + }); +}; +import { importShared, __tla as __tla_0 } from "./__federation_fn_import-BINF-Dl9.js"; +import { j as jsxRuntimeExports } from "./jsx-runtime-XyhW1nAj.js"; +import { V as VisRxWidget, CameraField, TRANSLATION_PREFIX, __tla as __tla_1 } from "./__federation_expose_RtspCamera-DEDIMDIg.js"; +let SnapshotCamera; +let __tla = Promise.all([ + (() => { + try { + return __tla_0; + } catch (e) { + } + })(), + (() => { + try { + return __tla_1; + } catch (e) { + } + })() +]).then(() => __async(void 0, null, function* () { + const React = yield importShared("react"); + const { Button, Dialog, DialogActions, DialogContent, DialogTitle } = yield importShared("@mui/material"); + const { Close } = yield importShared("@mui/icons-material"); + const { I18n } = yield importShared("@iobroker/adapter-react-v5"); + const styles = { + camera: { + width: "100%", + height: "100%", + objectFit: "contain", + cursor: "pointer" + }, + fullCamera: { + width: "100%", + height: "100%", + objectFit: "contain" + }, + imageContainer: { + flex: 1, + overflow: "hidden", + position: "relative", + width: "100%", + height: "100%" + } + }; + SnapshotCamera = class extends VisRxWidget { + constructor(props) { + super(props); + __publicField(this, "videoRef"); + __publicField(this, "fullVideoRef"); + __publicField(this, "loading", false); + __publicField(this, "subscribedOnAlive", ""); + __publicField(this, "pollingInterval", null); + __publicField(this, "updateImage", () => { + if (!this.loading) { + this.loading = true; + if (this.videoRef.current) { + this.videoRef.current.src = this.getUrl(); + this.videoRef.current.onload = (e) => { + const image = e.currentTarget; + if (image && image.style.opacity !== "1") { + image.style.opacity = "1"; + } + this.state.error && this.setState({ + error: false + }); + this.loading = false; + }; + this.videoRef.current.onerror = (e) => { + const image = e.target; + if (image && image.style.opacity !== "0") { + image.style.opacity = "0"; + } + !this.state.error && this.setState({ + error: true + }); + this.loading = false; + }; + } + if (this.fullVideoRef.current && this.state.full) { + this.fullVideoRef.current.src = this.getUrl(true); + } + } + }); + __publicField(this, "onAliveChanged", (id, state) => { + const data = SnapshotCamera.getNameAndInstance(this.state.rxData.camera); + if (data && id === `system.adapter.cameras.${data.instanceId}.alive`) { + const alive = !!(state == null ? void 0 : state.val); + if (alive !== this.state.alive) { + this.setState({ + alive + }, () => this.restartPollingInterval()); + } + } + }); + this.videoRef = React.createRef(); + this.fullVideoRef = React.createRef(); + Object.assign(this.state, { + full: false, + alive: false, + error: false + }); + } + static getWidgetInfo() { + return { + id: "tplCameras2SnapshotCamera", + visSet: "cameras", + visName: "Polling Camera", + visWidgetLabel: "Polling Camera", + visAttrs: [ + { + name: "common", + fields: [ + { + name: "noCard", + label: "without_card", + type: "checkbox" + }, + { + name: "widgetTitle", + label: "name", + hidden: "!!data.noCard" + }, + { + name: "pollingInterval", + label: "pollingInterval", + tooltip: "tooltip_ms", + type: "number", + default: 500 + }, + { + name: "pollingIntervalFull", + label: "pollingIntervalFull", + tooltip: "tooltip_ms", + type: "number", + default: 300 + }, + { + name: "noCacheByFull", + label: "noCacheByFull", + type: "checkbox" + }, + { + name: "rotate", + label: "rotate", + type: "select", + noTranslation: true, + options: [ + { + value: 0, + label: "0\xB0" + }, + { + value: 90, + label: "90\xB0" + }, + { + value: 180, + label: "180\xB0" + }, + { + value: 270, + label: "270\xB0" + } + ] + }, + { + label: "Camera", + name: "camera", + type: "custom", + component: (field, data, onDataChange, props) => jsxRuntimeExports.jsx(CameraField, { + field, + data, + onDataChange, + context: props.context, + t: (word) => I18n.t(TRANSLATION_PREFIX + word) + }) + }, + { + label: "camera_in_dialog", + name: "bigCamera", + type: "custom", + component: (field, data, onDataChange, props) => jsxRuntimeExports.jsx(CameraField, { + field, + data, + onDataChange, + context: props.context, + t: (word) => I18n.t(TRANSLATION_PREFIX + word) + }), + hidden: "!data.camera" + } + ] + } + ], + visDefaultStyle: { + width: "100%", + height: 240, + position: "relative" + }, + visPrev: "widgets/cameras/img/prev_snapshot.png" + }; + } + getWidgetInfo() { + return SnapshotCamera.getWidgetInfo(); + } + static getNameAndInstance(value) { + if (!value) { + return null; + } + const pos = value.indexOf("/"); + if (pos === -1) { + return null; + } + return { + instanceId: value.substring(0, pos), + name: value.substring(pos + 1) + }; + } + getImageWidth(isFull) { + var _a, _b, _c, _d; + isFull = isFull === void 0 ? this.state.full : isFull; + if (isFull && this.fullVideoRef.current) { + return ((_b = (_a = this.fullVideoRef.current) == null ? void 0 : _a.parentElement) == null ? void 0 : _b.clientWidth) || 0; + } + return ((_d = (_c = this.videoRef.current) == null ? void 0 : _c.parentElement) == null ? void 0 : _d.clientWidth) || 0; + } + subscribeOnAlive() { + const data = SnapshotCamera.getNameAndInstance(this.state.rxData.camera); + if (this.subscribedOnAlive !== (data ? data.instanceId : null)) { + if (this.subscribedOnAlive) { + this.props.context.socket.unsubscribeState(`system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged); + this.subscribedOnAlive = ""; + } + if (data) { + this.props.context.socket.subscribeState(`system.adapter.cameras.${data.instanceId}.alive`, this.onAliveChanged); + this.subscribedOnAlive = data.instanceId; + } + } + } + restartPollingInterval() { + if (this.pollingInterval) { + clearInterval(this.pollingInterval); + this.pollingInterval = null; + } + if (this.state.alive) { + this.pollingInterval = setInterval(this.updateImage, parseInt(this.state.full ? this.state.rxData.pollingIntervalFull : this.state.rxData.pollingInterval, 10) || 500); + } + } + componentDidMount() { + super.componentDidMount(); + this.subscribeOnAlive(); + } + onRxDataChanged() { + this.subscribeOnAlive(); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.pollingInterval && clearInterval(this.pollingInterval); + this.pollingInterval = null; + if (this.subscribedOnAlive) { + this.props.context.socket.unsubscribeState(`system.adapter.cameras.${this.subscribedOnAlive}.alive`, this.onAliveChanged); + this.subscribedOnAlive = ""; + } + } + renderDialog(url) { + if (this.state.full && this.state.rxData.bigCamera) { + url = this.getUrl(true) || url; + } + return this.state.full ? jsxRuntimeExports.jsxs(Dialog, { + fullWidth: true, + maxWidth: "lg", + open: true, + onClose: () => this.setState({ + full: false + }, () => this.restartPollingInterval()), + children: [ + jsxRuntimeExports.jsx(DialogTitle, { + children: this.state.rxData.widgetTitle + }), + jsxRuntimeExports.jsx(DialogContent, { + children: jsxRuntimeExports.jsx("div", { + style: styles.imageContainer, + children: jsxRuntimeExports.jsx("img", { + src: url, + ref: this.fullVideoRef, + style: styles.fullCamera, + alt: this.state.rxData.camera + }) + }) + }), + jsxRuntimeExports.jsx(DialogActions, { + children: jsxRuntimeExports.jsx(Button, { + onClick: (e) => { + e.stopPropagation(); + e.preventDefault(); + this.setState({ + full: false + }, () => this.restartPollingInterval()); + }, + startIcon: jsxRuntimeExports.jsx(Close, {}), + color: "primary", + variant: "contained", + children: I18n.t(`${TRANSLATION_PREFIX}Close`) + }) + }) + ] + }) : null; + } + getUrl(isFull) { + if (isFull && !this.state.rxData.bigCamera) { + const url = `../cameras.${this.state.rxData.bigCamera}?`; + const params = [ + `ts=${Date.now()}`, + `w=${this.getImageWidth(true)}`, + `noCache=${this.state.rxData.noCacheByFull}`, + this.state.rxData.rotate ? `angle=${this.state.rxData.rotate}` : "" + ]; + return url + params.filter((p) => p).join("&"); + } + if (this.state.rxData.camera) { + const url = `../cameras.${this.state.rxData.camera}?`; + const params = [ + `ts=${Date.now()}`, + `w=${this.getImageWidth(isFull)}`, + `noCache=${isFull ? this.state.rxData.noCacheByFull : false}`, + this.state.rxData.rotate ? `angle=${this.state.rxData.rotate}` : "" + ]; + return url + params.filter((p) => p).join("&"); + } + return ""; + } + renderWidgetBody(props) { + super.renderWidgetBody(props); + const url = this.getUrl(); + const content = jsxRuntimeExports.jsxs("div", { + style: styles.imageContainer, + onClick: () => !this.state.error && this.setState({ + full: true + }, () => this.restartPollingInterval()), + children: [ + !this.state.alive ? jsxRuntimeExports.jsx("div", { + style: { + position: "absolute", + top: 20, + left: 0 + }, + children: I18n.t(`${TRANSLATION_PREFIX}Camera instance %s inactive`, (this.state.rxData.camera || "").split("/")[0]) + }) : null, + url ? jsxRuntimeExports.jsx("img", { + src: url, + ref: this.videoRef, + style: styles.camera, + alt: this.state.rxData.camera + }) : I18n.t(`${TRANSLATION_PREFIX}No camera selected`), + this.state.alive && this.state.error ? jsxRuntimeExports.jsxs("div", { + style: { + position: "absolute", + top: 20, + left: 0 + }, + children: [ + jsxRuntimeExports.jsxs("div", { + style: { + color: "red" + }, + children: [ + I18n.t(`${TRANSLATION_PREFIX}Cannot load URL`), + ":" + ] + }), + jsxRuntimeExports.jsx("div", { + children: this.getUrl(true) + }) + ] + }) : null, + this.renderDialog(url) + ] + }); + if (this.state.rxData.noCard || props.widget.usedInWidget) { + return content; + } + return this.wrapContent(content, null, { + boxSizing: "border-box", + paddingBottom: 10, + height: "100%" + }); + } + }; +})); +export { + __tla, + SnapshotCamera as default +}; diff --git a/widgets/cameras/__federation_expose_Translations-HGakruMK.js b/widgets/cameras/__federation_expose_Translations-HGakruMK.js new file mode 100644 index 0000000..2e97288 --- /dev/null +++ b/widgets/cameras/__federation_expose_Translations-HGakruMK.js @@ -0,0 +1,435 @@ +const without_card$a = "Without frame"; +const name$a = "Name"; +const cameras_set_label$a = "RTSP Camera"; +const Cameras$a = "Cameras"; +const Camera$a = "Camera"; +const Close$a = "Close"; +const videoWidth$a = "Video width"; +const tooltip_videoWidth$a = "Do not set width of video too hight to save broadband. It is for both: small and full videos. If not set, the initial size of widget will be taken."; +const disabled$a = "disabled"; +const pollingInterval$a = "Polling interval"; +const pollingIntervalFull$a = "Polling interval in dialog"; +const tooltip_ms$a = "milliseconds"; +const rotate$a = "Rotate image"; +const noCacheByFull$a = "No cache in dialog"; +const camera_in_dialog$a = "Camera in Dialog"; +const en = { + "RTSP Camera": "RTSP Camera", + without_card: without_card$a, + name: name$a, + "RTSP url": "RTSP url", + cameras_set_label: cameras_set_label$a, + Cameras: Cameras$a, + Camera: Camera$a, + Close: Close$a, + videoWidth: videoWidth$a, + tooltip_videoWidth: tooltip_videoWidth$a, + "Camera instance %s inactive": "Camera instance %s inactive", + disabled: disabled$a, + pollingInterval: pollingInterval$a, + pollingIntervalFull: pollingIntervalFull$a, + tooltip_ms: tooltip_ms$a, + rotate: rotate$a, + "Polling Camera": "Polling Camera", + "Cannot load URL": "Cannot load URL", + noCacheByFull: noCacheByFull$a, + camera_in_dialog: camera_in_dialog$a, + "No camera selected": "No camera selected" +}; +const cameras_set_label$9 = "RTSP-Kamera"; +const name$9 = "Name"; +const without_card$9 = "Ohne Rahmen"; +const Cameras$9 = "Kameras"; +const Camera$9 = "Kamera"; +const Close$9 = "Schlie\xDFen"; +const videoWidth$9 = "Videobreite"; +const tooltip_videoWidth$9 = "Stellen Sie die Breite des Videos nicht zu hoch ein, um Breitband zu sparen. Es ist sowohl f\xFCr kleine als auch f\xFCr vollst\xE4ndige Videos geeignet. Wenn nicht festgelegt, wird die anf\xE4ngliche Gr\xF6\xDFe des Widgets verwendet."; +const disabled$9 = "deaktiviert"; +const pollingInterval$9 = "Abrufintervall"; +const pollingIntervalFull$9 = "Abfrageintervall im Dialog"; +const tooltip_ms$9 = "Millisekunden"; +const rotate$9 = "Bild drehen"; +const noCacheByFull$9 = "Kein Cache im Dialog"; +const camera_in_dialog$9 = "Kamera im Dialog"; +const de = { + "RTSP url": "RTSP-URL", + "RTSP Camera": "RTSP-Kamera", + cameras_set_label: cameras_set_label$9, + name: name$9, + without_card: without_card$9, + Cameras: Cameras$9, + Camera: Camera$9, + Close: Close$9, + videoWidth: videoWidth$9, + tooltip_videoWidth: tooltip_videoWidth$9, + "Camera instance %s inactive": "Kamerainstanz %s inaktiv", + disabled: disabled$9, + pollingInterval: pollingInterval$9, + pollingIntervalFull: pollingIntervalFull$9, + tooltip_ms: tooltip_ms$9, + rotate: rotate$9, + "Polling Camera": "URL kamera", + "Cannot load URL": "URL kann nicht geladen werden", + noCacheByFull: noCacheByFull$9, + camera_in_dialog: camera_in_dialog$9, + "No camera selected": "Keine Kamera ausgew\xE4hlt" +}; +const cameras_set_label$8 = "RTSP-\u043A\u0430\u043C\u0435\u0440\u0430"; +const name$8 = "\u0418\u043C\u044F"; +const without_card$8 = "\u0411\u0435\u0437 \u0440\u0430\u043C\u043A\u0438"; +const Cameras$8 = "\u041A\u0430\u043C\u0435\u0440\u044B"; +const Camera$8 = "\u041A\u0430\u043C\u0435\u0440\u0430"; +const Close$8 = "\u0417\u0430\u043A\u0440\u044B\u0432\u0430\u0442\u044C"; +const videoWidth$8 = "\u0428\u0438\u0440\u0438\u043D\u0430 \u0432\u0438\u0434\u0435\u043E"; +const tooltip_videoWidth$8 = "\u041D\u0435 \u0443\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0439\u0442\u0435 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u0443\u044E \u0448\u0438\u0440\u0438\u043D\u0443 \u0432\u0438\u0434\u0435\u043E, \u0447\u0442\u043E\u0431\u044B \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0448\u0438\u0440\u043E\u043A\u043E\u043F\u043E\u043B\u043E\u0441\u043D\u044B\u0439 \u0434\u043E\u0441\u0442\u0443\u043F. \u041E\u043D \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 \u043A\u0430\u043A \u0434\u043B\u044F \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0445, \u0442\u0430\u043A \u0438 \u0434\u043B\u044F \u043F\u043E\u043B\u043D\u044B\u0445 \u0432\u0438\u0434\u0435\u043E. \u0415\u0441\u043B\u0438 \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E, \u0431\u0443\u0434\u0435\u0442 \u0432\u0437\u044F\u0442 \u043D\u0430\u0447\u0430\u043B\u044C\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 \u0432\u0438\u0434\u0436\u0435\u0442\u0430."; +const disabled$8 = "\u043D\u0435\u043F\u043E\u043B\u043D\u043E\u0446\u0435\u043D\u043D\u044B\u0439"; +const pollingInterval$8 = "\u0418\u043D\u0442\u0435\u0440\u0432\u0430\u043B \u043E\u043F\u0440\u043E\u0441\u0430"; +const pollingIntervalFull$8 = "\u0418\u043D\u0442\u0435\u0440\u0432\u0430\u043B \u043E\u043F\u0440\u043E\u0441\u0430 \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u0435"; +const tooltip_ms$8 = "\u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u044B"; +const rotate$8 = "\u041F\u043E\u0432\u043E\u0440\u043E\u0442 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F"; +const noCacheByFull$8 = "\u041D\u0435\u0442 \u043A\u0435\u0448\u0430 \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u0435"; +const camera_in_dialog$8 = "\u041A\u0430\u043C\u0435\u0440\u0430 \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u0435"; +const ru = { + "RTSP url": "RTSP-\u0430\u0434\u0440\u0435\u0441", + "RTSP Camera": "RTSP-\u043A\u0430\u043C\u0435\u0440\u0430", + cameras_set_label: cameras_set_label$8, + name: name$8, + without_card: without_card$8, + Cameras: Cameras$8, + Camera: Camera$8, + Close: Close$8, + videoWidth: videoWidth$8, + tooltip_videoWidth: tooltip_videoWidth$8, + "Camera instance %s inactive": "\u042D\u043A\u0437\u0435\u043C\u043F\u043B\u044F\u0440 \u043A\u0430\u043C\u0435\u0440\u044B %s \u043D\u0435\u0430\u043A\u0442\u0438\u0432\u0435\u043D", + disabled: disabled$8, + pollingInterval: pollingInterval$8, + pollingIntervalFull: pollingIntervalFull$8, + tooltip_ms: tooltip_ms$8, + rotate: rotate$8, + "Polling Camera": "URL-\u041A\u0430\u043C\u0435\u0440\u0430", + "Cannot load URL": "\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C URL", + noCacheByFull: noCacheByFull$8, + camera_in_dialog: camera_in_dialog$8, + "No camera selected": "\u041A\u0430\u043C\u0435\u0440\u0430 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u0430" +}; +const cameras_set_label$7 = "C\xE2mera RTSP"; +const name$7 = "Nome"; +const without_card$7 = "sem moldura"; +const Cameras$7 = "C\xE2meras"; +const Camera$7 = "C\xE2mera"; +const Close$7 = "Fechar"; +const videoWidth$7 = "Largura do v\xEDdeo"; +const tooltip_videoWidth$7 = "N\xE3o defina a largura do v\xEDdeo muito alta para economizar banda larga. \xC9 para ambos: v\xEDdeos pequenos e completos. Se n\xE3o for definido, o tamanho inicial do widget ser\xE1 usado."; +const disabled$7 = "desabilitado"; +const pollingInterval$7 = "Intervalo de vota\xE7\xE3o"; +const pollingIntervalFull$7 = "Intervalo de pesquisa na caixa de di\xE1logo"; +const tooltip_ms$7 = "milissegundos"; +const rotate$7 = "Girar imagem"; +const noCacheByFull$7 = "Sem cache na caixa de di\xE1logo"; +const camera_in_dialog$7 = "C\xE2mera na caixa de di\xE1logo"; +const pt = { + "RTSP url": "url RTSP", + "RTSP Camera": "C\xE2mera RTSP", + cameras_set_label: cameras_set_label$7, + name: name$7, + without_card: without_card$7, + Cameras: Cameras$7, + Camera: Camera$7, + Close: Close$7, + videoWidth: videoWidth$7, + tooltip_videoWidth: tooltip_videoWidth$7, + "Camera instance %s inactive": "Inst\xE2ncia da c\xE2mera %s inativa", + disabled: disabled$7, + pollingInterval: pollingInterval$7, + pollingIntervalFull: pollingIntervalFull$7, + tooltip_ms: tooltip_ms$7, + rotate: rotate$7, + "Polling Camera": "C\xE2mera de vota\xE7\xE3o", + "Cannot load URL": "N\xE3o \xE9 poss\xEDvel carregar o URL", + noCacheByFull: noCacheByFull$7, + camera_in_dialog: camera_in_dialog$7, + "No camera selected": "Nenhuma c\xE2mera selecionada" +}; +const cameras_set_label$6 = "RTSP-camera"; +const name$6 = "Naam"; +const without_card$6 = "Zonder lijst"; +const Cameras$6 = "Camera's"; +const Camera$6 = "Camera"; +const Close$6 = "Dichtbij"; +const videoWidth$6 = "Videobreedte"; +const tooltip_videoWidth$6 = "Stel de breedte van de video niet te hoog in om breedband te besparen. Het is voor beide: kleine en volledige video's. Als dit niet is ingesteld, wordt de oorspronkelijke grootte van de widget gebruikt."; +const disabled$6 = "gehandicapt"; +const pollingInterval$6 = "Polling-interval"; +const pollingIntervalFull$6 = "Polling-interval in dialoogvenster"; +const tooltip_ms$6 = "milliseconden"; +const rotate$6 = "Afbeelding roteren"; +const noCacheByFull$6 = "Geen cache in dialoogvenster"; +const camera_in_dialog$6 = "Camera in dialoog"; +const nl = { + "RTSP url": "RTSP-URL", + "RTSP Camera": "RTSP-camera", + cameras_set_label: cameras_set_label$6, + name: name$6, + without_card: without_card$6, + Cameras: Cameras$6, + Camera: Camera$6, + Close: Close$6, + videoWidth: videoWidth$6, + tooltip_videoWidth: tooltip_videoWidth$6, + "Camera instance %s inactive": "Camera-instantie %s inactief", + disabled: disabled$6, + pollingInterval: pollingInterval$6, + pollingIntervalFull: pollingIntervalFull$6, + tooltip_ms: tooltip_ms$6, + rotate: rotate$6, + "Polling Camera": "Pollingcamera", + "Cannot load URL": "Kan URL niet laden", + noCacheByFull: noCacheByFull$6, + camera_in_dialog: camera_in_dialog$6, + "No camera selected": "Geen camera geselecteerd" +}; +const cameras_set_label$5 = "Cam\xE9ra RTSP"; +const name$5 = "Nom"; +const without_card$5 = "Sans cadre"; +const Cameras$5 = "Appareils photo"; +const Camera$5 = "Cam\xE9ra"; +const Close$5 = "Fermer"; +const videoWidth$5 = "Largeur vid\xE9o"; +const tooltip_videoWidth$5 = "Ne d\xE9finissez pas une largeur de vid\xE9o trop \xE9lev\xE9e pour \xE9conomiser le haut d\xE9bit. C'est pour les deux\xA0: petites et compl\xE8tes vid\xE9os. S'il n'est pas d\xE9fini, la taille initiale du widget sera prise."; +const disabled$5 = "d\xE9sactiv\xE9"; +const pollingInterval$5 = "Intervalle d'interrogation"; +const pollingIntervalFull$5 = "Intervalle d'interrogation dans la bo\xEEte de dialogue"; +const tooltip_ms$5 = "millisecondes"; +const rotate$5 = "Faire pivoter l'image"; +const noCacheByFull$5 = "Pas de cache dans la bo\xEEte de dialogue"; +const camera_in_dialog$5 = "Cam\xE9ra dans la bo\xEEte de dialogue"; +const fr = { + "RTSP url": "URL RTSP", + "RTSP Camera": "Cam\xE9ra RTSP", + cameras_set_label: cameras_set_label$5, + name: name$5, + without_card: without_card$5, + Cameras: Cameras$5, + Camera: Camera$5, + Close: Close$5, + videoWidth: videoWidth$5, + tooltip_videoWidth: tooltip_videoWidth$5, + "Camera instance %s inactive": "Instance de cam\xE9ra %s inactive", + disabled: disabled$5, + pollingInterval: pollingInterval$5, + pollingIntervalFull: pollingIntervalFull$5, + tooltip_ms: tooltip_ms$5, + rotate: rotate$5, + "Polling Camera": "Cam\xE9ra de sondage", + "Cannot load URL": "Impossible de charger l'URL", + noCacheByFull: noCacheByFull$5, + camera_in_dialog: camera_in_dialog$5, + "No camera selected": "Aucune cam\xE9ra s\xE9lectionn\xE9e" +}; +const cameras_set_label$4 = "Fotocamera RTSP"; +const name$4 = "Nome"; +const without_card$4 = "Senza cornice"; +const Cameras$4 = "Macchine fotografiche"; +const Camera$4 = "Telecamera"; +const Close$4 = "Vicino"; +const videoWidth$4 = "Larghezza video"; +const tooltip_videoWidth$4 = "Non impostare la larghezza del video troppo alta per salvare la banda larga. \xC8 per entrambi: video piccoli e completi. Se non impostato, verr\xE0 utilizzata la dimensione iniziale del widget."; +const disabled$4 = "Disabilitato"; +const pollingInterval$4 = "Intervallo di polling"; +const pollingIntervalFull$4 = "Intervallo di polling nella finestra di dialogo"; +const tooltip_ms$4 = "millisecondi"; +const rotate$4 = "Ruota l'immagine"; +const noCacheByFull$4 = "Nessuna cache nella finestra di dialogo"; +const camera_in_dialog$4 = "Fotocamera nella finestra di dialogo"; +const it = { + "RTSP url": "URL RTSP", + "RTSP Camera": "Fotocamera RTSP", + cameras_set_label: cameras_set_label$4, + name: name$4, + without_card: without_card$4, + Cameras: Cameras$4, + Camera: Camera$4, + Close: Close$4, + videoWidth: videoWidth$4, + tooltip_videoWidth: tooltip_videoWidth$4, + "Camera instance %s inactive": "Istanza fotocamera %s inattiva", + disabled: disabled$4, + pollingInterval: pollingInterval$4, + pollingIntervalFull: pollingIntervalFull$4, + tooltip_ms: tooltip_ms$4, + rotate: rotate$4, + "Polling Camera": "Telecamera per sondaggi", + "Cannot load URL": "Impossibile caricare l'URL", + noCacheByFull: noCacheByFull$4, + camera_in_dialog: camera_in_dialog$4, + "No camera selected": "Nessuna telecamera selezionata" +}; +const cameras_set_label$3 = "C\xE1mara RTSP"; +const name$3 = "Nombre"; +const without_card$3 = "sin marco"; +const Cameras$3 = "C\xE1maras"; +const Camera$3 = "C\xE1mara"; +const Close$3 = "Cerca"; +const videoWidth$3 = "Ancho de v\xEDdeo"; +const tooltip_videoWidth$3 = "No configure el ancho del video demasiado alto para ahorrar banda ancha. Es para ambos: videos peque\xF1os y completos. Si no se establece, se tomar\xE1 el tama\xF1o inicial del widget."; +const disabled$3 = "desactivado"; +const pollingInterval$3 = "Intervalo de votaci\xF3n"; +const pollingIntervalFull$3 = "Intervalo de sondeo en el di\xE1logo"; +const tooltip_ms$3 = "milisegundos"; +const rotate$3 = "Girar imagen"; +const noCacheByFull$3 = "No hay cach\xE9 en el di\xE1logo"; +const camera_in_dialog$3 = "C\xE1mara en di\xE1logo"; +const es = { + "RTSP url": "URL de RTSP", + "RTSP Camera": "C\xE1mara RTSP", + cameras_set_label: cameras_set_label$3, + name: name$3, + without_card: without_card$3, + Cameras: Cameras$3, + Camera: Camera$3, + Close: Close$3, + videoWidth: videoWidth$3, + tooltip_videoWidth: tooltip_videoWidth$3, + "Camera instance %s inactive": "Instancia de c\xE1mara %s inactiva", + disabled: disabled$3, + pollingInterval: pollingInterval$3, + pollingIntervalFull: pollingIntervalFull$3, + tooltip_ms: tooltip_ms$3, + rotate: rotate$3, + "Polling Camera": "C\xE1mara de sondeo", + "Cannot load URL": "No se puede cargar la URL", + noCacheByFull: noCacheByFull$3, + camera_in_dialog: camera_in_dialog$3, + "No camera selected": "Ninguna c\xE1mara seleccionada" +}; +const cameras_set_label$2 = "Kamera RTSP"; +const name$2 = "Nazwa"; +const without_card$2 = "Bez ramy"; +const Cameras$2 = "kamery"; +const Camera$2 = "Kamera"; +const Close$2 = "Zamkn\u0105\u0107"; +const videoWidth$2 = "Szeroko\u015B\u0107 wideo"; +const tooltip_videoWidth$2 = "Nie ustawiaj zbyt du\u017Cej szeroko\u015Bci wideo, aby zaoszcz\u0119dzi\u0107 \u0142\u0105cze szerokopasmowe. Dotyczy to zar\xF3wno ma\u0142ych, jak i pe\u0142nych film\xF3w. Je\u015Bli nie ustawiono, zostanie wzi\u0119ty pocz\u0105tkowy rozmiar wid\u017Cetu."; +const disabled$2 = "wy\u0142\u0105czony"; +const pollingInterval$2 = "Interwa\u0142 odpytywania"; +const pollingIntervalFull$2 = "Interwa\u0142 odpytywania w oknie dialogowym"; +const tooltip_ms$2 = "milisekundy"; +const rotate$2 = "Obr\xF3\u0107 obraz"; +const noCacheByFull$2 = "Brak pami\u0119ci podr\u0119cznej w oknie dialogowym"; +const camera_in_dialog$2 = "Kamera w oknie dialogowym"; +const pl = { + "RTSP url": "URL RTSP", + "RTSP Camera": "Kamera RTSP", + cameras_set_label: cameras_set_label$2, + name: name$2, + without_card: without_card$2, + Cameras: Cameras$2, + Camera: Camera$2, + Close: Close$2, + videoWidth: videoWidth$2, + tooltip_videoWidth: tooltip_videoWidth$2, + "Camera instance %s inactive": "Instancja kamery %s nieaktywna", + disabled: disabled$2, + pollingInterval: pollingInterval$2, + pollingIntervalFull: pollingIntervalFull$2, + tooltip_ms: tooltip_ms$2, + rotate: rotate$2, + "Polling Camera": "Kamera wyborcza", + "Cannot load URL": "Nie mo\u017Cna za\u0142adowa\u0107 adresu URL", + noCacheByFull: noCacheByFull$2, + camera_in_dialog: camera_in_dialog$2, + "No camera selected": "Nie wybrano kamery" +}; +const cameras_set_label$1 = "\u041A\u0430\u043C\u0435\u0440\u0430 RTSP"; +const name$1 = "\u0406\u043C'\u044F"; +const without_card$1 = "\u0411\u0435\u0437 \u0440\u0430\u043C\u0438"; +const Cameras$1 = "\u0424\u043E\u0442\u043E\u0430\u043F\u0430\u0440\u0430\u0442\u0438"; +const Camera$1 = "\u041A\u0430\u043C\u0435\u0440\u0430"; +const Close$1 = "\u0417\u0430\u043A\u0440\u0438\u0442\u0438"; +const videoWidth$1 = "\u0428\u0438\u0440\u0438\u043D\u0430 \u0432\u0456\u0434\u0435\u043E"; +const tooltip_videoWidth$1 = "\u041D\u0435 \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u044E\u0439\u0442\u0435 \u0437\u0430\u043D\u0430\u0434\u0442\u043E \u0432\u0438\u0441\u043E\u043A\u0443 \u0448\u0438\u0440\u0438\u043D\u0443 \u0432\u0456\u0434\u0435\u043E, \u0449\u043E\u0431 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0448\u0438\u0440\u043E\u043A\u043E\u0441\u043C\u0443\u0433\u043E\u0432\u0438\u0439 \u0437\u0432\u2019\u044F\u0437\u043E\u043A. \u0426\u0435 \u044F\u043A \u0434\u043B\u044F \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0445, \u0442\u0430\u043A \u0456 \u0434\u043B\u044F \u043F\u043E\u0432\u043D\u0438\u0445 \u0432\u0456\u0434\u0435\u043E. \u042F\u043A\u0449\u043E \u043D\u0435 \u0432\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E, \u0431\u0443\u0434\u0435 \u0432\u0437\u044F\u0442\u043E \u043F\u043E\u0447\u0430\u0442\u043A\u043E\u0432\u0438\u0439 \u0440\u043E\u0437\u043C\u0456\u0440 \u0432\u0456\u0434\u0436\u0435\u0442\u0430."; +const disabled$1 = "\u0432\u0438\u043C\u043A\u043D\u0435\u043D\u043E"; +const pollingInterval$1 = "\u0406\u043D\u0442\u0435\u0440\u0432\u0430\u043B \u043E\u043F\u0438\u0442\u0443\u0432\u0430\u043D\u043D\u044F"; +const pollingIntervalFull$1 = "\u0406\u043D\u0442\u0435\u0440\u0432\u0430\u043B \u043E\u043F\u0438\u0442\u0443\u0432\u0430\u043D\u043D\u044F \u0432 \u0434\u0456\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C\u0443 \u0432\u0456\u043A\u043D\u0456"; +const tooltip_ms$1 = "\u043C\u0456\u043B\u0456\u0441\u0435\u043A\u0443\u043D\u0434"; +const rotate$1 = "\u041F\u043E\u0432\u0435\u0440\u043D\u0443\u0442\u0438 \u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u043D\u044F"; +const noCacheByFull$1 = "\u0423 \u0434\u0456\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C\u0443 \u0432\u0456\u043A\u043D\u0456 \u043D\u0435\u043C\u0430\u0454 \u043A\u0435\u0448\u0443"; +const camera_in_dialog$1 = "\u041A\u0430\u043C\u0435\u0440\u0430 \u0432 \u0434\u0456\u0430\u043B\u043E\u0437\u0456"; +const uk = { + "RTSP url": "URL-\u0430\u0434\u0440\u0435\u0441\u0430 RTSP", + "RTSP Camera": "\u041A\u0430\u043C\u0435\u0440\u0430 RTSP", + cameras_set_label: cameras_set_label$1, + name: name$1, + without_card: without_card$1, + Cameras: Cameras$1, + Camera: Camera$1, + Close: Close$1, + videoWidth: videoWidth$1, + tooltip_videoWidth: tooltip_videoWidth$1, + "Camera instance %s inactive": "\u041F\u0440\u0438\u043C\u0456\u0440\u043D\u0438\u043A \u043A\u0430\u043C\u0435\u0440\u0438 %s \u043D\u0435\u0430\u043A\u0442\u0438\u0432\u043D\u0438\u0439", + disabled: disabled$1, + pollingInterval: pollingInterval$1, + pollingIntervalFull: pollingIntervalFull$1, + tooltip_ms: tooltip_ms$1, + rotate: rotate$1, + "Polling Camera": "\u041A\u0430\u043C\u0435\u0440\u0430 \u043E\u043F\u0438\u0442\u0443\u0432\u0430\u043D\u043D\u044F", + "Cannot load URL": "\u041D\u0435 \u0432\u0434\u0430\u0454\u0442\u044C\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 URL", + noCacheByFull: noCacheByFull$1, + camera_in_dialog: camera_in_dialog$1, + "No camera selected": "\u041A\u0430\u043C\u0435\u0440\u0430 \u043D\u0435 \u0432\u0438\u0431\u0440\u0430\u043D\u0430" +}; +const cameras_set_label = "RTSP\u6444\u50CF\u5934"; +const name = "\u59D3\u540D"; +const without_card = "\u65E0\u6846"; +const Cameras = "\u76F8\u673A"; +const Camera = "\u76F8\u673A"; +const Close = "\u5173\u95ED"; +const videoWidth = "\u89C6\u9891\u5BBD\u5EA6"; +const tooltip_videoWidth = "\u4E0D\u8981\u5C06\u89C6\u9891\u5BBD\u5EA6\u8BBE\u7F6E\u5F97\u592A\u9AD8\uFF0C\u4EE5\u8282\u7701\u5BBD\u5E26\u3002\u5B83\u9002\u7528\u4E8E\uFF1A\u5C0F\u89C6\u9891\u548C\u5B8C\u6574\u89C6\u9891\u3002\u5982\u679C\u672A\u8BBE\u7F6E\uFF0C\u5219\u5C06\u91C7\u7528\u5C0F\u90E8\u4EF6\u7684\u521D\u59CB\u5927\u5C0F\u3002"; +const disabled = "\u6B8B\u75BE\u4EBA"; +const pollingInterval = "\u8F6E\u8BE2\u95F4\u9694"; +const pollingIntervalFull = "\u5BF9\u8BDD\u6846\u4E2D\u7684\u8F6E\u8BE2\u95F4\u9694"; +const tooltip_ms = "\u6BEB\u79D2"; +const rotate = "\u65CB\u8F6C\u56FE\u50CF"; +const noCacheByFull = "\u5BF9\u8BDD\u6846\u4E2D\u6CA1\u6709\u7F13\u5B58"; +const camera_in_dialog = "\u76F8\u673A\u5BF9\u8BDD"; +const zhcn = { + "RTSP url": "RTSP \u7F51\u5740", + "RTSP Camera": "RTSP\u6444\u50CF\u5934", + cameras_set_label, + name, + without_card, + Cameras, + Camera, + Close, + videoWidth, + tooltip_videoWidth, + "Camera instance %s inactive": "\u76F8\u673A\u5B9E\u4F8B %s \u4E0D\u6D3B\u52A8", + disabled, + pollingInterval, + pollingIntervalFull, + tooltip_ms, + rotate, + "Polling Camera": "\u6295\u7968\u6444\u50CF\u5934", + "Cannot load URL": "\u65E0\u6CD5\u52A0\u8F7D\u7F51\u5740", + noCacheByFull, + camera_in_dialog, + "No camera selected": "\u672A\u9009\u62E9\u76F8\u673A" +}; +const translations = { + en, + de, + ru, + pt, + nl, + fr, + it, + es, + pl, + "zh-cn": zhcn, + uk, + prefix: "cameras_" +}; +export { + translations as default +}; diff --git a/widgets/cameras/customWidgets.js b/widgets/cameras/customWidgets.js new file mode 100644 index 0000000..a1e7dd9 --- /dev/null +++ b/widgets/cameras/customWidgets.js @@ -0,0 +1,93 @@ +var __async = (__this, __arguments, generator) => { + return new Promise((resolve, reject) => { + var fulfilled = (value) => { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + }; + var rejected = (value) => { + try { + step(generator.throw(value)); + } catch (e) { + reject(e); + } + }; + var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); + step((generator = generator.apply(__this, __arguments)).next()); + }); +}; +const import_meta = {}; +let dynamicLoadingCss, get, init; +var vis2CameraWidgets = (() => __async(void 0, null, function* () { + const currentImports = {}; + const exportSet = /* @__PURE__ */ new Set([ + "Module", + "__esModule", + "default", + "_export_sfc" + ]); + let moduleMap = { + "./RtspCamera": () => { + dynamicLoadingCss([], false, "./RtspCamera"); + return __federation_import("./__federation_expose_RtspCamera-DEDIMDIg.js").then((module) => Object.keys(module).every((item) => exportSet.has(item)) ? () => module.default : () => module); + }, + "./SnapshotCamera": () => { + dynamicLoadingCss([], false, "./SnapshotCamera"); + return __federation_import("./__federation_expose_SnapshotCamera-BDbQmboE.js").then((module) => Object.keys(module).every((item) => exportSet.has(item)) ? () => module.default : () => module); + }, + "./translations": () => { + dynamicLoadingCss([], false, "./translations"); + return __federation_import("./__federation_expose_Translations-HGakruMK.js").then((module) => Object.keys(module).every((item) => exportSet.has(item)) ? () => module.default : () => module); + } + }; + const seen = {}; + dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => { + const metaUrl = import_meta.url; + if (typeof metaUrl == "undefined") { + console.warn('The remote style takes effect only when the build.target option in the vite.config.ts file is higher than that of "es2020".'); + return; + } + const curUrl = metaUrl.substring(0, metaUrl.lastIndexOf("customWidgets.js")); + cssFilePaths.forEach((cssFilePath) => { + const href = curUrl + cssFilePath; + if (href in seen) return; + seen[href] = true; + if (dontAppendStylesToHead) { + const key = "css__vis2CameraWidgets__" + exposeItemName; + if (window[key] == null) window[key] = []; + window[key].push(href); + } else { + const element = document.head.appendChild(document.createElement("link")); + element.href = href; + element.rel = "stylesheet"; + } + }); + }; + function __federation_import(name) { + return __async(this, null, function* () { + var _a; + (_a = currentImports[name]) != null ? _a : currentImports[name] = import(name).then((m) => __async(this, null, function* () { + yield m.__tla; + return m; + })); + return currentImports[name]; + }); + } + get = (module) => { + if (!moduleMap[module]) throw new Error("Can not find remote module " + module); + return moduleMap[module](); + }; + init = (shareScope) => { + globalThis.__federation_shared__ = globalThis.__federation_shared__ || {}; + Object.entries(shareScope).forEach(([key, value]) => { + const versionKey = Object.keys(value)[0]; + const versionValue = Object.values(value)[0]; + const scope = versionValue.scope || "default"; + globalThis.__federation_shared__[scope] = globalThis.__federation_shared__[scope] || {}; + const shared = globalThis.__federation_shared__[scope]; + (shared[key] = shared[key] || {})[versionKey] = versionValue; + }); + }; +}))();