From d0b12e92e7e6b666ad0d60ca0da9c6fb16888b73 Mon Sep 17 00:00:00 2001 From: Moe Date: Thu, 7 Mar 2019 14:43:37 -0800 Subject: [PATCH] Son Goku - Rebased sql, test, web, defintions, languages, INSTALL, and libs folders. --- INSTALL/centos.sh | 31 +- INSTALL/openalpr-cpu-easy.sh | 41 + INSTALL/openalpr-gpu-easy.sh | 15 +- INSTALL/opencv-cuda.sh | 4 +- INSTALL/opensuse.sh | 48 +- INSTALL/shinobi | 114 +- INSTALL/start.sh | 1 + INSTALL/terminalCommands.js | 41 + INSTALL/ubuntu.sh | 42 +- camera.js | 20 +- conf.sample.json | 1 + cron.js | 2 +- definitions/en_CA.json | 20 +- languages/cz.json | 857 ++++ languages/en_CA.json | 78 +- languages/fr.json | 386 +- libs/.gitignore | 1 + libs/auth.js | 13 +- libs/basic.js | 34 +- libs/cloudUploaders.js | 563 ++- libs/codeTester.js | 67 + libs/config.js | 11 +- libs/customAutoLoad.js | 134 + libs/detector.js | 288 +- libs/dropInEvents.js | 208 + libs/events.js | 130 +- libs/extenders.js | 88 + libs/ffmpeg.js | 108 +- libs/ffmpegCoProcessor.js | 210 + libs/language.js | 24 +- libs/monitor.js | 311 +- libs/notification.js | 40 +- libs/plugins.js | 92 +- libs/process.js | 3 + libs/rtmpserver.js | 23 + libs/rtmpserver/node_core_amf.js | 1184 ++++++ libs/rtmpserver/node_core_av.js | 501 +++ libs/rtmpserver/node_core_bitop.js | 54 + libs/rtmpserver/node_core_ctx.js | 17 + libs/rtmpserver/node_core_logger.js | 53 + libs/rtmpserver/node_core_utils.js | 95 + libs/rtmpserver/node_rtmp_handshake.js | 113 + libs/rtmpserver/node_rtmp_server.js | 50 + libs/rtmpserver/node_rtmp_session.js | 1248 ++++++ libs/scheduler.js | 263 ++ libs/socketio.js | 55 +- libs/sql.js | 70 +- libs/startup.js | 39 +- libs/uploaders.js | 11 + libs/uploaders/amazonS3.js | 137 + libs/uploaders/backblazeB2.js | 170 + libs/uploaders/loader.js | 20 + libs/uploaders/sftp.js | 90 + libs/uploaders/wasabi.js | 138 + libs/uploaders/webdav.js | 169 + libs/user.js | 58 +- libs/videos.js | 138 +- libs/webServer.js | 8 + libs/webServerAdminPaths.js | 81 +- libs/webServerPaths.js | 340 +- libs/webServerStreamPaths.js | 7 - libs/webServerSuperPaths.js | 262 +- plugins/python-contour/.gitignore | 3 + plugins/python-contour/INSTALL.sh | 59 + plugins/python-contour/README.md | 72 + plugins/python-contour/bootPy.sh | 1 + plugins/python-contour/conf.sample.json | 10 + plugins/python-contour/package.json | 18 + plugins/python-contour/pumpkin.py | 116 + .../python-contour/shinobi-python-contour.js | 298 ++ sql/docker/01-framework.sql | 113 - sql/framework.sql | 40 +- test/run.js | 606 ++- test/testAdminUser.json | 7 - test/testApiAdd.json | 15 - test/testApiDelete.json | 5 - web/libs/css/dash2.basic.css | 89 + web/libs/css/dash2.forms.css | 44 + web/libs/css/dash2.modal.css | 6 + web/libs/css/dash2.monitors.css | 86 + web/libs/css/dash2.powervideo.css | 8 + web/libs/css/dash2.ptzcontrols.css | 89 + web/libs/css/dash2.regioneditor.css | 5 + web/libs/css/dash2.righttoleft.css | 11 + web/libs/css/dash2.timelapse.css | 13 + web/libs/css/main.dash2.css | 370 +- web/libs/css/main.dash2.old.css | 742 ++++ .../css/material-design-iconic-font.min.css | 1 + web/libs/css/material.style.css | 9 - web/libs/css/montserrat.css | 48 + web/libs/css/poseidon.css | 246 -- web/libs/css/roboto.css | 106 + web/libs/css/roboto.css.map | 7 + web/libs/css/roboto.less | 15 + web/libs/css/roboto.scss | 15 + web/libs/fonts/Black/Roboto-Black.ttf | Bin 0 -> 171480 bytes web/libs/fonts/Black/Roboto-Black.woff | Bin 0 -> 90664 bytes web/libs/fonts/Black/Roboto-Black.woff2 | Bin 0 -> 64960 bytes .../fonts/BlackItalic/Roboto-BlackItalic.ttf | Bin 0 -> 177552 bytes .../fonts/BlackItalic/Roboto-BlackItalic.woff | Bin 0 -> 98296 bytes .../BlackItalic/Roboto-BlackItalic.woff2 | Bin 0 -> 72088 bytes web/libs/fonts/Bold/Roboto-Bold.ttf | Bin 0 -> 170760 bytes web/libs/fonts/Bold/Roboto-Bold.woff | Bin 0 -> 90308 bytes web/libs/fonts/Bold/Roboto-Bold.woff2 | Bin 0 -> 64740 bytes .../fonts/BoldItalic/Roboto-BoldItalic.ttf | Bin 0 -> 174952 bytes .../fonts/BoldItalic/Roboto-BoldItalic.woff | Bin 0 -> 96244 bytes .../fonts/BoldItalic/Roboto-BoldItalic.woff2 | Bin 0 -> 70360 bytes web/libs/fonts/COPYRIGHT.txt | 1 + web/libs/fonts/Italic/Roboto-Italic.ttf | Bin 0 -> 173932 bytes web/libs/fonts/Italic/Roboto-Italic.woff | Bin 0 -> 95740 bytes web/libs/fonts/Italic/Roboto-Italic.woff2 | Bin 0 -> 70280 bytes web/libs/fonts/LICENSE.txt | 202 + web/libs/fonts/Light/Roboto-Light.ttf | Bin 0 -> 170420 bytes web/libs/fonts/Light/Roboto-Light.woff | Bin 0 -> 89672 bytes web/libs/fonts/Light/Roboto-Light.woff2 | Bin 0 -> 64320 bytes .../fonts/LightItalic/Roboto-LightItalic.ttf | Bin 0 -> 176616 bytes .../fonts/LightItalic/Roboto-LightItalic.woff | Bin 0 -> 96952 bytes .../LightItalic/Roboto-LightItalic.woff2 | Bin 0 -> 70760 bytes web/libs/fonts/METADATA.pb | 121 + .../fonts/Material-Design-Iconic-Font.eot | Bin 0 -> 42495 bytes .../fonts/Material-Design-Iconic-Font.svg | 787 ++++ .../fonts/Material-Design-Iconic-Font.ttf | Bin 0 -> 99212 bytes .../fonts/Material-Design-Iconic-Font.woff | Bin 0 -> 50312 bytes .../fonts/Material-Design-Iconic-Font.woff2 | Bin 0 -> 38384 bytes web/libs/fonts/Medium/Roboto-Medium.ttf | Bin 0 -> 172064 bytes web/libs/fonts/Medium/Roboto-Medium.woff | Bin 0 -> 90560 bytes web/libs/fonts/Medium/Roboto-Medium.woff2 | Bin 0 -> 65484 bytes .../MediumItalic/Roboto-MediumItalic.ttf | Bin 0 -> 176864 bytes .../MediumItalic/Roboto-MediumItalic.woff | Bin 0 -> 97476 bytes .../MediumItalic/Roboto-MediumItalic.woff2 | Bin 0 -> 71284 bytes web/libs/fonts/Regular/Roboto-Regular.ttf | Bin 0 -> 171676 bytes web/libs/fonts/Regular/Roboto-Regular.woff | Bin 0 -> 89828 bytes web/libs/fonts/Regular/Roboto-Regular.woff2 | Bin 0 -> 64632 bytes web/libs/fonts/Thin/Roboto-Thin.ttf | Bin 0 -> 171904 bytes web/libs/fonts/Thin/Roboto-Thin.woff | Bin 0 -> 88132 bytes web/libs/fonts/Thin/Roboto-Thin.woff2 | Bin 0 -> 63048 bytes .../fonts/ThinItalic/Roboto-ThinItalic.ttf | Bin 0 -> 176300 bytes .../fonts/ThinItalic/Roboto-ThinItalic.woff | Bin 0 -> 94808 bytes .../fonts/ThinItalic/Roboto-ThinItalic.woff2 | Bin 0 -> 68328 bytes web/libs/fonts/montserrat-bold.eot | Bin 0 -> 17346 bytes web/libs/fonts/montserrat-bold.svg | 706 ++++ web/libs/fonts/montserrat-bold.ttf | Bin 0 -> 42936 bytes web/libs/fonts/montserrat-bold.woff | Bin 0 -> 21740 bytes web/libs/fonts/montserrat-bold.woff2 | Bin 0 -> 15608 bytes web/libs/fonts/montserrat-light.eot | Bin 0 -> 17571 bytes web/libs/fonts/montserrat-light.svg | 725 ++++ web/libs/fonts/montserrat-light.ttf | Bin 0 -> 43500 bytes web/libs/fonts/montserrat-light.woff | Bin 0 -> 21916 bytes web/libs/fonts/montserrat-light.woff2 | Bin 0 -> 15764 bytes web/libs/fonts/montserrat-regular.eot | Bin 0 -> 17418 bytes web/libs/fonts/montserrat-regular.svg | 724 ++++ web/libs/fonts/montserrat-regular.ttf | Bin 0 -> 43536 bytes web/libs/fonts/montserrat-regular.woff | Bin 0 -> 21800 bytes web/libs/fonts/montserrat-regular.woff2 | Bin 0 -> 15556 bytes web/libs/js/dash2.apiwindow.js | 32 + web/libs/js/dash2.basic.js | 299 ++ web/libs/js/dash2.config.js | 134 + web/libs/js/dash2.confirm.js | 0 web/libs/js/dash2.cronfilter.js | 61 + web/libs/js/dash2.detectorfilter.js | 143 + web/libs/js/dash2.elementbuilder.js | 918 +++++ web/libs/js/dash2.elements.js | 801 ++++ web/libs/js/dash2.gridstack.js | 61 + web/libs/js/dash2.init.js | 612 +++ web/libs/js/dash2.logviewer.js | 66 + web/libs/js/dash2.monitoredit.js | 830 ++++ web/libs/js/dash2.multimon.js | 192 + web/libs/js/dash2.onvifscanner.js | 60 + web/libs/js/dash2.powervideo.js | 298 ++ web/libs/js/dash2.probe.js | 48 + web/libs/js/dash2.regioneditor.js | 200 + web/libs/js/dash2.socketio.js | 917 +++++ web/libs/js/dash2.timelapse.js | 334 ++ web/libs/js/dash2.usersettings.js | 101 + web/libs/js/dash2.vidview.js | 174 + web/libs/js/main.dash2.js | 35 +- web/libs/less/_Black.less | 8 + web/libs/less/_BlackItalic.less | 8 + web/libs/less/_Bold.less | 14 + web/libs/less/_BoldItalic.less | 14 + web/libs/less/_Italic.less | 14 + web/libs/less/_Light.less | 8 + web/libs/less/_LightItalic.less | 8 + web/libs/less/_Medium.less | 8 + web/libs/less/_MediumItalic.less | 8 + web/libs/less/_Regular.less | 14 + web/libs/less/_Thin.less | 8 + web/libs/less/_ThinItalic.less | 8 + web/libs/less/_mixins.less | 4 + web/libs/less/_variables.less | 3 + web/libs/sass/_Black.scss | 8 + web/libs/sass/_BlackItalic.scss | 8 + web/libs/sass/_Bold.scss | 14 + web/libs/sass/_BoldItalic.scss | 14 + web/libs/sass/_Italic.scss | 14 + web/libs/sass/_Light.scss | 8 + web/libs/sass/_LightItalic.scss | 8 + web/libs/sass/_Medium.scss | 8 + web/libs/sass/_MediumItalic.scss | 8 + web/libs/sass/_Regular.scss | 14 + web/libs/sass/_Thin.scss | 8 + web/libs/sass/_ThinItalic.scss | 8 + web/libs/sass/_mixins.scss | 4 + web/libs/sass/_variables.scss | 3 + web/libs/sass/roboto.scss | 15 + web/pages/admin.ejs | 12 + web/pages/blocks/api.ejs | 1 + web/pages/blocks/detectorfilters.ejs | 1 + web/pages/blocks/filters.ejs | 37 +- web/pages/blocks/header.ejs | 1 + web/pages/blocks/logs.ejs | 5 +- web/pages/blocks/mainpermissions.ejs | 53 +- web/pages/blocks/migrator.ejs | 81 - web/pages/blocks/monitorStates.ejs | 182 + web/pages/blocks/monitoredit.ejs | 116 +- web/pages/blocks/multimon.ejs | 3 +- web/pages/blocks/powervideo.ejs | 1 + web/pages/blocks/probe.ejs | 7 +- web/pages/blocks/region.ejs | 1 + web/pages/blocks/schedules.ejs | 179 + web/pages/blocks/settings.ejs | 404 +- web/pages/blocks/superBackupDatabase.ejs | 143 + web/pages/blocks/timelapse.ejs | 3 +- web/pages/blocks/videoview.ejs | 2 + web/pages/dashcam.ejs | 1 + web/pages/factor.ejs | 2 +- web/pages/home.ejs | 42 +- web/pages/robots.txt | 2 + web/pages/streamer.ejs | 1 + web/pages/super.ejs | 20 +- web/templates/boilerplate/README.md | 58 - .../boilerplate/assets/css/bootstrap.min.css | 7 - .../assets/css/now-ui-dashboard.css | 3570 ----------------- .../assets/css/now-ui-dashboard.css.map | 1 - .../assets/css/now-ui-dashboard.min.css | 24 - .../boilerplate/assets/css/styles.css | 51 - .../fonts/Montserrat/Montserrat-Black.eot | Bin 102451 -> 0 bytes .../fonts/Montserrat/Montserrat-Black.woff | Bin 107388 -> 0 bytes .../fonts/Montserrat/Montserrat-Black.woff2 | Bin 82528 -> 0 bytes .../Montserrat/Montserrat-BlackItalic.eot | Bin 107248 -> 0 bytes .../Montserrat/Montserrat-BlackItalic.woff | Bin 112068 -> 0 bytes .../Montserrat/Montserrat-BlackItalic.woff2 | Bin 85576 -> 0 bytes .../fonts/Montserrat/Montserrat-Bold.eot | Bin 104070 -> 0 bytes .../fonts/Montserrat/Montserrat-Bold.woff | Bin 108200 -> 0 bytes .../fonts/Montserrat/Montserrat-Bold.woff2 | Bin 83392 -> 0 bytes .../Montserrat/Montserrat-BoldItalic.eot | Bin 109016 -> 0 bytes .../Montserrat/Montserrat-BoldItalic.woff | Bin 113132 -> 0 bytes .../Montserrat/Montserrat-BoldItalic.woff2 | Bin 86440 -> 0 bytes .../fonts/Montserrat/Montserrat-ExtraBold.eot | Bin 103434 -> 0 bytes .../Montserrat/Montserrat-ExtraBold.woff | Bin 107912 -> 0 bytes .../Montserrat/Montserrat-ExtraBold.woff2 | Bin 82888 -> 0 bytes .../Montserrat/Montserrat-ExtraBoldItalic.eot | Bin 108428 -> 0 bytes .../Montserrat-ExtraBoldItalic.woff | Bin 112880 -> 0 bytes .../Montserrat-ExtraBoldItalic.woff2 | Bin 86192 -> 0 bytes .../Montserrat/Montserrat-ExtraLight.eot | Bin 102071 -> 0 bytes .../Montserrat/Montserrat-ExtraLight.woff | Bin 106308 -> 0 bytes .../Montserrat/Montserrat-ExtraLight.woff2 | Bin 81828 -> 0 bytes .../Montserrat-ExtraLightItalic.eot | Bin 106113 -> 0 bytes .../Montserrat-ExtraLightItalic.woff | Bin 110692 -> 0 bytes .../Montserrat-ExtraLightItalic.woff2 | Bin 84508 -> 0 bytes .../fonts/Montserrat/Montserrat-Italic.eot | Bin 107752 -> 0 bytes .../fonts/Montserrat/Montserrat-Italic.woff | Bin 112300 -> 0 bytes .../fonts/Montserrat/Montserrat-Italic.woff2 | Bin 86024 -> 0 bytes .../fonts/Montserrat/Montserrat-Light.eot | Bin 103001 -> 0 bytes .../fonts/Montserrat/Montserrat-Light.woff | Bin 106924 -> 0 bytes .../fonts/Montserrat/Montserrat-Light.woff2 | Bin 82104 -> 0 bytes .../Montserrat/Montserrat-LightItalic.eot | Bin 107149 -> 0 bytes .../Montserrat/Montserrat-LightItalic.woff | Bin 111468 -> 0 bytes .../Montserrat/Montserrat-LightItalic.woff2 | Bin 85172 -> 0 bytes .../fonts/Montserrat/Montserrat-Medium.eot | Bin 104101 -> 0 bytes .../fonts/Montserrat/Montserrat-Medium.woff | Bin 107872 -> 0 bytes .../fonts/Montserrat/Montserrat-Medium.woff2 | Bin 83080 -> 0 bytes .../Montserrat/Montserrat-MediumItalic.eot | Bin 108677 -> 0 bytes .../Montserrat/Montserrat-MediumItalic.woff | Bin 112476 -> 0 bytes .../Montserrat/Montserrat-MediumItalic.woff2 | Bin 86468 -> 0 bytes .../fonts/Montserrat/Montserrat-Regular.eot | Bin 104215 -> 0 bytes .../fonts/Montserrat/Montserrat-Regular.woff | Bin 108124 -> 0 bytes .../fonts/Montserrat/Montserrat-Regular.woff2 | Bin 83532 -> 0 bytes .../fonts/Montserrat/Montserrat-SemiBold.eot | Bin 103341 -> 0 bytes .../fonts/Montserrat/Montserrat-SemiBold.woff | Bin 107524 -> 0 bytes .../Montserrat/Montserrat-SemiBold.woff2 | Bin 82620 -> 0 bytes .../Montserrat/Montserrat-SemiBoldItalic.eot | Bin 108290 -> 0 bytes .../Montserrat/Montserrat-SemiBoldItalic.woff | Bin 112440 -> 0 bytes .../Montserrat-SemiBoldItalic.woff2 | Bin 85980 -> 0 bytes .../fonts/Montserrat/Montserrat-Thin.eot | Bin 97905 -> 0 bytes .../fonts/Montserrat/Montserrat-Thin.woff | Bin 102452 -> 0 bytes .../fonts/Montserrat/Montserrat-Thin.woff2 | Bin 78392 -> 0 bytes .../Montserrat/Montserrat-ThinItalic.eot | Bin 101456 -> 0 bytes .../Montserrat/Montserrat-ThinItalic.woff | Bin 106400 -> 0 bytes .../Montserrat/Montserrat-ThinItalic.woff2 | Bin 80832 -> 0 bytes .../assets/fonts/Montserrat/Montserrat.css | 399 -- .../Montserrat/MontserratAlternates-Black.eot | Bin 85264 -> 0 bytes .../MontserratAlternates-Black.woff | Bin 88280 -> 0 bytes .../MontserratAlternates-Black.woff2 | Bin 69140 -> 0 bytes .../MontserratAlternates-BlackItalic.eot | Bin 89463 -> 0 bytes .../MontserratAlternates-BlackItalic.woff | Bin 92568 -> 0 bytes .../MontserratAlternates-BlackItalic.woff2 | Bin 72368 -> 0 bytes .../Montserrat/MontserratAlternates-Bold.eot | Bin 87123 -> 0 bytes .../Montserrat/MontserratAlternates-Bold.woff | Bin 89816 -> 0 bytes .../MontserratAlternates-Bold.woff2 | Bin 70232 -> 0 bytes .../MontserratAlternates-BoldItalic.eot | Bin 90903 -> 0 bytes .../MontserratAlternates-BoldItalic.woff | Bin 93464 -> 0 bytes .../MontserratAlternates-BoldItalic.woff2 | Bin 73132 -> 0 bytes .../MontserratAlternates-ExtraBold.eot | Bin 87183 -> 0 bytes .../MontserratAlternates-ExtraBold.woff | Bin 89740 -> 0 bytes .../MontserratAlternates-ExtraBold.woff2 | Bin 70464 -> 0 bytes .../MontserratAlternates-ExtraBoldItalic.eot | Bin 90760 -> 0 bytes .../MontserratAlternates-ExtraBoldItalic.woff | Bin 93448 -> 0 bytes ...MontserratAlternates-ExtraBoldItalic.woff2 | Bin 72976 -> 0 bytes .../MontserratAlternates-ExtraLight.eot | Bin 85845 -> 0 bytes .../MontserratAlternates-ExtraLight.woff | Bin 88364 -> 0 bytes .../MontserratAlternates-ExtraLight.woff2 | Bin 69172 -> 0 bytes .../MontserratAlternates-ExtraLightItalic.eot | Bin 88968 -> 0 bytes ...MontserratAlternates-ExtraLightItalic.woff | Bin 91780 -> 0 bytes ...ontserratAlternates-ExtraLightItalic.woff2 | Bin 71648 -> 0 bytes .../MontserratAlternates-Italic.eot | Bin 90178 -> 0 bytes .../MontserratAlternates-Italic.woff | Bin 92968 -> 0 bytes .../MontserratAlternates-Italic.woff2 | Bin 72940 -> 0 bytes .../Montserrat/MontserratAlternates-Light.eot | Bin 86343 -> 0 bytes .../MontserratAlternates-Light.woff | Bin 88884 -> 0 bytes .../MontserratAlternates-Light.woff2 | Bin 69728 -> 0 bytes .../MontserratAlternates-LightItalic.eot | Bin 89428 -> 0 bytes .../MontserratAlternates-LightItalic.woff | Bin 92448 -> 0 bytes .../MontserratAlternates-LightItalic.woff2 | Bin 72068 -> 0 bytes .../MontserratAlternates-Medium.eot | Bin 87027 -> 0 bytes .../MontserratAlternates-Medium.woff | Bin 89664 -> 0 bytes .../MontserratAlternates-Medium.woff2 | Bin 70148 -> 0 bytes .../MontserratAlternates-MediumItalic.eot | Bin 90639 -> 0 bytes .../MontserratAlternates-MediumItalic.woff | Bin 93188 -> 0 bytes .../MontserratAlternates-MediumItalic.woff2 | Bin 73040 -> 0 bytes .../MontserratAlternates-Regular.eot | Bin 87088 -> 0 bytes .../MontserratAlternates-Regular.woff | Bin 89720 -> 0 bytes .../MontserratAlternates-Regular.woff2 | Bin 70316 -> 0 bytes .../MontserratAlternates-SemiBold.eot | Bin 86796 -> 0 bytes .../MontserratAlternates-SemiBold.woff | Bin 89264 -> 0 bytes .../MontserratAlternates-SemiBold.woff2 | Bin 69948 -> 0 bytes .../MontserratAlternates-SemiBoldItalic.eot | Bin 90411 -> 0 bytes .../MontserratAlternates-SemiBoldItalic.woff | Bin 92804 -> 0 bytes .../MontserratAlternates-SemiBoldItalic.woff2 | Bin 72488 -> 0 bytes .../Montserrat/MontserratAlternates-Thin.eot | Bin 82212 -> 0 bytes .../Montserrat/MontserratAlternates-Thin.woff | Bin 85048 -> 0 bytes .../MontserratAlternates-Thin.woff2 | Bin 66452 -> 0 bytes .../MontserratAlternates-ThinItalic.eot | Bin 84883 -> 0 bytes .../MontserratAlternates-ThinItalic.woff | Bin 88068 -> 0 bytes .../MontserratAlternates-ThinItalic.woff2 | Bin 68236 -> 0 bytes .../assets/fonts/nucleo-outline.eot | Bin 30528 -> 0 bytes .../assets/fonts/nucleo-outline.ttf | Bin 30352 -> 0 bytes .../assets/fonts/nucleo-outline.woff | Bin 18056 -> 0 bytes .../assets/fonts/nucleo-outline.woff2 | Bin 15364 -> 0 bytes .../boilerplate/assets/js/bootstrap-notify.js | 433 -- .../boilerplate/assets/js/bootstrap.min.js | 6 - .../boilerplate/assets/js/chartjs.min.js | 10 - web/templates/boilerplate/assets/js/main.js | 105 - .../boilerplate/assets/js/moment.min.js | 1 - .../assets/js/now-ui-dashboard.min.js | 1 - .../assets/js/perfect-scrollbar.jquery.min.js | 2 - .../boilerplate/assets/js/popper.min.js | 4 - web/templates/boilerplate/handler.js | 8 - .../boilerplate/views/blocks/footer.ejs | 23 - .../boilerplate/views/blocks/header.ejs | 48 - .../boilerplate/views/blocks/home.ejs | 132 - web/templates/boilerplate/views/index.ejs | 7 - 362 files changed, 21716 insertions(+), 7018 deletions(-) create mode 100644 INSTALL/openalpr-cpu-easy.sh create mode 100644 INSTALL/terminalCommands.js create mode 100644 languages/cz.json create mode 100644 libs/.gitignore create mode 100644 libs/codeTester.js create mode 100644 libs/customAutoLoad.js create mode 100644 libs/dropInEvents.js create mode 100644 libs/ffmpegCoProcessor.js create mode 100644 libs/rtmpserver.js create mode 100644 libs/rtmpserver/node_core_amf.js create mode 100644 libs/rtmpserver/node_core_av.js create mode 100644 libs/rtmpserver/node_core_bitop.js create mode 100644 libs/rtmpserver/node_core_ctx.js create mode 100644 libs/rtmpserver/node_core_logger.js create mode 100644 libs/rtmpserver/node_core_utils.js create mode 100644 libs/rtmpserver/node_rtmp_handshake.js create mode 100644 libs/rtmpserver/node_rtmp_server.js create mode 100644 libs/rtmpserver/node_rtmp_session.js create mode 100644 libs/scheduler.js create mode 100644 libs/uploaders.js create mode 100644 libs/uploaders/amazonS3.js create mode 100644 libs/uploaders/backblazeB2.js create mode 100644 libs/uploaders/loader.js create mode 100644 libs/uploaders/sftp.js create mode 100644 libs/uploaders/wasabi.js create mode 100644 libs/uploaders/webdav.js create mode 100644 plugins/python-contour/.gitignore create mode 100644 plugins/python-contour/INSTALL.sh create mode 100644 plugins/python-contour/README.md create mode 100644 plugins/python-contour/bootPy.sh create mode 100644 plugins/python-contour/conf.sample.json create mode 100644 plugins/python-contour/package.json create mode 100644 plugins/python-contour/pumpkin.py create mode 100644 plugins/python-contour/shinobi-python-contour.js delete mode 100644 sql/docker/01-framework.sql delete mode 100644 test/testAdminUser.json delete mode 100644 test/testApiAdd.json delete mode 100644 test/testApiDelete.json create mode 100644 web/libs/css/dash2.basic.css create mode 100644 web/libs/css/dash2.forms.css create mode 100644 web/libs/css/dash2.modal.css create mode 100644 web/libs/css/dash2.monitors.css create mode 100644 web/libs/css/dash2.powervideo.css create mode 100644 web/libs/css/dash2.ptzcontrols.css create mode 100644 web/libs/css/dash2.regioneditor.css create mode 100644 web/libs/css/dash2.righttoleft.css create mode 100644 web/libs/css/dash2.timelapse.css create mode 100644 web/libs/css/main.dash2.old.css create mode 100644 web/libs/css/material-design-iconic-font.min.css create mode 100644 web/libs/css/montserrat.css delete mode 100644 web/libs/css/poseidon.css create mode 100644 web/libs/css/roboto.css create mode 100644 web/libs/css/roboto.css.map create mode 100644 web/libs/css/roboto.less create mode 100644 web/libs/css/roboto.scss create mode 100644 web/libs/fonts/Black/Roboto-Black.ttf create mode 100644 web/libs/fonts/Black/Roboto-Black.woff create mode 100644 web/libs/fonts/Black/Roboto-Black.woff2 create mode 100644 web/libs/fonts/BlackItalic/Roboto-BlackItalic.ttf create mode 100644 web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff create mode 100644 web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff2 create mode 100644 web/libs/fonts/Bold/Roboto-Bold.ttf create mode 100644 web/libs/fonts/Bold/Roboto-Bold.woff create mode 100644 web/libs/fonts/Bold/Roboto-Bold.woff2 create mode 100644 web/libs/fonts/BoldItalic/Roboto-BoldItalic.ttf create mode 100644 web/libs/fonts/BoldItalic/Roboto-BoldItalic.woff create mode 100644 web/libs/fonts/BoldItalic/Roboto-BoldItalic.woff2 create mode 100644 web/libs/fonts/COPYRIGHT.txt create mode 100644 web/libs/fonts/Italic/Roboto-Italic.ttf create mode 100644 web/libs/fonts/Italic/Roboto-Italic.woff create mode 100644 web/libs/fonts/Italic/Roboto-Italic.woff2 create mode 100644 web/libs/fonts/LICENSE.txt create mode 100644 web/libs/fonts/Light/Roboto-Light.ttf create mode 100644 web/libs/fonts/Light/Roboto-Light.woff create mode 100644 web/libs/fonts/Light/Roboto-Light.woff2 create mode 100644 web/libs/fonts/LightItalic/Roboto-LightItalic.ttf create mode 100644 web/libs/fonts/LightItalic/Roboto-LightItalic.woff create mode 100644 web/libs/fonts/LightItalic/Roboto-LightItalic.woff2 create mode 100644 web/libs/fonts/METADATA.pb create mode 100644 web/libs/fonts/Material-Design-Iconic-Font.eot create mode 100644 web/libs/fonts/Material-Design-Iconic-Font.svg create mode 100644 web/libs/fonts/Material-Design-Iconic-Font.ttf create mode 100644 web/libs/fonts/Material-Design-Iconic-Font.woff create mode 100644 web/libs/fonts/Material-Design-Iconic-Font.woff2 create mode 100644 web/libs/fonts/Medium/Roboto-Medium.ttf create mode 100644 web/libs/fonts/Medium/Roboto-Medium.woff create mode 100644 web/libs/fonts/Medium/Roboto-Medium.woff2 create mode 100644 web/libs/fonts/MediumItalic/Roboto-MediumItalic.ttf create mode 100644 web/libs/fonts/MediumItalic/Roboto-MediumItalic.woff create mode 100644 web/libs/fonts/MediumItalic/Roboto-MediumItalic.woff2 create mode 100644 web/libs/fonts/Regular/Roboto-Regular.ttf create mode 100644 web/libs/fonts/Regular/Roboto-Regular.woff create mode 100644 web/libs/fonts/Regular/Roboto-Regular.woff2 create mode 100644 web/libs/fonts/Thin/Roboto-Thin.ttf create mode 100644 web/libs/fonts/Thin/Roboto-Thin.woff create mode 100644 web/libs/fonts/Thin/Roboto-Thin.woff2 create mode 100644 web/libs/fonts/ThinItalic/Roboto-ThinItalic.ttf create mode 100644 web/libs/fonts/ThinItalic/Roboto-ThinItalic.woff create mode 100644 web/libs/fonts/ThinItalic/Roboto-ThinItalic.woff2 create mode 100644 web/libs/fonts/montserrat-bold.eot create mode 100644 web/libs/fonts/montserrat-bold.svg create mode 100644 web/libs/fonts/montserrat-bold.ttf create mode 100644 web/libs/fonts/montserrat-bold.woff create mode 100644 web/libs/fonts/montserrat-bold.woff2 create mode 100644 web/libs/fonts/montserrat-light.eot create mode 100644 web/libs/fonts/montserrat-light.svg create mode 100644 web/libs/fonts/montserrat-light.ttf create mode 100644 web/libs/fonts/montserrat-light.woff create mode 100644 web/libs/fonts/montserrat-light.woff2 create mode 100644 web/libs/fonts/montserrat-regular.eot create mode 100644 web/libs/fonts/montserrat-regular.svg create mode 100644 web/libs/fonts/montserrat-regular.ttf create mode 100644 web/libs/fonts/montserrat-regular.woff create mode 100644 web/libs/fonts/montserrat-regular.woff2 create mode 100644 web/libs/js/dash2.apiwindow.js create mode 100644 web/libs/js/dash2.basic.js create mode 100644 web/libs/js/dash2.config.js create mode 100644 web/libs/js/dash2.confirm.js create mode 100644 web/libs/js/dash2.cronfilter.js create mode 100644 web/libs/js/dash2.detectorfilter.js create mode 100644 web/libs/js/dash2.elementbuilder.js create mode 100644 web/libs/js/dash2.elements.js create mode 100644 web/libs/js/dash2.gridstack.js create mode 100644 web/libs/js/dash2.init.js create mode 100644 web/libs/js/dash2.logviewer.js create mode 100644 web/libs/js/dash2.monitoredit.js create mode 100644 web/libs/js/dash2.multimon.js create mode 100644 web/libs/js/dash2.onvifscanner.js create mode 100644 web/libs/js/dash2.powervideo.js create mode 100644 web/libs/js/dash2.probe.js create mode 100644 web/libs/js/dash2.regioneditor.js create mode 100644 web/libs/js/dash2.socketio.js create mode 100644 web/libs/js/dash2.timelapse.js create mode 100644 web/libs/js/dash2.usersettings.js create mode 100644 web/libs/js/dash2.vidview.js create mode 100644 web/libs/less/_Black.less create mode 100644 web/libs/less/_BlackItalic.less create mode 100644 web/libs/less/_Bold.less create mode 100644 web/libs/less/_BoldItalic.less create mode 100644 web/libs/less/_Italic.less create mode 100644 web/libs/less/_Light.less create mode 100644 web/libs/less/_LightItalic.less create mode 100644 web/libs/less/_Medium.less create mode 100644 web/libs/less/_MediumItalic.less create mode 100644 web/libs/less/_Regular.less create mode 100644 web/libs/less/_Thin.less create mode 100644 web/libs/less/_ThinItalic.less create mode 100644 web/libs/less/_mixins.less create mode 100644 web/libs/less/_variables.less create mode 100644 web/libs/sass/_Black.scss create mode 100644 web/libs/sass/_BlackItalic.scss create mode 100644 web/libs/sass/_Bold.scss create mode 100644 web/libs/sass/_BoldItalic.scss create mode 100644 web/libs/sass/_Italic.scss create mode 100644 web/libs/sass/_Light.scss create mode 100644 web/libs/sass/_LightItalic.scss create mode 100644 web/libs/sass/_Medium.scss create mode 100644 web/libs/sass/_MediumItalic.scss create mode 100644 web/libs/sass/_Regular.scss create mode 100644 web/libs/sass/_Thin.scss create mode 100644 web/libs/sass/_ThinItalic.scss create mode 100644 web/libs/sass/_mixins.scss create mode 100644 web/libs/sass/_variables.scss create mode 100644 web/libs/sass/roboto.scss delete mode 100644 web/pages/blocks/migrator.ejs create mode 100644 web/pages/blocks/monitorStates.ejs create mode 100644 web/pages/blocks/schedules.ejs create mode 100644 web/pages/blocks/superBackupDatabase.ejs create mode 100644 web/pages/robots.txt delete mode 100644 web/templates/boilerplate/README.md delete mode 100644 web/templates/boilerplate/assets/css/bootstrap.min.css delete mode 100644 web/templates/boilerplate/assets/css/now-ui-dashboard.css delete mode 100644 web/templates/boilerplate/assets/css/now-ui-dashboard.css.map delete mode 100644 web/templates/boilerplate/assets/css/now-ui-dashboard.min.css delete mode 100644 web/templates/boilerplate/assets/css/styles.css delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Black.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Black.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Black.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BlackItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BlackItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BlackItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Bold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Bold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Bold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-BoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLight.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLight.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLight.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Italic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Italic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Italic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Light.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Light.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Light.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-LightItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-LightItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-LightItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Medium.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Medium.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Medium.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-MediumItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-MediumItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-MediumItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Regular.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Regular.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Regular.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Thin.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Thin.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-Thin.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ThinItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ThinItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat-ThinItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/Montserrat.css delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Black.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Black.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Black.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BlackItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BlackItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BlackItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Bold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Bold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Bold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-BoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraBoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLight.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLight.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLight.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLightItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLightItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ExtraLightItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Italic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Italic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Italic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Light.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Light.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Light.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-LightItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-LightItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-LightItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Medium.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Medium.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Medium.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-MediumItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-MediumItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-MediumItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Regular.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Regular.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Regular.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBold.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBold.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBold.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBoldItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBoldItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-SemiBoldItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Thin.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Thin.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-Thin.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ThinItalic.eot delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ThinItalic.woff delete mode 100644 web/templates/boilerplate/assets/fonts/Montserrat/MontserratAlternates-ThinItalic.woff2 delete mode 100644 web/templates/boilerplate/assets/fonts/nucleo-outline.eot delete mode 100644 web/templates/boilerplate/assets/fonts/nucleo-outline.ttf delete mode 100644 web/templates/boilerplate/assets/fonts/nucleo-outline.woff delete mode 100644 web/templates/boilerplate/assets/fonts/nucleo-outline.woff2 delete mode 100644 web/templates/boilerplate/assets/js/bootstrap-notify.js delete mode 100644 web/templates/boilerplate/assets/js/bootstrap.min.js delete mode 100644 web/templates/boilerplate/assets/js/chartjs.min.js delete mode 100644 web/templates/boilerplate/assets/js/main.js delete mode 100644 web/templates/boilerplate/assets/js/moment.min.js delete mode 100644 web/templates/boilerplate/assets/js/now-ui-dashboard.min.js delete mode 100644 web/templates/boilerplate/assets/js/perfect-scrollbar.jquery.min.js delete mode 100644 web/templates/boilerplate/assets/js/popper.min.js delete mode 100644 web/templates/boilerplate/handler.js delete mode 100644 web/templates/boilerplate/views/blocks/footer.ejs delete mode 100644 web/templates/boilerplate/views/blocks/header.ejs delete mode 100644 web/templates/boilerplate/views/blocks/home.ejs delete mode 100644 web/templates/boilerplate/views/index.ejs diff --git a/INSTALL/centos.sh b/INSTALL/centos.sh index 129822a..c762adb 100644 --- a/INSTALL/centos.sh +++ b/INSTALL/centos.sh @@ -27,7 +27,7 @@ if [ ! -e "./super.json" ]; then fi echo "Shinobi - Run yum update" sudo yum update -y -sudo yum install make -y +sudo yum install make zip -y echo "=============" echo "Shinobi - Do you want to Install FFMPEG?" echo "(y)es or (N)o" @@ -143,21 +143,14 @@ if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then echo "=====================================" echo "=====================================" fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/INSTALL/openalpr-cpu-easy.sh b/INSTALL/openalpr-cpu-easy.sh new file mode 100644 index 0000000..56e54b7 --- /dev/null +++ b/INSTALL/openalpr-cpu-easy.sh @@ -0,0 +1,41 @@ +# Install prerequisites +DIR=`dirname $0` +INSTALLERS_DIR="$DIR" +echo "-----------------------------------" +if ! [ -x "$(command -v opencv_version)" ]; then + echo "Installing OpenCV" + dos2unix $INSTALLERS_DIR/opencv-cuda.sh + sh $INSTALLERS_DIR/opencv-cuda.sh +else + echo "OpenCV found... : $(opencv_version)" +fi +# this includes all the ones missing from OpenALPR's guide. +sudo apt install libtesseract-dev git cmake build-essential libleptonica-dev -y +sudo apt install liblog4cplus-dev libcurl3-dev -y +sudo apt install libleptonica-dev -y +sudo apt install libcurl4-openssl-dev -y +sudo apt install liblog4cplus-dev -y +sudo apt install beanstalkd -y +sudo apt install openjdk-8-jdk -y + +# Clone the latest code from GitHub +git clone https://github.com/openalpr/openalpr.git + +# Setup the build directory +cd openalpr/src +mkdir build +cd build + +# setup the compile environment +cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_INSTALL_SYSCONFDIR:PATH=/etc .. + +# compile the library +make + +# Install the binaries/libraries to your local system (prefix is /usr) +sudo make install + +# Test the library +wget http://plates.openalpr.com/h786poj.jpg -O lp.jpg +alpr lp.jpg +rm lp.jpg diff --git a/INSTALL/openalpr-gpu-easy.sh b/INSTALL/openalpr-gpu-easy.sh index 0949629..06903f1 100644 --- a/INSTALL/openalpr-gpu-easy.sh +++ b/INSTALL/openalpr-gpu-easy.sh @@ -1,4 +1,17 @@ # Install prerequisites +DIR=`dirname $0` +INSTALLERS_DIR="$DIR" +echo "-----------------------------------" +if ! [ -x "$(command -v opencv_version)" ]; then + echo "Installing OpenCV" + dos2unix $INSTALLERS_DIR/opencv-cuda.sh + sh $INSTALLERS_DIR/opencv-cuda.sh +else + echo "OpenCV found... : $(opencv_version)" +fi +# get tesseract repo because ubuntu repo is serving a broken version +sudo add-apt-repository ppa:alex-p/tesseract-ocr -y +sudo apt-get update # this includes all the ones missing from OpenALPR's guide. sudo apt install libtesseract-dev git cmake build-essential libleptonica-dev -y sudo apt install liblog4cplus-dev libcurl3-dev -y @@ -28,4 +41,4 @@ sudo make install # Test the library wget http://plates.openalpr.com/h786poj.jpg -O lp.jpg alpr lp.jpg -rm lp.jpg \ No newline at end of file +rm lp.jpg diff --git a/INSTALL/opencv-cuda.sh b/INSTALL/opencv-cuda.sh index 0eb75e9..1a1ee50 100644 --- a/INSTALL/opencv-cuda.sh +++ b/INSTALL/opencv-cuda.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # OpenCV CUDA if [ $(dpkg-query -W -f='${Status}' git 2>/dev/null | grep -c "ok installed") -eq 0 ]; then echo "Installing Git..." @@ -77,4 +77,4 @@ read opencvuninstall if [ "$opencvuninstall" = "y" ] || [ "$opencvuninstall" = "Y" ]; then rm -rf opencv rm -rf opencv_contrib -fi +fi \ No newline at end of file diff --git a/INSTALL/opensuse.sh b/INSTALL/opensuse.sh index db021be..3a10b3e 100644 --- a/INSTALL/opensuse.sh +++ b/INSTALL/opensuse.sh @@ -115,15 +115,6 @@ sudo chmod -R 755 . touch INSTALL/installed.txt dos2unix /home/Shinobi/INSTALL/shinobi ln -s /home/Shinobi/INSTALL/shinobi /usr/bin/shinobi -if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then - echo "=====================================" > INSTALL/installed.txt - echo "======= Login Credentials =======" >> INSTALL/installed.txt - echo "|| Username : $userEmail" >> INSTALL/installed.txt - echo "|| Password : $userPasswordPlain" >> INSTALL/installed.txt - echo "|| API Key : $apiKey" >> INSTALL/installed.txt - echo "=====================================" >> INSTALL/installed.txt - echo "=====================================" >> INSTALL/installed.txt -fi echo "Shinobi - Start Shinobi and set to start on boot?" echo "(y)es or (N)o" read startShinobi @@ -134,31 +125,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "Y" ]; then sudo pm2 save sudo pm2 list fi -if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then - echo "details written to INSTALL/installed.txt" - echo "=====================================" - echo "======= Login Credentials =======" - echo "|| Username : $userEmail" - echo "|| Password : $userPasswordPlain" - echo "|| API Key : $apiKey" - echo "=====================================" - echo "=====================================" -fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/INSTALL/shinobi b/INSTALL/shinobi index 30b9401..ed17294 100644 --- a/INSTALL/shinobi +++ b/INSTALL/shinobi @@ -1,30 +1,68 @@ #!/bin/bash -installationDirectory="/home/Shinobi" -if [ ! "$1" ]; then +if [ ! -e "/etc/shinobisystems/path.txt" ]; then + installationDirectory="/home/Shinobi" +else + installationDirectory=$(cat /etc/shinobisystems/cctv.txt) +fi +cd $installationDirectory +currentBuild=$(git show --oneline -s) +gitOrigin=$(git remote show origin) +splitBuildString=($currentBuild) +currentCommitNumber=${splitBuildString[0]} +if [[ $gitOrigin == *'ShinobiCE'* ]]; then + repo="CE" +else + repo="Pro" +fi +if [[ $@ == *'help'* ]] || [ ! "$1" ]; then echo "=========================================================" echo "==!! Shinobi : The Open Source CCTV and NVR Solution !!==" echo "=========================================================" - echo "You are missing function parameters." - echo "Example : shinobi [command] .." - echo "Example : shinobi flush restart logs" + if [ ! "$1" ]; then + echo "You are missing function parameters." + echo "Example : shinobi [command] .." + echo "Example : shinobi flush restart logs" + else + echo "Hello there! if you need support come on over" + echo "to the Shinobi Community Chat! :)" + echo "https://discordapp.com/invite/mdhmvuH/" + fi echo "=========================================================" echo "Your available options for COMMAND are as follows" echo "=========================================================" - echo "| start or s :" + echo "| start :" echo "|--> Start camera.js and cron.js under PM2 (Process Manager)" echo "-" - echo "| restart or r :" + echo "| restart :" echo "|--> Restart all processes running under the PM2 daemon." echo "-" - echo "| stop, exit, or e :" + echo "| stop, exit :" echo "|--> Stop all processes running under the PM2 daemon." echo "-" + echo "| version :" + echo "|--> get version of your current build by git." + echo "-" echo "| logs :" echo "|--> Get PM2 log stream with last 100 lines." echo "-" - echo "| clear, flush, or f :" + echo "| update :" + echo "|--> Update via Git." + echo "-" + echo "| getMaster :" + echo "|--> Switch to the Master Branch (For Pro Repo only)." + echo "-" + echo "| getDev :" + echo "|--> Switch to the Development Branch (For Pro Repo only)." + echo "-" + echo "| clear, flush :" echo "|--> Clear all PM2 logs." echo "-" + echo "| bootupEnable :" + echo "|--> Start Shinobi on OS reboot." + echo "-" + echo "| bootupDisable :" + echo "|--> Disable starting Shinobi on OS reboot." + echo "-" echo "| kill :" echo "|--> Stop the entire PM2 daemon." fi @@ -34,7 +72,8 @@ fi if [[ $@ == *'restart'* ]]; then proccessAlive=$(pm2 list | grep camera) if [ "$proccessAlive" ]; then - pm2 restart all + pm2 restart $installationDirectory/camera.js + pm2 restart $installationDirectory/cron.js else echo "Shinobi process is not running." fi @@ -58,11 +97,64 @@ fi if [[ $@ == *'stop'* ]] || [[ $@ == *'exit'* ]]; then proccessAlive=$(pm2 list | grep camera) if [ "$proccessAlive" ]; then - pm2 kill + pm2 stop $installationDirectory/camera.js + pm2 stop $installationDirectory/cron.js else echo "Shinobi process is not running." fi fi +if [[ $@ == *'version'* ]]; then + echo "Build ID : $currentCommitNumber" + if [[ $repo == "Pro" ]]; then + echo "Repository : Shinobi Pro" + else + echo "Repository : Shinobi CE" + fi + echo $currentBuild +fi +if [[ $@ == *'bootupEnable'* ]] || [[ $@ == *'bootupenable'* ]]; then + pm2 startup + pm2 save +fi +if [[ $@ == *'bootupDisable'* ]] || [[ $@ == *'bootupdisable'* ]]; then + pm2 unstartup + pm2 save +fi +if [[ $@ == *'getDev'* ]] || [[ $@ == *'getdev'* ]]; then + if [[ $repo == "Pro" ]]; then + git checkout dev + echo "Shinobi - Restart Shinobi to make the changes take affect." + else + echo "Shinobi - Cannot use \"getDev\" with Shinobi CE" + fi +fi +if [[ $@ == *'getMaster'* ]] || [[ $@ == *'getmaster'* ]]; then + if [[ $repo == "Pro" ]]; then + git checkout master + echo "Shinobi - Restart Shinobi to make the changes take affect." + else + echo "Shinobi - Cannot use \"getMaster\" with Shinobi CE" + fi +fi +if [[ $@ == *'update'* ]]; then + echo "=============" + echo "Shinobi - Are you sure you want to update? This will restart Shinobi." + echo "(y)es or (N)o" + read updateshinobi + if [ "$updateshinobi" = "y" ] || [ "$updateshinobi" = "Y" ]; then + echo "Beginning Update Process..." + pm2 stop $installationDirectory/camera.js + pm2 stop $installationDirectory/cron.js + npm install --unsafe-perm + npm audit fix --force + git reset --hard + git pull + pm2 start $installationDirectory/camera.js + pm2 start $installationDirectory/cron.js + else + echo "Cancelled Update Process." + fi +fi if [[ $@ == *'kill'* ]]; then pm2 kill fi diff --git a/INSTALL/start.sh b/INSTALL/start.sh index a2439f7..64df53e 100644 --- a/INSTALL/start.sh +++ b/INSTALL/start.sh @@ -3,6 +3,7 @@ if [ -e "INSTALL/installed.txt" ]; then echo "Starting Shinobi" pm2 start camera.js pm2 start cron.js + pm2 logs fi if [ ! -e "INSTALL/installed.txt" ]; then chmod +x INSTALL/now.sh&&INSTALL/now.sh diff --git a/INSTALL/terminalCommands.js b/INSTALL/terminalCommands.js new file mode 100644 index 0000000..bc6e4e7 --- /dev/null +++ b/INSTALL/terminalCommands.js @@ -0,0 +1,41 @@ +var fs = require('fs'); +var moment = require('moment'); +var exec = require('child_process').exec; +var execSync = require('child_process').execSync; +s = { + isWin: (process.platform === 'win32' || process.platform === 'win64'), + mainDirectory: __dirname.split('/INSTALL')[0] +} +var createTerminalCommands = function(callback){ + var next = function(){ + if(callback)callback() + } + if(!s.isWin){ + var etcPath = '/etc/shinobisystems/' + console.log('Creating "' + etcPath + '"...') + var createPathFile = function(){ + var pathTxt = etcPath + 'cctv.txt' + console.log('Creating "' + pathTxt + '"...') + fs.writeFile(pathTxt,s.mainDirectory,function(err){ + if(err)console.log(err) + fs.chmod(pathTxt,0o777,function(err){ + if(err)console.log(err) + console.log('Linking "' + s.mainDirectory + '/INSTALL/shinobi" to "/usr/bin/shinobi"...') + fs.symlink(s.mainDirectory + '/INSTALL/shinobi', '/usr/bin/shinobi', next) + console.log('You can now use `shinobi` in terminal.') + }) + }) + } + fs.stat(etcPath,function(err,stat){ + if(!err && stat){ + createPathFile() + }else{ + fs.mkdir(etcPath,createPathFile) + } + }) + }else{ + //no commands for windows yet + next() + } +} +createTerminalCommands() diff --git a/INSTALL/ubuntu.sh b/INSTALL/ubuntu.sh index b033daf..87bd23f 100644 --- a/INSTALL/ubuntu.sh +++ b/INSTALL/ubuntu.sh @@ -30,12 +30,17 @@ if [ ! -e "./super.json" ]; then echo "* You can edit these settings in \"super.json\" located in the Shinobi directory." sudo cp super.sample.json super.json fi +if ! [ -x "$(command -v ifconfig)" ]; then + echo "=============" + echo "Shinobi - Installing Net-Tools" + sudo apt install net-tools -y +fi if ! [ -x "$(command -v node)" ]; then echo "=============" echo "Shinobi - Installing Node.js" - wget https://deb.nodesource.com/setup_9.x - chmod +x setup_9.x - ./setup_9.x + wget https://deb.nodesource.com/setup_8.x + chmod +x setup_8.x + ./setup_8.x sudo apt install nodejs -y else echo "Node.js Found..." @@ -44,7 +49,7 @@ fi if ! [ -x "$(command -v npm)" ]; then sudo apt install npm -y fi -sudo apt install make -y +sudo apt install make zip -y if ! [ -x "$(command -v ffmpeg)" ]; then if [ "$getubuntuversion" = "16" ] || [ "$getubuntuversion" < "16" ]; then echo "=============" @@ -132,21 +137,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "y" ]; then sudo pm2 save sudo pm2 list fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/camera.js b/camera.js index f5939a0..f5de80e 100644 --- a/camera.js +++ b/camera.js @@ -15,16 +15,20 @@ var loadLib = function(lib){ } //process handlers var s = loadLib('process')(process,__dirname) +//load extender functions +loadLib('extenders')(s) //configuration loader var config = loadLib('config')(s) //language loader var lang = loadLib('language')(s,config) +//code test module +loadLib('codeTester')(s,config,lang) //basic functions loadLib('basic')(s,config) -//load extender functions -loadLib('extenders')(s,config) //video processing engine -loadLib('ffmpeg')(s,config,function(){ +loadLib('ffmpeg')(s,config,function(ffmpeg){ + //ffmpeg coProcessor + loadLib('ffmpegCoProcessor')(s,config,lang,ffmpeg) //database connection : mysql, sqlite3.. loadLib('sql')(s,config) //working directories : videos, streams, fileBin.. @@ -60,9 +64,17 @@ loadLib('ffmpeg')(s,config,function(){ //cluster module loadLib('childNode')(s,config,lang,app,io) //cloud uploaders : amazon s3, webdav, backblaze b2.. - loadLib('cloudUploaders')(s,config,lang) + loadLib('uploaders')(s,config,lang) //notifiers : discord.. loadLib('notification')(s,config,lang) + //notifiers : discord.. + loadLib('rtmpserver')(s,config,lang) + //dropInEvents server (file manipulation to create event trigger) + loadLib('dropInEvents')(s,config,lang,app,io) + //custom module loader + loadLib('customAutoLoad')(s,config,lang,app,io) + //scheduling engine + loadLib('scheduler')(s,config,lang,app,io) //on-start actions, daemon(s) starter loadLib('startup')(s,config,lang) }) diff --git a/conf.sample.json b/conf.sample.json index 9a74575..5931061 100644 --- a/conf.sample.json +++ b/conf.sample.json @@ -1,6 +1,7 @@ { "port": 8080, "passwordType": "sha256", + "detectorMergePamRegionTriggers": true, "addStorage": [ {"name":"second","path":"__DIR__/videos2"} ], diff --git a/cron.js b/cron.js index 6856322..a9eb577 100644 --- a/cron.js +++ b/cron.js @@ -170,7 +170,7 @@ s.localToUtc = function(time){ return moment(time).utc() } s.nameToTime = function(x){x=x.replace('.webm','').replace('.mp4','').split('T'),x[1]=x[1].replace(/-/g,':');x=x.join(' ');return x;} -io = require('socket.io-client')('ws://'+config.ip+':'+config.port);//connect to master +io = require('socket.io-client')('ws://'+config.ip+':'+config.port,{transports:['websocket']});//connect to master s.cx = function(x){x.cronKey=config.cron.key;return io.emit('cron',x)} //emulate master socket emitter s.tx = function(x,y){s.cx({f:'s.tx',data:x,to:y})} diff --git a/definitions/en_CA.json b/definitions/en_CA.json index ab0e5af..d5ea281 100644 --- a/definitions/en_CA.json +++ b/definitions/en_CA.json @@ -766,6 +766,14 @@ "example": "", "possible": "" }, + { + "name": "detail=detector_send_video_length", + "field": "Notification Video Length", + "description": "In seconds. The length of the video that gets sent to your Notification service, like Email or Discord.", + "default": "10", + "example": "", + "possible": "" + }, { "name": "detail=watchdog_reset", "field": "Timeout Reset on Next Event", @@ -835,8 +843,16 @@ { "name": "detail=detector_fps", "field": "Detector Rate (FPS)", - "description": "How many frames a second to send to the motion detector; 1 is the default.", - "default": "1", + "description": "How many frames a second to send to the motion detector; 2 is the default.", + "default": "2", + "example": "", + "possible": "" + }, + { + "name": "detail=detector_buffer_seconds", + "field": "Recorded Buffer", + "description": "How many seconds before the event to include in the recorded video.", + "default": "5", "example": "", "possible": "" }, diff --git a/languages/cz.json b/languages/cz.json new file mode 100644 index 0000000..485ea71 --- /dev/null +++ b/languages/cz.json @@ -0,0 +1,857 @@ +{ + "Shinobi": "Shinobi", + "superAdminTitle": "Shinobi : Super Admin", + "failedLoginText1": "Příliš mnoho neúspěšných přihlášení. Vyčkejte 15minut před dalším pokusem.", + "failedLoginText2": "Prosím zkontrolujte přihlašovací údaje.", + "Time Left": "Zbývající čas", + "Login": "Uživatel", + "Authenticate": "Ověřit", + "Dashboard": "Dashboard", + "Streamer": "Streamer", + "Admin": "Admin", + "Superuser": "Superuser", + "Dashcam": "Dashcam", + "Email": "Email", + "Username": "Uživatel", + "Profile": "Profil", + "Password": "Heslo", + "Password Again": "Heslo znovu", + "Remember Me": "Zamapatovat si mě", + "RAM": "RAM", + "CPU": "CPU", + "on": "na", + "Power Viewer": "Power Viewer", + "Power Video Viewer": "Power Video Viewer", + "Time-lapse": "Time-lapse", + "Montage": "Montáž", + "Accounts": "Účty", + "Settings": "Nastavení", + "Recording FPS": "Recording FPS", + "Input Selector": "Výběr vstupu", + "Input Settings": "Nastavení vstupu", + "Connection": "Připojení", + "Video Set": "Video Set", + "API": "API", + "ONVIF": "ONVIF", + "FFprobe": "Sonda", + "Monitor States": "Monitorovat stavy", + "Schedule": "Plán", + "Schedules": "Plány", + "Monitor States and Schedules": "Monitorovat stavy a plány", + "Filters": "Filtry", + "Full URL Path": "Plná URL cesta", + "Logs": "Logy", + "Full Stream URL": "Full Stream URL", + "Manual": "Manuální", + "List Toggle": "Přepnout zobrazení", + "Hide List": "Skrýt seznam", + "Motion GUI": "Motion GUI", + "Motion": "Pohyb", + "Global Detector Settings": "Globální nastavení detekce", + "Motion Detection": "Detekce pohybu", + "Object Detection": "Detekce objektů", + "JPEG Mode": "Režim JPEG", + "Order Streams": "Řazení streamů", + "Hide Notes": "Skrýt poznámky", + "Example": "Příklad", + "Logout": "Odhlásit", + "Closed": "Uzavřeno", + "Ended": "Skončilo", + "Options": "Možnosti", + "Started": "Začalo", + "Monitor": "Monitor", + "Filename": "Soubor", + "Size (mb)": "Velikost (MB)", + "Watch": "Sledovat", + "Download": "Stáhnout", + "Delete": "Smazat", + "Fix": "Opravit", + "Use HTML5 Play Method": "Použít HTML5 Přehrávání", + "Connection timed out": "Připojení vypršelo", + "skipPingText1": "Zkuste Nastavit \"Vynechat Ping\" na Ano.", + "Ping Failed": "Ping Selhal", + "Zoom In": "Přiblížení URL Adresa", + "Zoom Out": "Oddálení URL Adresa", + "Enable Nightvision": "Zapni Noční Vidění", + "Disable Nightvision": "Vypni Noční Vidění", + "Current": "Aktuální", + "Monitors": "Monitory", + "Video": "Video", + "Themes": "Témata", + "Videos": "Videa", + "Events": "Události", + "Streams": "Streamy", + "Snapshot": "Snapshot", + "Snapshots": "Snapshoty", + "Date Range": "Časové období", + "Event Limit": "Limit Událostí", + "No Data": "Žádná Data", + "Live View": "Živý Pohled", + "New Monitor": "Nový Monitor", + "Please Check Your Settings": "Prosím Zkontrolujte Nastavení", + "migrateText1": "Typ Vstupu nelze parsovat. Prosím nastavte manuálně.", + "Add": "Přidat", + "Save": "Uložit", + "Close": "Zavřít", + "Secure": "Secure", + "Check": "Ověřit", + "Stop": "Stop", + "Confirm": "Potvrdit", + "Enable": "Povolit", + "Enabled": "Povoleno", + "API Key": "API Klíč", + "API Keys": "API Klíče", + "Group Key": "Klíč Skupiny", + "Allowed IPs": "Povolené IP", + "Separate with commas, no spaces": "Oddělit čárkami, bez mezer", + "Can Get Monitors": "Can Get Monitory", + "Can Get Logs": "Can Get Logs", + "Can Authenticate Websocket": "Can Authenticate Websocket", + "Can Control Monitors": "Can Control Monitory", + "Can View Snapshots": "Can View Snapshots", + "Can View Streams": "Can View Streams", + "Can View Videos": "Can View Videos", + "Can View Monitor": "Can View Monitor", + "Can Change User Settings": "Can Change User Settings", + "Can Create and Delete Monitors": "Can Create and Delete Monitory", + "Can Edit Monitor": "Can Edit Monitor", + "Can Delete Videos": "Can Delete Videos", + "Delete Video": "Delete Video", + "Can View Videos and Events": "Can View Videos and Events", + "Can Delete Videos and Events": "Can Delete Videos and Events", + "Saved Filters": "Uložené filtry", + "Saved Presets": "Uložené předvolby", + "Saved Schedules": "Uložené časovače", + "Filter Name": "Název filtru", + "Find Where": "Find Where", + "Reason": "Důvod", + "Detection Engine": "Detection Engine", + "X Point": "Bod X", + "Y Point": "Bod Y", + "Sort By": "Řadit dle", + "Start Time": "Start Time", + "End Time": "End Time", + "Time": "Čas", + "Monitor ID": "Monitor ID", + "File Type": "Typ souboru", + "Filesize": "Velikost souboru", + "Video Status": "Stav Videa", + "Preferences": "Možnosti", + "Equal to": "Rovno", + "Not Equal to": "Není rovno", + "Greater Than or Equal to": "Větší než nebo rovno", + "Greater Than": "Větší než", + "Less Than": "Menší než", + "Less Than or Equal to": "Menší než nebo rovno", + "Contains": "Obsahuje", + "Does Not Contain": "Neobsahuje", + "AND": "A", + "OR": "Nebo", + "Like": "Jako", + "Matches": "Odpovída", + "Not Matches": "Neodpovídá", + "In": "In", + "Not In": "Not In", + "ASC": "Vzestupně", + "DESC": "Sestupně", + "Action for Selected": "Action for Selected", + "Search": "Hledat", + "No": "Ne", + "Yes": "Ano", + "Start": "Start", + "End": "Konec", + "Archive": "Archiv", + "Email Details": "Email Details", + "Delete Matches": "Delete Matches", + "Delete selected": "Delete selected", + "Execute Command": "Execute Command", + "for Global Access": "for Global Access", + "Help": "Pomoc", + "Don't show this anymore": "Již nezobrazovat", + "Chat on Discord": "Chat on Discord", + "Documentation": "Dokumentace", + "All Monitors": "Všechny monitory", + "Motion Meter": "Motion Meter", + "FFmpegTip": "FFprobe is a simple multimedia streams analyzer. You can use it to output all kinds of information about an input including duration, frame rate, frame size, etc.", + "Complete Stream URL": "Complete Stream URL", + "ONVIF Scanner": "ONVIF Scanner", + "ONVIFEventsNotAvailable": "ONVIF události njesou dostupné", + "ONVIFnotCompliantProfileT": "Camera is not ONVIF Profile T Compliant", + "Scan Settings": "Nastavení scanu", + "ONVIFnote": "Discover ONVIF devices on networks outside your own or leave it blank to scan your current network.
Username and Password can be left blank.", + "Range or Single": "Range or Single", + "IP Address": "IP Addresa", + "Port": "Port", + "Camera Username": "CAM Uživatel", + "Camera Password": "CAM Heslo", + "Found Devices": "Zařízení nalezeno", + "Switch on for Still Image": "Switch on for Still Image", + "Live Stream Toggle": "Live Stream Toggle", + "RegionNote": "Points are only saved when you press Save on the Monitor Settings window.", + "Points": "Points When adding points click on the edge of the polygon.", + "Indifference": "Indifference", + "Max Indifference": "Max Indifference", + "Trigger Threshold": "Trigger Threshold", + "Color Threshold": "Color Threshold", + "Region Name": "Název Regionu", + "Regions": "Regiony", + "Again": "Znovu", + "Account Info": "Info o účtu", + "blankPassword": "Nechcete-li měnit heslo, ponechte prázdné", + "2-Factor Authentication": "Dvou-faktorové přihlášení", + "Use Max Storage Amount": "Použít max. velikost uložiště", + "Max Storage Amount": "Maximum uložiště v Megabajtech", + "Number of Days to keep": "Počet dní uchování", + "Monitor Groups": "Skupiny monitorů", + "Group Name": "Název skupiny", + "Show Thumbnails in Video List": "Show Thumbnails in Video List", + "WebDAV": "WebDAV", + "Backblaze B2": "Backblaze B2", + "Backblaze Error": "Backblaze Error", + "Could not create Bucket.": "Could not create Bucket.", + "Amazon S3": "Amazon S3", + "Database": "Databáze", + "Database Not Found": "Databáze nenalezena", + "User Not Found": "Uživatel nenalezen", + "Save Links to Database": "Save Links to Database", + "Upload File": "Nahrát soubor", + "Bucket": "Bucket", + "Region": "Region", + "Use Global Amazon S3 Video Storage": "Use Global Amazon S3 Video Storage", + "Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Video Storage", + "Use Global WebDAV Video Storage": "Use Global WebDAV Video Storage", + "Amazon S3 Upload Error": "Amazon S3 Upload Error", + "accountId": "Account ID", + "applicationKey": "Application Key", + "aws_accessKeyId": "Access Key Id", + "aws_secretAccessKey": "Secret Access Key", + "Discord Bot": "Discord Bot", + "URL": "URL", + "Operating Hours": "Operating Hours", + "Autosave": "Autosave", + "Save Directory": "Save Directory", + "CSS": "CSS Style your dashboard.", + "Force Monitors Per Row": "Force Monitory Per Row", + "Monitors per row": "Monitory per row for Montage", + "Browser Console Log": "Browser Console Log", + "Log Stream": "Log Stream", + "Privileges": "Privileges", + "All Monitors and Privileges": "All Monitory and Privileges", + "Permissions": "Permissions", + "Time-lapse Tool": "Time-lapse Tool", + "total": "total", + "MB": "MB", + "Calendar": "Calendar", + "Leave blank for random.": "Leave blank for random.", + "Currently viewing": "Currently viewing", + "Status Indicator": "Status Indicator", + "Show Logs": "Show Logs", + "Videos List": "Videos List", + "Monitor Settings": "Monitor Settings", + "Enlarge": "Enlarge", + "Fullscreen": "Fullscreen", + "Value": "Value", + "Idle": "Idle", + "Disabled": "Disabled", + "Record": "Record", + "Watch Only": "Watch Only", + "Toggle Sidebar": "Toggle Sidebar", + "Add Monitor": "Add Monitor", + "Start Recording": "Start Recording", + "Set to Watch Only": "Set to Watch Only", + "Save as": "Ukládat jako", + "Add New": "Přidat", + "Zip and Download": "Stáhnout jako ZIP", + "Export Selected Videos": "Exportovat vybraná videa", + "Delete Selected Videos": "Smazat vybraná videa", + "DeleteSelectedVideosMsg": "Do you want to delete these videos? You cannot recover them.", + "ExportSelectedVideosMsg": "Do you want to export these videos? It may take some time to zip and download.", + "clientStreamFailedattemptingReconnect": "Client side ctream check failed, attempting reconnect.", + "Export Video": "Export Video", + "Delete Filter": "Delete Filter", + "confirmDeleteFilter": "Do you want to delete this filter? You cannot recover it.", + "Fix Video": "Fix Video", + "FixVideoMsg": "Do you want to fix this video? You cannot undo this action.", + "DeleteVideoMsg": "Do you want to delete this video? You cannot recover it.", + "dropBoxSuccess": "Success! Files saved to your Dropbox.", + "API Key Deleted": "API Key Deleted", + "APIKeyDeletedText": "Key has been deleted. It will no longer work.", + "API Key Added": "API Key Added", + "APIKeyAddedText": "You may use this key now.", + "Update": "Update", + "Update to Master": "Update to Master", + "Update to Development": "Update to Development", + "Filters Updated": "Filters Updated", + "FiltersUpdatedText": "Your changes have been saved and applied.", + "Settings Changed": "Settings Changed", + "SettingsChangedText": "Your settings have been saved and applied. Some settings may require a refresh of this page.", + "Are you sure?": "Are you sure?", + "Import Monitor Configuration": "Import Monitor Configuration", + "ImportMultiMonitorConfigurationText": "Doing this will overrwrite any monitory with IDs existing in the import file.", + "ImportMonitorConfigurationText": "Doing this will overrwrite any changes currently not saved. Imported changes will only be applied when you press Save.", + "Paste JSON here.": "Paste JSON here.", + "Delete Monitor": "Delete Monitor", + "DeleteMonitorText": "Do you want to delete this monitor? You cannot recover it. You can choose for the files to remain in the filesystem. If you choose to recreate a monitor with the same ID the videos and events will become visible in the dashboard.", + "DeleteMonitorsText": "Do you want to delete these monitory? You cannot recover them. You can choose to keep the files for these IDs in the filesystem. If you choose to recreate a monitor with one of the IDs the videos and events will become visible in the dashboard.", + "Invalid JSON": "Invalid JSON", + "Invalid Data": "Invalid Data", + "Name cannot be empty.": "Name cannot be empty.", + "Start Time cannot be empty.": "Start Time cannot be empty.", + "Must be atleast one row": "Must be atleast one row", + "InvalidJSONText": "Please ensure this is a valid JSON string for Shinobi monitor configuration.", + "Passwords don't match": "Passwords don't match", + "Email address is in use.": "Email address is in use.", + "Group Key is in use.": "Group Key is in use.", + "Create Sub-Accounts at /admin": "Create Sub-Accounts at /admin", + "No Events found for this video": "No Events found for this video", + "Video and Time Span (Minutes)": "Video and Time Span (Minutes)", + "Video Length (minutes) and Motion Count per video": "Video Length (minutes) and Motion Count per video", + "Counts of Motion": "Counts of Motion", + "Unable to Launch": "Unable to Launch", + "UnabletoLaunchText": "Please save new monitor first. Then attempt to launch the region editor.", + "NoVideosFoundForDateRange": "No Videos found in this date range. Try setting the start date further back.", + "NoLogsFoundForDateRange": "No Logs found in this date range. Try widening the date range.", + "monitorEditFailedMaxReached": "Your account has reached the maximum number of cameras that can be created. Speak to an administrator if you would like this changed.", + "in": "in", + "ago": "ago", + "a few seconds": "a few seconds", + "a minute": "a minute", + "minutes": "minutes", + "an hour": "an hour", + "hours": "hours", + "a day": "a day", + "days": "days", + "a month": "a month", + "months": "months", + "a year": "a year", + "years": "years", + "Identity": "Identity", + "Input": "Input", + "Input Feed": "Input Feed", + "Stream": "Stream", + "Stream Timestamp": "Stream Timestamp", + "Stream Watermark": "Stream Watermark", + "JPEG API": "JPEG API Snapshot (cgi-bin)", + "Raw H.264 Stream": "Raw H.264 Stream", + "Recording": "Recording", + "Recording Timestamp": "Recording Timestamp", + "Recording Watermark": "Recording Watermark", + "Region Editor": "Region Editor", + "Detector Filters": "Detector Filters", + "Filter for Objects only": "Filter for Objects only", + "Custom": "Custom", + "Detector": "Detector", + "Audio Detector": "Audio Detector", + "Audio Detection": "Audio Detection", + "Minimum dB": "Minimum dB", + "Maximum dB": "Maximum dB", + "Connected": "Connected", + "Not Saved": "Not Saved", + "Not Connected": "Not Connected", + "Lisence Plate Detector": "Lisence Plate Detector", + "OpenCV Cascades": "OpenCV Cascades", + "Refresh List of Cascades": "Refresh List of Cascades", + "\"No Motion\" Detector": "\"No Motion\" Detector", + "Control": "Control", + "Grouping": "Grouping Add groups in Settings", + "Detector Grouping": "Detector Grouping Add groups in Settings", + "Detector Recording Process Exited Prematurely. Restarting.": "Detector Recording Process Exited Prematurely. Restarting.", + "Detector Recording Complete": "Detector Recording Complete", + "Clear Recorder Process": "Clear Recorder Process", + "Logging": "Logging", + "IdentityText1": "This is how the system will identify the data for this stream. You cannot change the Monitor ID once you have pressed save. If you want you can make the Monitor ID more human readable before you continue.", + "IdentityText2": "You can duplicate a monitor by modifying the Monitor ID then pressing save. You cannot use the ID of a monitor that already exists or it will save over that monitor's database information.", + "noSpecialCharacters": "No spaces or special characters.", + "NotesPlacholder": "Comments you want to leave for this cameras settings.", + "InputText1": "This section tells Shinobi how to consume a stream. For optimal performance try tuning your camera's internal settings. Find the following options and set them as shown. To find your camera you can use the built in ONVIF Scanner of Shinobi. Some ONVIF cameras require the use of a management tool to modify their internal settings. If you can't find your cameras you can try ONVIF Device Manager for Windows.", + "InputText2": "", + "InputText3": "If you need help figuring out what input type your camera is you can take a look in the Camera URLs List on the Shinobi website.", + "StreamText": "

This section will designate the primary method of streaming out and its settings. This stream will be displayed in the dashboard. If you choose to use HLS, JPEG, or MJPEG then you can consume the stream through other programs.

Using JPEG stream essentially turns off the primary stream and uses the snapshot bin to get frames.

", + "DetectorText": "

When the Width and Height boxes are shown you should set them to 640x480 or below. This will optimize the read speed of frames.

", + "RecordingText": "It is recommended that you set Record File Type to WebMMP4 and Video Codec to libvpxcopy or libx264 because your Input Type is set to .", + "Mode": "Mód", + "Name": "Název", + "Skip Ping": "Vynechat Ping", + "Retry Connection": "Retry Connection Number of times allowed to fail", + "Notes": "Notes", + "Input Type": "Input Type", + "Connection Type": "Connection Type", + "RTSP Transport": "RTSP Transport", + "Host": "Host", + "Force Port": "Force Port", + "Path": "Path", + "Monitor Capture Rate": "Monitor Capture Rate (FPS)", + "Analyzation Duration": "Analyzation Duration", + "Probe Size": "Probe Size", + "Stream Type": "Stream Type", + "# of Allow MJPEG Clients": "# of Allow MJPEG Clients 0 for infinite", + "HLS Video Encoder": "Video Encoder", + "HLS Audio Encoder": "Audio Encoder", + "HLS Segment Length": "Segment Length in Seconds", + "HLS Preset": "Preset Template", + "HLS List Size": "List Size", + "Traditional Recording": "Traditional Recording", + "Buffer Preview": "Buffer Preview", + "HLS Start Number": "HLS Start Number", + "HLS Live Start Index": "HLS Live Start Index", + "Check Signal Interval": "Check Signal Interval in Minutes", + "Log Signal Event": "Log Signal Event Client side only", + "Quality": "Quality 1 is High, 23 is Low", + "Rate": "Rate (FPS)", + "Width": "Width", + "Height": "Height", + "Rotate": "Rotate", + "Primary Engine": "Primary Engine", + "Video Filter": "Video Filter", + "Font Path": "Font Path", + "Font Size": "Font Size", + "Text Color": "Text Color", + "Text Box Color": "Text Box Color", + "Position X": "Position X", + "Position Y": "Position Y", + "Image Location": "Image Location Absolute Path or leave blank to use global", + "Image Position": "Image Position", + "Frame Rate": "Frame Rate (FPS)", + "Image Width": "Image Width", + "Image Height": "Image Height", + "Record File Type": "Record File Type", + "Video Codec": "Video Codec", + "Delete Monitor States Preset": "Delete Monitor States Preset", + "Delete Monitor State?": "Delete Monitor State", + "deleteMonitorStateText1": "Do you want to delete this Monitor States Preset?", + "deleteMonitorStateText2": "Do you want to delete this Monitor's Preset?", + "Search Images": "Hledat Images", + "Launch in New Window": "Launch in New Window", + "Preset": "Preset", + "Presets": "Presets", + "possibleInternalError": "Possible Internal Error", + "sizePurgeLockedText": "The Size Purge Lock (deleteOverMax) appears to have failed to unlock. Unlocking now...", + "Use coProcessor": "Use coProcessor", + "Audio Codec": "Audio Codec", + "Video Record Rate": "Video Record Rate (FPS)", + "Record Width": "Record Width", + "Record Height": "Record Height", + "Double Quote Directory": "Double Quote Directory Some directories have spaces. Using this may crash some cameras.", + "Recording Segment Interval": "Recording Segment Interval in minutes", + "Record Video Filter": "Record Video Filter", + "Input Flags": "Input Flags", + "Snapshot Flags": "Snapshot Flags", + "Detector Flags": "Detector Flags", + "Stream Flags": "Stream Flags", + "Stream to YouTube": "Stream to YouTube", + "Stream to YouTube Flags": "Stream to YouTube Flags", + "Recording Flags": "Recording Flags", + "Traditional Recording Flags": "Traditional Recording Flags", + "Output Method": "Output Method", + "Webhook": "Webhook", + "Event Webhook Error": "Event Webhook Error", + "Webhook URL": "Webhook URL", + "Command on Trigger": "Command on Trigger", + "Command": "Command", + "Allow Next Command": "Allow Next Command in Minutes", + "Allow Next Trigger": "Allow Next Trigger in Milliseconds", + "Save Events to SQL": "Save Events to SQL", + "Email on Trigger": "Email on Trigger Emails go to the main account holder's login address.", + "Attach Video Clip": "Attach Video Clip", + "Error While Decoding": "Error While Decoding", + "ErrorWhileDecodingText": "Your hardware may have an unstable connection to the network. Check your network connections.", + "Discord": "Discord", + "Discord Alert on Trigger": "Discord Alert on Trigger", + "Allow Next Email": "Allow Next Email in Minutes", + "Allow Next Discord Alert": "Allow Next Discord Alert in Minutes", + "DiscordLoggedIn": "Discord Bot Authenticated", + "DiscordFailedText": "Sending to Discord Failed", + "DiscordErrorText": "Sending to Discord caused an Error", + "DiscordNotEnabledText": "Discord Bot Not Enabled, Enable it in your Account Settings.", + "How to Record": "How to Record", + "Trigger Record": "Trigger Record", + "Recording Timeout": "Recording Timeout in Minutes", + "Timeout Reset on Next Motion": "Timeout Reset on Next Motion", + "Timeout Reset on Next Event": "Timeout Reset on Next Event", + "Delete Motionless Video": "Delete Motionless Video", + "Send Frames": "Send Frames Push frames to be analyzed", + "Detector Rate": "Detector Rate (FPS)", + "Feed-in Image Width": "Feed-in Image Width", + "Feed-in Image Height": "Feed-in Image Height", + "Check for Motion First": "Check for Motion First", + "Detect Objects": "Detect Objects See below", + "Full Frame Detection": "Full Frame Detection", + "Country of Plates": "Country of Plates", + "Email on No Motion": "Email on \"No Motion\"", + "Timeout": "Timeout", + "Controllable": "Controllable", + "Custom Base URL": "Custom Base URL Leave blank to use Host URL", + "Stop URL": "Stop URL", + "Stop Command": "Stop Command", + "Digest Authentication": "Digest Authentication", + "URL Stop Timeout": "URL Stop Timeout Run stop URL after X milliseconds", + "Center": "Center URL Address", + "Left": "Left URL Address", + "Left Stop": "Left Stop URL Address", + "Right": "Right URL Address", + "Right Stop": "Right Stop URL Address", + "Up": "Up URL Address", + "Up Stop": "Up Stop URL Address", + "Down": "Down URL Address", + "Down Stop": "Down Stop URL Address", + "Enable Night Vision": "Enable Night Vision URL Address", + "Disable Night Vision": "Disable Night Vision URL Address", + "Zoom Out Stop": "Zoom Out Stop URL Address", + "Zoom In Stop": "Zoom In Stop URL Address", + "Log Level": "Log Level", + "Save Log in SQL": "Save Log in SQL This can fill up quickly.", + "JPEG": "JPEG", + "Web Page": "Web Page", + "MJPEG": "MJPEG", + "H.264 / H.265 / H.265+": "H.264 / H.265 / H.265+", + "HLS (.m3u8)": "HLS (.m3u8)", + "MPEG-4 (.mp4 / .ts)": "MPEG-4 (.mp4 / .ts)", + "Shinobi Streamer": "Shinobi Streamer", + "Dashcam (Streamer v2)": "Dashcam (Streamer v2)", + "Local": "Local", + "Raw": "Raw", + "HTTP": "HTTP", + "HTTPS": "HTTPS", + "RTSP": "RTSP", + "RTMP": "RTMP", + "RTMPS": "RTMPS", + "UDP": "UDP", + "Auto": "Auto", + "TCP": "TCP", + "Base64 over Websocket": "Base64 over Websocket", + "Websocket": "Websocket", + "JPEG (Auto Enables JPEG API)": "JPEG (Auto Enables JPEG API)", + "HLS (includes Audio)": "HLS (includes Audio)", + "MPEG-DASH (includes Audio)": "MPEG-DASH (includes Audio)", + "libx264": "libx264", + "libx265": "libx265", + "copy": "copy", + "Audio": "Audio", + "Mute Audio": "Vypnout zvuk", + "No Audio": "Bez Zvuku", + "Popout Monitor on Event": "Vyskakovací okno při události", + "aac": "aac", + "ac3": "ac3", + "libmp3lame": "libmp3lame", + "No Rotation": "No Rotation", + "180 Degrees": "180 Degrees", + "90 Counter Clockwise and Vertical Flip (default)": "90 Counter Clockwise and Vertical Flip (default)", + "90 Clockwise": "90 Clockwise", + "90 Clockwise and Vertical Flip": "90 Clockwise and Vertical Flip", + "Top Right": "Top Right", + "Top Left": "Top Left", + "Bottom Right": "Bottom Right", + "Bottom Left": "Bottom Left", + "WebM (libvpx)": "WebM (libvpx)", + "Poseidon": "Poseidon", + "HEVC (H.265)": "HEVC (H.265)", + "MP4 (copy, libx264, libx265)": "MP4 (copy, libx264, libx265)", + "Default": "Default", + "libvpx (Default)": "libvpx (Default)", + "libvpx-vp9": "libvpx-vp9", + "libx264 (Default)": "libx264 (Default)", + "libvorbis (Default)": "libvorbis (Default)", + "libopus": "libopus", + "aac (Default)": "aac (Default)", + "Traditional (Watch-Only, Includes Buffer)": "Traditional (Watch-Only, Includes Buffer)", + "Hotswap Modes (Watch-Only)": "Hotswap Modes (Watch-Only)", + "Delete Motionless Videos (Record)": "Delete Motionless Videos (Record)", + "US": "US", + "EU": "EU", + "Silent": "Silent", + "Fatal": "Fatal", + "on Error": "on Error", + "All Warnings": "All Warnings", + "Warning": "Warning", + "Debug": "Debug", + "Export": "Exportovat", + "Import": "Importovat", + "Simple": "Jednoduché", + "Advanced": "Rozšířené", + "Error Connecting": "Chyba připojení", + "DB Lost.. Retrying..": "Database Lost.. Retrying..", + "Retrying...": "Nový pokus...", + "Filter Matches": "Filter Matches", + "FilterMatchesText1": "This filter has met conditions.", + "FilterMatchesText2": "záznamů nalezeno.", + "Executed": "Executed", + "Deleted": "Deleted", + "Query": "Query", + "Request": "Request", + "System": "System", + "Restart Core": "Restart Core", + "Restart CRON": "Restart CRON", + "Flush PM2 Logs": "Flush PM2 Logs", + "Filter ID": "Filter ID", + "Webdav Error": "Chyba WebDAV", + "WebdavErrorTextTryCreatingDir": "Cannot save. Trying to create directory.", + "WebdavErrorTextCreatingDir": "Nelze vytvořit složku.", + "File Not Exist": "Soubor neexistuje", + "No Videos Found": "Nenalezena žádná videa", + "FileNotExistText": "Cannot save non existant file. Something went wrong.", + "CameraNotRecordingText": "Settings may be incompatible. Check encoders. Restarting...", + "Camera is not running": "Kamera neběží", + "Camera is not recording": "Kamera nenahrává", + "Camera is not streaming": "Kamera nestreamuje", + "Process Started": "Proces spuštěn", + "Restarting Process": "Restartuji proces", + "Restarting": "Restartuji", + "Starting": "Startuje", + "Watching": "Sledování", + "Recording": "Nahrávání", + "Stopped": "Zastaveno", + "Died": "Selhalo", + "Restart": "Restart", + "Monitor Stopped": "Monitor zastaven", + "MonitorStoppedText": "Monitor session has been ordered to stop.", + "Monitor Idling": "Monitor Idling", + "MonitorIdlingText": "Monitor session has been ordered to idle.", + "NoMotionEmailText1": "Nezaznamenán pohyb na", + "NoMotionEmailText2": "Nebyl detekován žádný pohyb detected na kameře pro", + "Monitor Name": "Název monitoru", + "Mp4Frag": "Mp4Frag", + "Status Changed": "Stav byl změněn", + "Monitor is now Idle": "Monitor je nečinný", + "Monitor is now Disabled": "Monitor neaktivní", + "Monitor is now Watching": "Monitor sleduje", + "Monitor is now Recording": "Monitor nahrává", + "Space Used": "Využité místo", + "Processor": "Procesor", + "coProcessor": "koprocesor", + "coProcessor Stopped": "koprocesor zastaven", + "coProcessor Started": "koprocesor spuštěn", + "coProcessorTextStarted": "coProcessor has started for CPU only outputs.", + "coProcessorTextStopped": "coProcessor has ended.", + "Process Unexpected Exit": "Process Unexpected Exit", + "coProcess Unexpected Exit": "coProcess Unexpected Exit", + "Process Crashed for Monitor": "Process Crashed for Monitor", + "coProcess Crashed for Monitor": "coProcess Crashed for Monitor", + "FFmpegCantStart": "FFmpeg Couldn't Start", + "FFmpegCantStartText": "The recording engine for this camera could not start. There may be something wrong with your camera configuration. If there are any logs other than this one please post them in the Issues on Github.", + "JPEG Error": "JPEG Error", + "JPEGErrorText": "There was an issue getting data from your camera.", + "Fatal Maximum Reached": "Fatal Maximum Reached, Stopping Camera.", + "FatalMaximumReachedText": "JPEG Error was fatal.", + "Incorrect Settings Chosen": "Incorrect Settings Chosen", + "Can't Connect": "Can't Connect", + "Video Finished": "Video Finished", + "No Monitors Selected": "No Monitory Selected", + "monSavedButNotCopied": "Your monitor was saved but not copied to any other monitor.", + "No Monitor Found, Ignoring Request": "No Monitor Found, Ignoring Request", + "Event": "Event", + "CPU used by this stream": "CPU used by this stream", + "Detector Buffer": "Detector Buffer", + "EventText1": "Triggered a motion event at", + "EventText2": "Could not email image, file was not accessible", + "MailError": "MAIL ERROR : Could not send email, Check conf.json. Skipping any features relying on mailing.", + "updateKeyText1": "\"updateKey\" is missing from \"conf.json\", cannot do updates this way until you add it.", + "updateKeyText2": "\"updateKey\" is incorrect.", + "Control Error": "Control Error", + "Database row does not exist": "Database row does not exist", + "File Delete Error": "File Delete Error", + "List of Videos Delete Error": "List of Videos Delete Error", + "postDataBroken": "Check the format of the JSON. Ensure it is stringified and defined under 'data'", + "ControlErrorText1": "Control is not enabled", + "ControlErrorText2": "Check your connection details. You may need to point the Base URL at port 8000 or 80. Check your authentication info.", + "NotAuthorizedText1": "Not Authorized, Submit init command with \"auth\",\"ke\", and \"uid\"", + "Fields cannot be empty": "Fields cannot be empty", + "Email and Password fields cannot be empty": "Email and Password fields cannot be empty", + "AccountEditText1": "Could not edit. Refresh page if problem continues.", + "Not an Administrator Account": "Not an Administrator Account", + "superAdminText": "\"super.json\" does not exist. Please rename \"super.sample.json\" to \"super.json\".", + "Enter this code to proceed": "Enter this code to proceed", + "FactorAuthText1": "The code will only be active for 15 minutes. If you login again the timer will be reset to 15 minutes with the same code.", + "monitorEditText1": "Invalid Data, Check to see this is a valid import string.", + "monitorEditText2": "Invalid Details String. Check to see it is a JSON string and not a regular object being passed.", + "Monitor Updated by user": "Monitor Updated by user.", + "Monitor Added by user": "Monitor Added by user.", + "monitorGetText1": "incomplete request, remove last slash in URL or put acceptable value.", + "Monitor mode changed": "Monitor mode changed", + "Reset Timer": "Reset Timer", + "Monitor mode is already": "Monitor mode is already", + "Monitor or Key does not exist.": "Monitor or Key does not exist.", + "No Group with this key exists": "No Group with this key exists", + "Success": "Success", + "Trigger Successful": "Trigger Successful", + "No such file": "No such file", + "h265BrowserText1": "If you are trying to play an H.265 file, you may need to download it and open it in another application like VLC.", + "modifyVideoText1": "Method doesn't exist. Check to make sure that the last value of the URL is not blank.", + "CPU indicator will not work. Continuing...": "CPU indicator will not work. Continuing...", + "startUpText0": "Checking Disk Used..", + "startUpText1": "Completed Checking Disk Used.", + "startUpText2": "all users checked, wait to close open files and remove files over user limit", + "startUpText3": "waiting to give unfinished video check some time. 3 seconds.", + "startUpText4": "Starting Monitory... Please Wait...", + "startUpText5": "Shinobi is ready.", + "startUpText6": "Orphaned Videos Found and Inserted", + "Migrator": "Migrator", + "Thumbnail": "Thumbnail", + "Host Type": "Host Type", + "Edit": "Edit", + "Show Matrices": "Show Matrices", + "Show Matrix": "Show Matrix", + "No Monitor ID Present in Form": "No Monitor ID Present in Form", + "State Configuration has no monitory associated": "State Configuration has no monitory associated", + "State Configuration Not Found": "State Configuration Not Found", + "Inserted State Configuration": "Inserted State Configuration", + "Edited State Configuration": "Edited State Configuration", + "Deleted State Configuration": "Deleted State Configuration", + "Schedule Configuration Not Found": "Schedule Configuration Not Found", + "Inserted Schedule Configuration": "Inserted Schedule Configuration", + "Edited Schedule Configuration": "Edited Schedule Configuration", + "Deleted Schedule Configuration": "Deleted Schedule Configuration", + "Dashboard Language": "Jazyk prostředí", + "Form Data Not Found": "Form Data Not Found", + "File Not Found": "File Not Found", + "File Not Found in Filesystem": "File Not Found in Filesystem", + "File Not Found in Database": "File Not Found in Database", + "No Monitor Exists with this ID.": "No Monitor Exists with this ID.", + "Cannot watch a monitor that isn't running.": "Cannot watch a monitor that isn't running.", + "Not Permitted": "Not Permitted", + "notPermitted1": "This action is not permitted by the administrator of your account.'", + "Not Authorized": "Not Authorized", + "Generate Subtitles": "Generate Subtitles", + "Video Limit":"Video Limit", + "Preview":"Preview", + "Websocket Connected":"Websocket Connected", + "Websocket Disconnected":"Websocket Disconnected", + "Token":"Token", + "Channel ID":"Channel ID", + "New Authentication Token":"New Authentication Token", + "All Logs":"All Logs", + "For Group":"For Group", + "Basic Authentication":"Basic Authentication", + "Superuser Logs":"Superuser Logs", + "Authentication Failed":"Authentication Failed", + "Max Number of Cameras":"Max Number of Cameras", + "Can edit Max Storage":"Can edit Max Storage", + "Can edit Max Days":"Can edit Max Days", + "in Days":"in Days", + "Can edit how long to keep Logs":"Can edit how long to keep Logs", + "Can use Admin Panel":"Can use Admin Panel", + "Can use Discord Bot":"Can use Discord Bot", + "Can use WebDAV":"Can use WebDAV", + "Can use Amazon S3":"Can use Amazon S3", + "Can use LDAP":"Can use LDAP", + "Can View Logs":"Can View Logs", + "Can edit how long to keep Events":"Can edit how long to keep Events", + "Leave blank for unlimited":"Leave blank for unlimited", + "Limited":"Limited", + "All Privileges":"All Privileges", + "LDAP":"LDAP", + "LDAP Success":"LDAP Success", + "LDAP User Authenticated":"LDAP User Authenticated", + "LDAP User is New":"LDAP User is New", + "Creating New Account":"Creating New Account", + "bindDN":"bindDN", + "Bind Credentials":"Bind Credentials (Password)", + "Search Base":"Search Base", + "Configuration":"Nastavení", + "Blank for No Change":"Blank for No Change", + "Pop":"Pop", + "Recording FPS Change on Start":"Recording FPS Change on Start", + "Save Frames to Events":"Save Frames to Events", + "Search Filter":"Search Filter", + "h264_cuvid": "H.264 CUVID", + "hevc_cuvid": "H.265 CUVID", + "mjpeg_cuvid": "MJPEG CUVID", + "mpeg4_cuvid": "MPEG4 CUVID", + "h264_qsv": "H.264 (Quick Sync Video)", + "hevc_qsv": "H.265 (Quick Sync Video)", + "vp8_qsv": "VP8 (Quick Sync Video)", + "mpeg2_qsv": "MPEG2 (Quick Sync Video)", + "h264_mmal": "H.264 (Raspberry Pi)", + "mpeg2_mmal": "MPEG-2 (Raspberry Pi)", + "mpeg4_mmal": "MPEG-4 (Raspberry Pi)", + "h264_omx": "H.264 openMAX (Raspberry Pi)", + "h264_vaapi": "H.264 VA-API (Intel HW Accel)", + "h264_nvenc": "H.264 NVENC (NVIDIA HW Accel)", + "hevc_nvenc": "H.265 NVENC (NVIDIA HW Accel)", + "hevc_vaapi": "H.265 VA-API (Intel HW Accel)", + "vp8_cuvid": "VP8 NVENC (NVIDIA HW Accel)", + "vp9_cuvid": "VP9 NVENC (NVIDIA HW Accel)", + "hwaccel": "Acceleration Engine", + "hwaccel_vcodec": "Video Decoder", + "hwaccel_device": "HWAccel Device", + "Get Logs to Client": "Get Logs to Client", + "Hardware Accelerated": "Hardware Accelerated", + "Accelerator": "Accelerator", + "drm": "DRM object sharing", + "qsv": "qsv", + "dxva2": "dxva2 (DirectX Video, Windows)", + "vdpau": "vdpau", + "vaapi": "vaapi (VA-API)", + "vda": "vda (Apple VDA Hardware Acceleration)", + "videotoolbox": "videotoolbox", + "cuvid": "cuvid (NVIDIA NVENC)", + "Main": "Main", + "Storage Location": "Storage Location", + "Recommended": "Recommended", + "Please Wait for Completion": "Please Wait for Completion, Depending on the number of files selected this may take some time. Refresh to check again.", + "flv": "flv", + "FLV": "FLV", + "FLV Stream Type": "FLV Stream Type", + "Link Shinobi": "Link Shinobi", + "Show Stream HUD":"Show Stream HUD", + "Call Method":"Call Method", + "Gender":"Gender", + "Emotion":"Emotion", + "Age":"Age", + "Object":"Object", + "Uniform":"Uniform", + "Pose":"Pose", + "Male":"Male", + "Female":"Female", + "Channel":"Channel", + "Stream Key":"Stream Key", + "Server URL":"Server URL", + "Video Bit Rate":"Video Bit Rate", + "Audio Bit Rate":"Audio Bit Rate", + "RTMP Stream Flags":"RTMP Stream Flags", + "RTMP Stream":"RTMP Stream", + "Stream Channel":"Stream Channel", + "Confidence":"Confidence", + "Trainer Engine":"Trainer Engine", + "Train":"Train", + "TrainConfirm":"Are you sure you want to begin training? This can take more than 12 hours with over 500 images. This will consume a large amount of resources, like RAM or CPU.", + "Batch":"Batch", + "Subdivision":"Subdivision", + "Map":"Map", + "Add Map":"Add Map", + "Add Input Feed":"Add Input Feed", + "Add Channel":"Add Channel", + "Automatic":"Automatic", + "Max Latency":"Max Latency", + "Loop Stream":"Loop Stream", + "Object Tag":"Object Tag", + "Noise Filter":"Noise Filter", + "Noise Filter Range":"Noise Filter Range", + "TV Channel":"TV Channel", + "Channel ID":"Channel ID", + "TV Channel ID":"TV Channel ID", + "TV Channel Group":"TV Channel Group", + "Emotion Average":"Emotion Average", + "Require Object to be in Region":"Require Object to be in Region", + "Show Regions of Interest":"Show Regions of Interest", + "Confidence of Detection":"Confidence of Detection", + "Edit Selected":"Edit Selected", + "Copy Settings":"Copy Settings", + "Copy to Settings":"Copy to Settings", + "Copy Group Settings":"Copy Group Settings", + "Copy Connection Settings":"Copy Connection Settings", + "Copy Custom Settings":"Copy Custom Settings", + "Copy Logging Settings":"Copy Logging Settings", + "Copy JPEG API Settings":"Copy JPEG API Settings", + "Copy Input Settings":"Copy Input Settings", + "Copy Stream Settings":"Copy Stream Settings", + "Copy Stream Channel Settings":"Copy Stream Channel Settings", + "Copy Recording Settings":"Copy Recording Settings", + "Copy Detector Settings":"Copy Detector Settings", + "Monitors to Copy to":"Monitory to Copy to", + "Notification Sound":"Zvuk notifikace", + "Alert Sound":"Zvuk upozornění", + "Alert Sound Delay":"Zpoždění zvukového upozornění", + "powerVideoEventLimit":"You have set a high event limit. Are you sure you want to make this request?", + "There are no monitors that you can view with this account.":"There are no monitory that you can view with this account.", + "Use Built-In":"Použít vestavěné" +} diff --git a/languages/en_CA.json b/languages/en_CA.json index 896b779..fed1739 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -34,6 +34,10 @@ "API": "API", "ONVIF": "ONVIF", "FFprobe": "Probe", + "Monitor States": "Monitor States", + "Schedule": "Schedule", + "Schedules": "Schedules", + "Monitor States and Schedules": "Monitor States and Schedules", "Filters": "Filters", "Full URL Path": "Full URL Path", "Logs": "Logs", @@ -44,6 +48,8 @@ "Motion GUI": "Motion GUI", "Motion": "Motion", "Global Detector Settings": "Global Detector Settings", + "Trigger Group to Record": "Trigger Group to Record", + "Trigger Camera Groups": "Trigger Camera Groups", "Motion Detection": "Motion Detection", "Object Detection": "Object Detection", "JPEG Mode": "JPEG Mode", @@ -116,6 +122,8 @@ "Can View Videos and Events": "Can View Videos and Events", "Can Delete Videos and Events": "Can Delete Videos and Events", "Saved Filters": "Saved Filters", + "Saved Presets": "Saved Presets", + "Saved Schedules": "Saved Schedules", "Filter Name": "Filter Name", "Find Where": "Find Where", "Reason": "Reason", @@ -168,6 +176,15 @@ "Motion Meter": "Motion Meter", "FFmpegTip": "FFprobe is a simple multimedia streams analyzer. You can use it to output all kinds of information about an input including duration, frame rate, frame size, etc.", "Complete Stream URL": "Complete Stream URL", + "Primary Input": "Primary Input", + "All streams in first feed": "All streams in first feed", + "First stream in feed": "First stream in feed", + "Second stream in feed": "Second stream in feed", + "Video streams only": "Video streams only", + "Video stream only from first feed": "Video stream only from first feed", + "Audio streams only": "Audio streams only", + "Audio stream only from first feed": "Audio stream only from first feed", + "ONVIF Port": "ONVIF Port", "ONVIF Scanner": "ONVIF Scanner", "ONVIFEventsNotAvailable": "ONVIF Events not Available", "ONVIFnotCompliantProfileT": "Camera is not ONVIF Profile T Compliant", @@ -198,18 +215,29 @@ "Number of Days to keep": "Number of Days to keep", "Monitor Groups": "Monitor Groups", "Group Name": "Group Name", + "Show Thumbnails in Video List": "Show Thumbnails in Video List", "WebDAV": "WebDAV", "Backblaze B2": "Backblaze B2", "Backblaze Error": "Backblaze Error", + "SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)", + "SFTP Error": "SFTP Error", + "SFTP": "SFTP", "Could not create Bucket.": "Could not create Bucket.", "Amazon S3": "Amazon S3", + "Wasabi Hot Cloud Storage": "Wasabi Hot Cloud Storage", + "Database": "Database", + "Database Not Found": "Database Not Found", + "User Not Found": "User Not Found", "Save Links to Database": "Save Links to Database", + "Upload File": "Upload File", "Bucket": "Bucket", "Region": "Region", "Use Global Amazon S3 Video Storage": "Use Global Amazon S3 Video Storage", + "Use Global Wasabi Hot Cloud Storage Video Storage": "Use Global Wasabi Hot Cloud Storage Video Storage", "Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Video Storage", "Use Global WebDAV Video Storage": "Use Global WebDAV Video Storage", "Amazon S3 Upload Error": "Amazon S3 Upload Error", + "Wasabi Hot Cloud Storage Upload Error": "Wasabi Hot Cloud Storage Upload Error", "accountId": "Account ID", "applicationKey": "Application Key", "aws_accessKeyId": "Access Key Id", @@ -250,13 +278,17 @@ "Set to Watch Only": "Set to Watch Only", "Save as": "Save as", "Add New": "Add New", + "Merge and Download": "Merge and Download", "Zip and Download": "Zip and Download", + "Merge Selected Videos": "Merge Selected Videos", "Export Selected Videos": "Export Selected Videos", "Delete Selected Videos": "Delete Selected Videos", "DeleteSelectedVideosMsg": "Do you want to delete these videos? You cannot recover them.", "ExportSelectedVideosMsg": "Do you want to export these videos? It may take some time to zip and download.", + "MergeSelectedVideosMsg": "Do you want to merge these videos? It may take some time to merge and download. The moment the connection is closed the file will be deleted. Ensure you keep the browser open until it is complete.", "clientStreamFailedattemptingReconnect": "Client side ctream check failed, attempting reconnect.", "Export Video": "Export Video", + "Merge Video": "Merge Video", "Delete Filter": "Delete Filter", "confirmDeleteFilter": "Do you want to delete this filter? You cannot recover it.", "Fix Video": "Fix Video", @@ -283,9 +315,15 @@ "DeleteMonitorText": "Do you want to delete this monitor? You cannot recover it. You can choose for the files to remain in the filesystem. If you choose to recreate a monitor with the same ID the videos and events will become visible in the dashboard.", "DeleteMonitorsText": "Do you want to delete these monitors? You cannot recover them. You can choose to keep the files for these IDs in the filesystem. If you choose to recreate a monitor with one of the IDs the videos and events will become visible in the dashboard.", "Invalid JSON": "Invalid JSON", + "Invalid Data": "Invalid Data", + "Name cannot be empty.": "Name cannot be empty.", + "Start Time cannot be empty.": "Start Time cannot be empty.", + "Must be atleast one row": "Must be atleast one row", "InvalidJSONText": "Please ensure this is a valid JSON string for Shinobi monitor configuration.", "Passwords don't match": "Passwords don't match", "Email address is in use.": "Email address is in use.", + "Group Key is in use.": "Group Key is in use.", + "Create Sub-Accounts at /admin": "Create Sub-Accounts at /admin", "No Events found for this video": "No Events found for this video", "Video and Time Span (Minutes)": "Video and Time Span (Minutes)", "Video Length (minutes) and Motion Count per video": "Video Length (minutes) and Motion Count per video", @@ -324,16 +362,23 @@ "Filter for Objects only": "Filter for Objects only", "Custom": "Custom", "Detector": "Detector", + "Audio Detector": "Audio Detector", + "Audio Detection": "Audio Detection", + "Minimum dB": "Minimum dB", + "Maximum dB": "Maximum dB", "Connected": "Connected", "Not Saved": "Not Saved", "Not Connected": "Not Connected", - "Lisence Plate Detector": "Lisence Plate Detector", + "License Plate Detector": "License Plate Detector", "OpenCV Cascades": "OpenCV Cascades", "Refresh List of Cascades": "Refresh List of Cascades", "\"No Motion\" Detector": "\"No Motion\" Detector", "Control": "Control", "Grouping": "Grouping Add groups in Settings", "Detector Grouping": "Detector Grouping Add groups in Settings", + "Detector Recording Process Exited Prematurely. Restarting.": "Detector Recording Process Exited Prematurely. Restarting.", + "Detector Recording Complete": "Detector Recording Complete", + "Clear Recorder Process": "Clear Recorder Process", "Logging": "Logging", "IdentityText1": "This is how the system will identify the data for this stream. You cannot change the Monitor ID once you have pressed save. If you want you can make the Monitor ID more human readable before you continue.", "IdentityText2": "You can duplicate a monitor by modifying the Monitor ID then pressing save. You cannot use the ID of a monitor that already exists or it will save over that monitor's database information.", @@ -367,6 +412,7 @@ "HLS Preset": "Preset Template", "HLS List Size": "List Size", "Traditional Recording": "Traditional Recording", + "Recorded Buffer": "Recorded Buffer", "Buffer Preview": "Buffer Preview", "HLS Start Number": "HLS Start Number", "HLS Live Start Index": "HLS Live Start Index", @@ -391,8 +437,19 @@ "Image Width": "Image Width", "Image Height": "Image Height", "Record File Type": "Record File Type", + "Notification Video Length": "Notification Video Length", "Video Codec": "Video Codec", + "Delete Monitor States Preset": "Delete Monitor States Preset", + "Delete Monitor State?": "Delete Monitor State", + "deleteMonitorStateText1": "Do you want to delete this Monitor States Preset?", + "deleteMonitorStateText2": "Do you want to delete this Monitor's Preset?", + "Search Images": "Search Images", + "Launch in New Window": "Launch in New Window", "Preset": "Preset", + "Presets": "Presets", + "possibleInternalError": "Possible Internal Error", + "sizePurgeLockedText": "The Size Purge Lock (deleteOverMax) appears to have failed to unlock. Unlocking now...", + "Use coProcessor": "Use coProcessor", "Audio Codec": "Audio Codec", "Video Record Rate": "Video Record Rate (FPS)", "Record Width": "Record Width", @@ -407,6 +464,7 @@ "Stream to YouTube": "Stream to YouTube", "Stream to YouTube Flags": "Stream to YouTube Flags", "Recording Flags": "Recording Flags", + "Traditional Recording Flags": "Traditional Recording Flags", "Output Method": "Output Method", "Webhook": "Webhook", "Event Webhook Error": "Event Webhook Error", @@ -418,10 +476,13 @@ "Save Events to SQL": "Save Events to SQL", "Email on Trigger": "Email on Trigger Emails go to the main account holder's login address.", "Attach Video Clip": "Attach Video Clip", + "Error While Decoding": "Error While Decoding", + "ErrorWhileDecodingText": "Your hardware may have an unstable connection to the network. Check your network connections.", "Discord": "Discord", "Discord Alert on Trigger": "Discord Alert on Trigger", "Allow Next Email": "Allow Next Email in Minutes", "Allow Next Discord Alert": "Allow Next Discord Alert in Minutes", + "DiscordLoggedIn": "Discord Bot Authenticated", "DiscordFailedText": "Sending to Discord Failed", "DiscordErrorText": "Sending to Discord caused an Error", "DiscordNotEnabledText": "Discord Bot Not Enabled, Enable it in your Account Settings.", @@ -633,6 +694,7 @@ "Monitor mode is already": "Monitor mode is already", "Monitor or Key does not exist.": "Monitor or Key does not exist.", "No Group with this key exists": "No Group with this key exists", + "Success": "Success", "Trigger Successful": "Trigger Successful", "No such file": "No such file", "h265BrowserText1": "If you are trying to play an H.265 file, you may need to download it and open it in another application like VLC.", @@ -657,6 +719,10 @@ "Inserted State Configuration": "Inserted State Configuration", "Edited State Configuration": "Edited State Configuration", "Deleted State Configuration": "Deleted State Configuration", + "Schedule Configuration Not Found": "Schedule Configuration Not Found", + "Inserted Schedule Configuration": "Inserted Schedule Configuration", + "Edited Schedule Configuration": "Edited Schedule Configuration", + "Deleted Schedule Configuration": "Deleted Schedule Configuration", "Dashboard Language": "Dashboard Language", "Form Data Not Found": "Form Data Not Found", "File Not Found": "File Not Found", @@ -672,6 +738,7 @@ "Preview":"Preview", "Websocket Connected":"Websocket Connected", "Websocket Disconnected":"Websocket Disconnected", + "Videos Merge":"Videos Merge", "Token":"Token", "Channel ID":"Channel ID", "New Authentication Token":"New Authentication Token", @@ -689,10 +756,13 @@ "Can use Discord Bot":"Can use Discord Bot", "Can use WebDAV":"Can use WebDAV", "Can use Amazon S3":"Can use Amazon S3", + "Can use SFTP":"Can use SFTP", + "Can use Wasabi Hot Cloud Storage":"Can use Wasabi Hot Cloud Storage", "Can use LDAP":"Can use LDAP", "Can View Logs":"Can View Logs", "Can edit how long to keep Events":"Can edit how long to keep Events", "Leave blank for unlimited":"Leave blank for unlimited", + "privateKey":"Private Key", "Limited":"Limited", "All Privileges":"All Privileges", "LDAP":"LDAP", @@ -768,6 +838,11 @@ "RTMP Stream":"RTMP Stream", "Stream Channel":"Stream Channel", "Confidence":"Confidence", + "Trainer Engine":"Trainer Engine", + "Train":"Train", + "TrainConfirm":"Are you sure you want to begin training? This can take more than 12 hours with over 500 images. This will consume a large amount of resources, like RAM or CPU.", + "Batch":"Batch", + "Subdivision":"Subdivision", "Map":"Map", "Add Map":"Add Map", "Add Input Feed":"Add Input Feed", @@ -783,6 +858,7 @@ "TV Channel ID":"TV Channel ID", "TV Channel Group":"TV Channel Group", "Emotion Average":"Emotion Average", + "Require Object to be in Region":"Require Object to be in Region", "Show Regions of Interest":"Show Regions of Interest", "Confidence of Detection":"Confidence of Detection", "Edit Selected":"Edit Selected", diff --git a/languages/fr.json b/languages/fr.json index f8ce5eb..b6846d6 100644 --- a/languages/fr.json +++ b/languages/fr.json @@ -1,10 +1,10 @@ { - "\"No Motion\" Detector": "\"Aucun Mouvement\" Détecteur De", - "# of Allow MJPEG Clients": "# of Allow MJPEG Clients 0 for infinite", + "\"No Motion\" Detector": "\"Aucun Mouvement\" Détecteur", + "# of Allow MJPEG Clients": "# d'autoriser les clients MJPEG 0 pour l'infini", "180 Degrees": "180 Degrés", - "2-Factor Authentication": "Une Authentification À 2 Facteurs", + "2-Factor Authentication": "Authentification À 2 Facteurs", "90 Clockwise": "90 dans le sens des Aiguilles", - "90 Clockwise and Vertical Flip": "90 vers la gauche et Vertical Flip", + "90 Clockwise and Vertical Flip": "90 vers la gauche et rotation Vertical", "90 Counter Clockwise and Vertical Flip (default)": "90 dans le sens antihoraire et la rotation Verticale (par défaut)", "API": "API", "API Key": "Clé API", @@ -15,30 +15,30 @@ "APIKeyDeletedText": "Clé a été supprimé. Elle ne fonctionnera plus.", "ASC": "ASC", "Accelerator": "Accélérateur", - "Account Info": "Les Informations De Compte", + "Account Info": "Les Informations Du Compte", "AccountEditText1": "Ne pouvait pas modifier. Actualiser la page si le problème persiste.", "Accounts": "Comptes", "Action for Selected": "Action Sélectionnés", "Add": "Ajouter", "Add Channel": "Ajouter Un Canal", - "Add Input Feed": "Ajoutez D'Entrée D'Alimentation", + "Add Input Feed": "Ajouter un flux d'entrée", "Add Map": "Ajouter La Carte", "Add Monitor": "Ajouter Moniteur", - "Add New": "Ajouter De Nouveaux", + "Add New": "Ajouter Nouveau", "Admin": "Admin", "Advanced": "Avancé", "Again": "Encore", - "Age": "L'âge", + "Age": "Age", "All Logs": "Tous Les Journaux", "All Monitors": "Tous Les Moniteurs", "All Monitors and Privileges": "Tous les Moniteurs et Privilèges", "All Privileges": "Tous Les Privilèges", "All Warnings": "Tous Les Avertissements", - "Allow Next Command": "Permettre Prochaine Commande en quelques Minutes", - "Allow Next Email": "Permettre Prochain Email en quelques Minutes", - "Allow Next Trigger": "Permettre à Côté de Déclenchement en Millisecondes", - "Allowed IPs": "Permis IPs", - "Analyzation Duration": "Analyzation Durée", + "Allow Next Command": "Autoriser la commande suivante en quelques Minutes", + "Allow Next Email": "Autoriser le prochain Email en quelques Minutes", + "Allow Next Trigger": "Autoriser le déclencheur suivant en Millisecondes", + "Allowed IPs": "IPs autorisés", + "Analyzation Duration": "Durée d'analyse", "Archive": "Archive", "Are you sure?": "Êtes-vous sûr?", "Audio Bit Rate": "Le Débit Binaire Audio", @@ -51,49 +51,49 @@ "Base64 over Websocket": "Base64 sur Websocket", "Basic Authentication": "L'Authentification De Base", "Bind Credentials": "Lier Les Informations D'Identification (Mot De Passe)", - "Blank for No Change": "Vide pour Pas Changer", + "Blank for No Change": "Vide pour aucun changement", "Bottom Left": "En Bas À Gauche", "Bottom Right": "En Bas À Droite", - "Browser Console Log": "Le Navigateur De La Console De Log", - "Buffer Preview": "Tampon Aperçu", + "Browser Console Log": "Journal de la console du navigateur", + "Buffer Preview": "Aperçu de la mémoire tampon", "CPU": "CPU", - "CPU indicator will not work. Continuing...": "CPU indicateur ne fonctionnera pas. Continue...", + "CPU indicator will not work. Continuing...": "L'indicateur du CPU ne fonctionnera pas. En continuant ...", "CSS": "CSS Style de votre tableau de bord.", "Calendar": "Calendrier", - "Call Method": "Appel De La Méthode", + "Call Method": "Méthode d'appel", "Camera Password": "Mot De Passe De La Caméra", "Camera Username": "Nom D'Utilisateur De La Caméra", - "Camera is not recording": "Appareil photo n'est pas de l'enregistrement", + "Camera is not recording": "La caméra n'enregistre pas", "Camera is not streaming": "La caméra n'est pas en streaming", "CameraNotRecordingText": "Les paramètres peuvent être incompatibles. Vérifier les encodeurs. Le redémarrage...", "Can Authenticate Websocket": "Peut Authentifier Websocket", - "Can Control Monitors": "Peut Moniteurs De Contrôle", - "Can Delete Videos": "Pouvez Supprimer Des Vidéos", - "Can Delete Videos and Events": "Pouvez Supprimer des Vidéos et des Événements", - "Can Edit Monitor": "Pouvez Modifier Moniteur", - "Can Get Logs": "Pouvez Obtenir Les Journaux", - "Can Get Monitors": "Pouvez Obtenir Des Moniteurs", - "Can View Monitor": "Peut Moniteur De Vue", + "Can Control Monitors": "Peut contrôler les moniteurs", + "Can Delete Videos": "Peut supprimer des vidéos", + "Can Delete Videos and Events": "Peut supprimer des vidéos et des événements", + "Can Edit Monitor": "Peut modifier le moniteur", + "Can Get Logs": "Peut Obtenir Les Journaux", + "Can Get Monitors": "Peut Obtenir Des Moniteurs", + "Can View Monitor": "Peut afficher le moniteur", "Can View Snapshots": "Peut Afficher Des Instantanés", "Can View Streams": "Peut Afficher Des Flux", "Can View Videos": "Peut Visionner Des Vidéos", "Can View Videos and Events": "Pouvez Visualiser des Vidéos et des Événements", - "Can edit Max Days": "Pouvez éditer Max Jours", - "Can edit Max Storage": "Pouvez éditer Max de Stockage", - "Can edit how long to keep Events": "Pouvez modifier la durée de conservation des Événements", - "Can edit how long to keep Logs": "Pouvez modifier la durée de conservation des Journaux d'", - "Can use Admin Panel": "Pouvez utiliser le Panneau d'administration", + "Can edit Max Days": "Peut éditer le maximum de Jours", + "Can edit Max Storage": "Peut éditer maximum de Stockage", + "Can edit how long to keep Events": "Peut modifier la durée de conservation des Événements", + "Can edit how long to keep Logs": "Peut modifier la durée de conservation des Journaux d'", + "Can use Admin Panel": "Peut utiliser le Panneau d'administration", "Can use LDAP": "Peut utiliser le protocole LDAP", - "Can use WebDAV": "Pouvez utiliser WebDAV", + "Can use WebDAV": "Peut utiliser WebDAV", "Can't Connect": "Ne peut pas se Connecter", "Cannot watch a monitor that isn't running.": "Impossible de regarder un moniteur qui n'est pas en cours d'exécution.", "Center": "Centre d' Adresse URL", "Channel": "Canal", "Channel ID": "ID de canal", - "Chat on Discord": "Chat sur la Discorde", + "Chat on Discord": "Discuter sur Discord", "Check": "Vérifier", - "Check Signal Interval": "Vérifier le Signal de l'Intervalle en Minutes", - "Check for Motion First": "Vérifier le Mouvement de la Première", + "Check Signal Interval": "Vérifier l'intervalle du signal en Minutes", + "Check for Motion First": "Vérifier le Mouvement d'abord", "Close": "Fermer", "Closed": "Fermé", "Command": "Commande", @@ -107,42 +107,42 @@ "Connection": "Connexion", "Connection Type": "Type De Connexion", "Control": "Contrôle", - "Control Error": "Erreur De Contrôle De La", + "Control Error": "Erreur De Contrôle", "ControlErrorText1": "Le contrôle n'est pas activé", "Controllable": "Contrôlable", - "Copy Connection Settings": "Copie Des Paramètres De Connexion", - "Copy Custom Settings": "Copie Des Paramètres Personnalisés", - "Copy Detector Settings": "Copie Réglages Du Détecteur", - "Copy Group Settings": "Copier Les Paramètres De Groupe", + "Copy Connection Settings": "Copier les paramètres de connexion", + "Copy Custom Settings": "Copier les paramètres personnalisés", + "Copy Detector Settings": "Copier les réglages de Détection", + "Copy Group Settings": "Copier Les Paramètres Du Groupe", "Copy Input Settings": "Copier Les Paramètres D'Entrée", "Copy Logging Settings": "Copie Des Paramètres De Journalisation", "Copy Recording Settings": "Copier Les Paramètres D'Enregistrement", "Copy Settings": "Paramètres De Copie", - "Copy Stream Channel Settings": "Copie De Flux De Paramètres De Canal", + "Copy Stream Channel Settings": "Copier les réglages du canal", "Copy Stream Settings": "Copier Les Paramètres De Flux", - "Copy to Settings": "Copie des Paramètres de", - "Country of Plates": "Pays de Plaques", - "Counts of Motion": "Comtes de Mouvement", - "Creating New Account": "La Création D'Un Nouveau Compte", + "Copy to Settings": "Copier dans les paramètres", + "Country of Plates": "Pays de Plaques d'immatriculation", + "Counts of Motion": "Comptes de mouvement", + "Creating New Account": "Créer un nouveau compte", "Current": "Actuel", - "Currently viewing": "Visualisez actuellement", + "Currently viewing": "En cours de visionnage", "Custom": "Personnalisé", "Custom Base URL": "Base d'URL personnalisée Laissez le champ vide pour utiliser l'Hôte de l'URL", "DB Lost.. Retrying..": "Base De Données Perdues.. Réessayer..", "DESC": "DESC", "Dashboard": "Tableau de bord", - "Dashboard Language": "Langue Du Tableau De Bord", + "Dashboard Language": "Langue du Tableau De Bord", "Dashcam": "Dashcam", "Dashcam (Streamer v2)": "Dashcam (Streamer v2)", - "Date Range": "La Plage De Dates", + "Date Range": "Plage De Dates", "Debug": "Debug", "Default": "Par défaut", "Delete": "Supprimer", - "Delete Filter": "Supprimer Le Filtre", - "Delete Matches": "Supprimer Les Matchs", - "Delete Monitor": "Supprimer Moniteur", - "Delete Motionless Video": "Supprimer Immobile Vidéo", - "Delete Motionless Videos (Record)": "Supprimer Immobile Vidéos (Enregistrement)", + "Delete Filter": "Supprimer le Filtre", + "Delete Matches": "Supprimer les correspondances", + "Delete Monitor": "Supprimer le Moniteur", + "Delete Motionless Video": "Supprimer la vidéo immobile", + "Delete Motionless Videos (Record)": "Supprimer des vidéos immobiles (Enregistrer)", "Delete Selected Videos": "Supprimer La Sélection Des Vidéos", "Delete Video": "Supprimer La Vidéo", "Delete selected": "Supprimer la sélection", @@ -152,30 +152,30 @@ "DeleteVideoMsg": "Voulez-vous supprimer cette vidéo? Vous ne pouvez pas le récupérer.", "Deleted": "Supprimé", "Detect Objects": "Détecter les Objets Voir ci-dessous", - "Detector": "Détecteur de", + "Detector": "Détecteur", "Detector Buffer": "Détecteur De Tampon", "Detector Flags": "Détecteur De Drapeaux", - "Detector Grouping": "Détecteur de Groupement d'Ajouter des groupes de Paramètres", - "Detector Rate": "Détecteur de Taux (FPS)", + "Detector Grouping": "Groupement de détecteurs d'Ajouter des groupes de Paramètres", + "Detector Rate": "Taux de détecteur (FPS)", "DetectorText": "

Lorsque les zones Largeur et Hauteur sont affichés, vous devriez les mettre à 640x480 ou ci-dessous. Cela permettra d'optimiser la vitesse de lecture d'images.

", - "Disable Night Vision": "Désactiver la Vision de Nuit Adresse URL", - "Disable Nightvision": "Désactiver Nightvision", + "Disable Night Vision": "Désactiver la vision nocturne Adresse URL", + "Disable Nightvision": "Désactiver la vision nocturne", "Disabled": "Désactivé", - "Documentation": "La Documentation", + "Documentation": "Documentation", "Don't show this anymore": "Ne pas afficher plus", - "Double Quote Directory": "Devis Double Répertoire de Certains répertoires sont séparés par des espaces. L'utilisation de ce peut se bloquer certains appareils photo.", + "Double Quote Directory": "Devis Double Répertoire Certains répertoires sont séparés par des espaces. L'utilisation de ce peut se bloquer certaines cameras.", "Down": "Bas Adresse URL", - "Down Stop": "Vers le bas Arrêter Adresse URL", + "Down Stop": "Arrêt bas Adresse URL", "Download": "Télécharger", - "EU": "L'UE", + "EU": "UE", "Edit": "Modifier", - "Edit Selected": "Modifier Les", + "Edit Selected": "Modifier la sélection", "Email": "E-mail", - "Email Details": "E-Mail Les Détails", + "Email Details": "Détails de l'e-mail", "Email on No Motion": "E-mail sur \"Aucun Mouvement\"", "Email on Trigger": "E-mail sur la Gâchette e-Mails passent à la principale du titulaire de compte de connexion l'adresse.", "Emotion": "L'émotion", - "Emotion Average": "L'Émotion De La Moyenne", + "Emotion Average": "Emotion Moyenne", "Enable": "Activer", "Enable Night Vision": "Activer la Vision Nocturne Adresse URL", "Enable Nightvision": "Activer La Vision Nocturne", @@ -184,13 +184,13 @@ "End Time": "Heure De Fin", "Ended": "Terminé", "Enlarge": "Agrandir", - "Enter this code to proceed": "Entrez ce code pour procéder", + "Enter this code to proceed": "Entrez ce code pour continuer", "Equal to": "Égal à", "Error Connecting": "Erreur Lors De La Connexion", "Event": "L'événement", - "Event Limit": "Cas Limite", - "EventText1": "Déclenché un mouvement à", - "EventText2": "Pourrait pas l'email de l'image, le fichier n'est pas accessible", + "Event Limit": "Limite d'événement", + "EventText1": "Mouvement détectée à", + "EventText2": "Impossible d'envoyer l'image par l'email, le fichier n'est pas accessible", "Events": "Les événements", "Example": "Exemple", "Execute Command": "Exécuter La Commande", @@ -198,22 +198,22 @@ "Export": "L'exportation", "FFmpegCantStart": "FFmpeg ne Pouvait pas Commencer", "FFmpegCantStartText": "Le moteur d'enregistrement pour cette caméra n'a pas pu démarrer. Il y a peut être quelque chose de mal avec votre configuration de la caméra. S'il y a des journaux autres que celui-ci, veuillez les poster dans les Questions sur Github.", - "FFmpegTip": "FFprobe est un simple flux multimédias de l'analyseur. Vous pouvez l'utiliser pour afficher toutes sortes d'informations sur les intrants, y compris la durée, taux d'armature, taille de l'image, etc.", + "FFmpegTip": "FFprobe est un analyseur de flux multimédia simple. Vous pouvez l’utiliser pour générer toutes sortes d’informations sur une entrée, notamment la durée, la cadence, la taille, etc.", "FFprobe": "Sonde", "FLV": "FLV", "FLV Stream Type": "FLV Type de Flux", - "FactorAuthText1": "Le code ne sera actif pendant 15 minutes. Si vous vous connectez de nouveau la minuterie est réinitialisée à 15 minutes avec le même code.", - "Fatal": "Fatale", - "Fatal Maximum Reached": "Fatale Maximum Atteint, L'Arrêt De La Caméra.", - "FatalMaximumReachedText": "JPEG Erreur a été fatale.", - "Feed-in Image Height": "De rachat à la Hauteur de l'Image", - "Feed-in Image Width": "Alimentation sur la Largeur de l'Image", + "FactorAuthText1": "Le code ne sera actif que pendant 15 minutes. Si vous vous reconnectez, le chronomètre sera réinitialisé à 15 minutes avec le même code.", + "Fatal": "Fatal", + "Fatal Maximum Reached": "Maximum fatal atteint, L'Arrêt De La Caméra.", + "FatalMaximumReachedText": "Erreur JPEG fatale.", + "Feed-in Image Height": "Hauteur de l'image d'alimentation", + "Feed-in Image Width": "Largeur de l'image d'alimentation", "Female": "Femelle", "Fields cannot be empty": "Les champs ne peut pas être vide", - "File Not Exist": "Le Fichier N'Existe Pas", + "File Not Exist": "Fichier non existant", "File Not Found": "Fichier Non Trouvé", "File Type": "Type De Fichier", - "FileNotExistText": "Impossible d'enregistrer non existant fichier. Quelque chose s'est mal passé.", + "FileNotExistText": "Impossible de sauvegarder le fichier non existant. Quelque chose s'est mal passé.", "Filename": "Filename", "Filesize": "La taille du fichier", "Filter ID": "Filtre ID", @@ -228,7 +228,7 @@ "Fix": "Fix", "Fix Video": "Corriger La Vidéo", "FixVideoMsg": "Voulez-vous corriger cette vidéo? Vous ne pouvez pas annuler cette action.", - "Flush PM2 Logs": "Rincer les PM2 Journaux", + "Flush PM2 Logs": "Nettoyer les Journaux PM2", "Font Path": "Le Chemin De Police", "Font Size": "Taille De La Police", "For Group": "Pour Le Groupe", @@ -242,20 +242,20 @@ "Gender": "Genre", "Generate Subtitles": "Générer Des Sous-Titres", "Get Logs to Client": "Obtenir les Journaux du Client", - "Greater Than": "Plus De", + "Greater Than": "Plus grand que", "Greater Than or Equal to": "Supérieure ou Égale à", - "Group Key": "La Clé De Groupe", + "Group Key": "Clé de groupe", "Group Name": "Nom Du Groupe", "Grouping": "Groupement d'Ajouter des groupes de Paramètres", "H.264 / H.265 / H.265+": "H. 264 / H. 265 / H. 265 ", "HLS (.m3u8)": "HLS (.m3u8)", - "HLS (includes Audio)": "HLS (Audio)", + "HLS (includes Audio)": "HLS (inclut l'audio)", "HLS Audio Encoder": "Encodeur Audio", - "HLS List Size": "La Taille De La Liste", + "HLS List Size": "Taille de la liste HLS", "HLS Live Start Index": "HLS Vivre Index de Début", "HLS Preset": "Modèle Préréglé", "HLS Segment Length": "La Longueur du Segment en quelques Secondes", - "HLS Start Number": "HLS Nombre de Départ", + "HLS Start Number": "Numéro de départ HLS", "HLS Video Encoder": "Encodeur Vidéo", "HTTP": "HTTP", "HTTPS": "HTTPS", @@ -265,47 +265,47 @@ "Hide List": "Masquer La Liste", "Hide Notes": "Masquer Les Notes", "Host": "Accueil", - "Hotswap Modes (Watch-Only)": "Hotswap Modes (Watch)", - "How to Record": "Comment faire pour Enregistrer", + "Hotswap Modes (Watch-Only)": "Modes Hotswap (Regarder Uniquement)", + "How to Record": "Comment enregistrer", "IP Address": "Adresse IP", "Identity": "Identité", - "IdentityText1": "C'est la façon dont le système permettra d'identifier les données pour ce flux. Vous ne pouvez pas modifier le Moniteur ID une fois que vous avez appuyé sur la touche enregistrer. Si vous le souhaitez, vous pouvez faire le Moniteur ID plus lisible par l'homme avant de continuer.", - "IdentityText2": "Vous pouvez dupliquer l'écran en modifiant le Moniteur ID , puis appuyez sur save. Vous ne peut pas utiliser l'ID d'un moniteur qui existe déjà ou qu'il permettra d'économiser plus que surveiller les informations de base de données.", + "IdentityText1": "Voici comment le système identifiera les données pour ce flux. Vous ne pouvez pas changer l'identifiant du moniteur une fois que vous avez appuyé sur Enregistrer. Si vous le souhaitez, vous pouvez rendre l'identifiant du moniteur plus lisible par l'homme avant de continuer.", + "IdentityText2": "Vous pouvez dupliquer l'écran en modifiant l'ID du moniteur , puis appuyez sur save. Vous ne pouvez pas utiliser l'ID d'un moniteur existant, sinon les informations de la base de données de ce moniteur seront sauvegardées.", "Idle": "Inactif", "Image Height": "La Hauteur De L'Image", - "Image Location": "Emplacement de l'Image de Chemin d'accès Absolu ou laissez le champ vide pour utiliser mondiale", + "Image Location": "Emplacement de l'Image Chemin absolu ou laisser vide pour utiliser global", "Image Position": "La Position De L'Image", "Image Width": "La Largeur De L'Image", "Import": "L'importation", - "Import Monitor Configuration": "L'Importation De Configuration Du Moniteur De", - "ImportMonitorConfigurationText": "En faisant cela, vous overrwrite toutes les modifications ne sont pas enregistrées. Les modifications importées ne sera appliquée que lorsque vous appuyez sur Enregistrer.", - "ImportMultiMonitorConfigurationText": "En faisant cela, vous overrwrite tous les moniteurs avec des Id existant dans le fichier d'importation.", + "Import Monitor Configuration": "Importer la configuration du moniteur", + "ImportMonitorConfigurationText": "Cela écrasera toutes les modifications actuellement non enregistrées. Les modifications importées ne seront appliquées que si vous appuyez sur Enregistrer .", + "ImportMultiMonitorConfigurationText": "Cela écrasera tous les moniteurs dont l'ID existe dans le fichier d'importation.", "In": "Dans", - "Incorrect Settings Chosen": "Des Paramètres Incorrects Choisi", - "Indifference": "L'indifférence", + "Incorrect Settings Chosen": "Réglages incorrects choisis", + "Indifference": "Indifférence", "Input": "Entrée", - "Input Feed": "Entrée D'Alimentation", - "Input Flags": "Entrée Des Drapeaux", + "Input Feed": "Flux d'entrée", + "Input Flags": "Drapeaux d'entrée", "Input Selector": "Sélecteur D'Entrée", - "Input Settings": "Les Paramètres D'Entrée", + "Input Settings": "Paramètres D'Entrée", "Input Type": "Type D'Entrée", - "InputText1": "Cette section indique Shinobi comment utiliser un flux. Pour des performances optimales, essayez de régler votre appareil photo les réglages internes. Trouvez les options suivantes et de les définir comme illustré. Pour trouver votre appareil photo vous pouvez utiliser le construit en ONVIF Scanner de Shinobi. Certaines caméras ONVIF nécessitent l'utilisation d'un outil de gestion pour modifier leurs paramètres internes. Si vous ne trouvez pas votre appareil, vous pouvez essayer ONVIF le Gestionnaire de Périphériques de Windows.", - "InputText2": "
  • Framerate (FPS) : Haut : 10 - 15 FPS, Faible : 2 à 5 FPS
  • I-frame interval : 80
  • Taux de bits Type : CBR (Constant Bit rate)
  • Débit binaire : entre 256kbps - 500kbps
", - "InputText3": "Si vous avez besoin d'aide pour déterminer ce type d'entrée de votre appareil photo vous pouvez prendre un coup d'oeil à la Caméra Liste d'URLs sur le Shinobi site web.", - "Invalid JSON": "Invalid JSON", - "InvalidJSONText": "Veuillez vous assurer que c'est une chaîne JSON valide pour les Shinobi de configuration du moniteur.", + "InputText1": "Cette section explique à Shinobi comment utiliser un flux. Pour des performances optimales, essayez de régler les paramètres internes de votre appareil photo. Recherchez les options suivantes et configurez-les comme indiqué. Pour trouver votre appareil photo, vous pouvez utiliser le scanner ONVIF intégré de Shinobi. Certaines caméras ONVIF nécessitent l'utilisation d'un outil de gestion pour modifier leurs paramètres internes. Si vous ne trouvez pas vos caméras, vous pouvez utiliser le gestionnaire de périphériques ONVIF pour Windows .", + "InputText2": "
  • Fréquence d'image (FPS) : Haut : 10 - 15 FPS, Faible : 2 à 5 FPS
  • Intervalle I-frame : 80
  • Type de débit : CBR (débit binaire constant)
  • Débit binaire : entre 256kbps - 500kbps
", + "InputText3": "Si vous avez besoin d'aide pour déterminer le type d'entrée de votre caméra, consultez la Liste des URL de la caméra s le site Web de Shinobi.", + "Invalid JSON": "JSON invalide", + "InvalidJSONText": "Assurez-vous qu'il s'agit d'une chaîne JSON valide pour la configuration du moniteur Shinobi.", "JPEG": "JPEG", - "JPEG (Auto Enables JPEG API)": "JPEG (Auto Permet JPEG API)", + "JPEG (Auto Enables JPEG API)": "JPEG (Activation automatique de l'API JPEG)", "JPEG API": "JPEG API Instantané (cgi-bin)", - "JPEG Error": "JPEG Erreur", - "JPEG Mode": "Le Mode JPEG", - "JPEGErrorText": "Il y avait un problème dans l'obtention de données de votre appareil photo.", + "JPEG Error": "Erreur JPEG", + "JPEG Mode": "Mode JPEG", + "JPEGErrorText": "Il y avait un problème dans l'obtention de données de votre camera.", "LDAP": "LDAP", "LDAP Success": "LDAP Succès", - "LDAP User Authenticated": "LDAP de l'Utilisateur Authentifié", + "LDAP User Authenticated": "Utilisateur LDAP authentifié", "LDAP User is New": "Utilisateur LDAP est Nouveau", "Leave blank for random.": "Laissez vide pour aléatoire.", - "Leave blank for unlimited": "Laissez vide pour un nombre illimité de", + "Leave blank for unlimited": "Laissez vide pour illimité", "Left": "Gauche Adresse URL", "Left Stop": "Butée gauche, Adresse URL", "Less Than": "Moins De", @@ -313,14 +313,14 @@ "Like": "Comme", "Limited": "Limitée", "Link Shinobi": "Lien Shinobi", - "Lisence Plate Detector": "Lisence Détecteur De Plaque", + "Lisence Plate Detector": "Détecteur de plaques Lisence", "List Toggle": "Liste De Bascule", - "Live Stream Toggle": "Live Stream De La Bascule", + "Live Stream Toggle": "Flux en direct bascule", "Live View": "Vue En Direct", "Local": "Local", - "Log Level": "Le Niveau De Journal", - "Log Signal Event": "Journal du Signal d'Événements côté Client seulement", - "Logging": "La journalisation", + "Log Level": "Niveau de journalisation", + "Log Signal Event": "Consigner un événement de signal Côté client uniquement", + "Logging": "Journalisation", "Login": "Connexion", "Logout": "Déconnexion", "Logs": "Les journaux", @@ -336,24 +336,24 @@ "Manual": "Manuel", "Map": "Carte", "Matches": "Matchs", - "Max Latency": "Max Latence", - "Max Number of Cameras": "Le Nombre maximum de Caméras", - "Max Storage Amount": "Max de la Quantité de Stockage en mo", + "Max Latency": "Latence maximale", + "Max Number of Cameras": "Nombre maximum de caméras", + "Max Storage Amount": "Quantité de stockage maximum en mo", "Mode": "Mode", "Monitor": "Moniteur", "Monitor Added by user": "Moniteur Ajoutés par l'utilisateur.", "Monitor Capture Rate": "Surveiller les Taux de Capture (FPS)", - "Monitor Groups": "La Surveillance De Groupes", + "Monitor Groups": "Groupes de surveillance", "Monitor ID": "Moniteur ID", "Monitor Idling": "Surveiller La Marche Au Ralenti", "Monitor Name": "Nom De Moniteur", "Monitor Settings": "Paramètres Du Moniteur", "Monitor Stopped": "Moniteur Arrêté", - "Monitor Updated by user": "Surveiller les mises à Jour par l'utilisateur.", - "Monitor mode changed": "Le mode moniteur changé", + "Monitor Updated by user": "Moniteur mis à jour par l'utilisateur.", + "Monitor mode changed": "Le mode moniteur a changé", "Monitor mode is already": "Le mode Monitor est déjà", "Monitor or Key does not exist.": "Le moniteur ou la Clé n'existe pas.", - "MonitorIdlingText": "Surveiller la session a été commandé au ralenti.", + "MonitorIdlingText": "La session du moniteur a été commandée pour être inactive.", "MonitorStoppedText": "Surveiller la session a été ordonné d'arrêter.", "Monitors": "Les moniteurs", "Monitors per row": "Surveille par ligne de Montage", @@ -364,41 +364,41 @@ "Name": "Nom", "New Authentication Token": "Nouveau Jeton D'Authentification", "New Monitor": "Nouveau Moniteur", - "No": "Pas de", + "No": "Non", "No Audio": "Pas D'Audio", "No Data": "Pas De Données", "No Events found for this video": "Aucun événement trouvé pour cette vidéo", - "No Group with this key exists": "Pas de Groupe avec cette clé existe", - "No Monitor Exists with this ID.": "Pas de Moniteur Existe à cet ID.", - "No Monitor Found, Ignoring Request": "Pas De Moniteur Trouvé, En Ignorant La Demande", - "No Monitors Selected": "Pas De Moniteurs Sélectionnés", + "No Group with this key exists": "Aucun groupe avec cette clé n'existe", + "No Monitor Exists with this ID.": "Aucun moniteur n'existe avec cet ID.", + "No Monitor Found, Ignoring Request": "Aucun moniteur trouvé, demande ignorée", + "No Monitors Selected": "Aucun moniteur sélectionné", "No Rotation": "Pas De Rotation", "No Videos Found": "Pas De Vidéos Trouvées", - "No such file": "Pas de tel fichier", + "No such file": "Ce fichier n'existe pas", "NoMotionEmailText1": "Pas de Mouvement pour", - "NoMotionEmailText2": "Il n'y a pas été de tout mouvement détecté sur l'appareil photo pour", - "NoVideosFoundForDateRange": "Pas de Vidéos trouvées dans cette plage de dates. Essayez de définir la date de début de la plus en arrière.", + "NoMotionEmailText2": "Aucun mouvement n'a été détecté sur la caméra depuis", + "NoVideosFoundForDateRange": "Aucune vidéo trouvée dans cette plage de dates. Essayez de définir la date de début plus en arrière.", "Noise Filter": "Filtre De Bruit", "Not Authorized": "Pas Autorisé", "Not Connected": "Pas Connecté", "Not Equal to": "Pas Égal à", "Not In": "Pas Dans", - "Not Matches": "Pas De Matchs", + "Not Matches": "Pas de correspondance", "Not Permitted": "Pas Autorisés", - "Not Saved": "Pas Enregistrées", + "Not Saved": "Non enregistré", "Not an Administrator Account": "Pas un Compte d'Administrateur", - "NotAuthorizedText1": "Pas Autorisé, Soumettre commande init avec \"auth\",\"ke\", et \"uid\"", + "NotAuthorizedText1": "Non autorisé, soumettre la commande init avec \"auth\",\"ke\", et \"uid\"", "Notes": "Notes", - "NotesPlacholder": "Les commentaires que vous voulez quitter pour ce caméras de paramètres.", + "NotesPlacholder": "Commentaires que vous souhaitez laisser pour les paramètres de cette caméra.", "Number of Days to keep": "Nombre de Jours de conservation", "ONVIF": "ONVIF", "ONVIF Scanner": "ONVIF Scanner", - "ONVIFnote": "Découvrez ONVIF appareils sur les réseaux à l'extérieur de votre propre ou la laisser vide pour analyser votre réseau actuel.
Nom d'utilisateur et le Mot de passe peut être laissé en blanc.", + "ONVIFnote": "Découvrez les périphériques ONVIF sur des réseaux extérieurs aux vôtres ou laissez-le vide pour analyser votre réseau actuel.
Le Nom d'utilisateur et le Mot de passe peut être laissé vide.", "Object": "Objet", "Object Tag": "Balise Object", "OpenCV Cascades": "OpenCV Cascades", "Options": "Options", - "Order Streams": "Afin De Ruisseaux", + "Order Streams": "Ordre des flux", "Output Method": "Méthode De Sortie", "Password": "Mot de passe", "Password Again": "Mot De Passe À Nouveau", @@ -407,7 +407,7 @@ "Path": "Chemin", "Permissions": "Les autorisations", "Please Wait for Completion": "Veuillez Attendre la fin, Selon le nombre de fichiers sélectionnés, cela peut prendre un certain temps. Actualiser pour vérifier de nouveau.", - "Points": "Des Points Lors de l'ajout de points de cliquer sur le bord du polygone.", + "Points": "Points Lorsque vous ajoutez des points, cliquez sur le bord du polygone.", "Pop": "Pop", "Port": "Port", "Pose": "Poser", @@ -419,38 +419,38 @@ "Preferences": "Préférences", "Preset": "Preset", "Preview": "Aperçu", - "Probe Size": "Longueur De La Sonde", + "Probe Size": "Taille de la Sonde", "Process Crashed for Monitor": "Le processus s'est Écrasé pour Surveiller", "Process Unexpected Exit": "Processus Inattendu De Sortie", "Profile": "Profil", - "Quality": "La qualité de l' 1 est Élevée, 23 est Faible", + "Quality": "Qualité 1 est Élevée, 23 est Faible", "Query": "Requête", "RAM": "RAM", "RTMP Stream": "Flux RTMP", "RTMP Stream Flags": "Flux RTMP Drapeaux", "RTSP": "RTSP", - "RTSP Transport": "RTSP de Transport", + "RTSP Transport": "Transport RTSP", "Range or Single": "Gamme ou Seul", "Rate": "Taux (FPS)", "Raw": "Raw", "Raw H.264 Stream": "Raw H. 264 Flux", "Recommended": "Recommandé", "Record": "Enregistrement", - "Record File Type": "Enregistrement De Type De Fichier", - "Record Height": "Record De Hauteur", - "Record Video Filter": "Enregistrement Vidéo De Filtre", - "Record Width": "Enregistrement De Largeur", + "Record File Type": "Type de fichier d'enregistrement", + "Record Height": "Enregistrer Hauteur", + "Record Video Filter": "Enregistrer le filtre vidéo", + "Record Width": "Enregistrer Largeur", "Recording": "Enregistrement", - "Recording FPS": "L'enregistrement de FPS", - "Recording FPS Change on Start": "Enregistrement FPS Changement sur Démarrer", - "Recording Flags": "L'Enregistrement Des Drapeaux", - "Recording Segment Interval": "Enregistrement Segment de l'Intervalle en minutes", - "Recording Timeout": "L'enregistrement de Délai d'attente en Minutes", - "Recording Timestamp": "L'Enregistrement D'Horodatage", - "Recording Watermark": "Enregistrement Filigrane", - "RecordingText": "Il est recommandé que vous définissez Enregistrement de Type de Fichier pour WebMMP4 et Vidéo Codec pour libvpxcopie ou libx264 parce que votre Type d'Entrée est définie sur .", + "Recording FPS": "L'enregistrement FPS", + "Recording FPS Change on Start": "Enregistrer le changement de FPS au démarrage", + "Recording Flags": "Drapeaux d'enregistrement", + "Recording Segment Interval": "Enregistrement de l'intervalle de segment en minutes", + "Recording Timeout": "Délai d'enregistrement en Minutes", + "Recording Timestamp": "Enregistrement de l'horodatage", + "Recording Watermark": "Filigrane d'enregistrement", + "RecordingText": "Il est recommandé de définir Type de fichier d'enregistrement sur WebMMP4 et Vidéo Codec pour libvpxou copie libx264 parce que votre Type d'Entrée est définie sur .", "Refresh List of Cascades": "Actualiser la Liste des Cascades", - "Region Editor": "Région De L'Éditeur", + "Region Editor": "Éditeur de région", "Region Name": "Nom De La Région", "RegionNote": "Les Points ne sont enregistrées que lorsque vous appuyez sur Enregistrer sur les Paramètres du Moniteur de la fenêtre.", "Regions": "Les régions", @@ -458,14 +458,14 @@ "Request": "Demande", "Reset Timer": "Réinitialiser La Minuterie", "Restart": "Redémarrer", - "Restart CRON": "Redémarrage du CRON", - "Restart Core": "Le Redémarrage De Base", - "Restarting Process": "Le Redémarrage Du Processus De", - "Retry Connection": "Nouvelle tentative de Connexion Nombre de fois que le droit à l'échec", + "Restart CRON": "Redémarrer CRON", + "Restart Core": "Redémarrer le noyau", + "Restarting Process": "Processus de redémarrage", + "Retry Connection": "Nouvelle tentative de Connexion Nombre d'échecs autorisés", "Retrying...": "Réessayer...", "Right": "Droit de l' Adresse URL", "Right Stop": "Droit d'Arrêter Adresse URL", - "Rotate": "Faites tourner", + "Rotate": "Tourner", "Save": "Enregistrer", "Save Directory": "Répertoire De Sauvegarde", "Save Events to SQL": "Enregistrer les Événements dans le SQL", @@ -481,20 +481,20 @@ "Send Frames": "Envoyer des Images Pousser images à analyser", "Separate with commas, no spaces": "Séparés par des virgules, sans espace", "Server URL": "URL du serveur", - "Set to Watch Only": "Mis à Regarder Seulement", + "Set to Watch Only": "Définir pour regarder uniquement", "Settings": "Paramètres", - "Settings Changed": "La Modification Des Réglages", + "Settings Changed": "Paramètres modifiés", "SettingsChangedText": "Vos paramètres sont enregistrés et appliqués. Certains paramètres peuvent nécessiter une actualisation de cette page.", "Shinobi": "Shinobi", "Shinobi Streamer": "Shinobi Streamer", - "Show Logs": "Afficher Les Journaux D'", - "Show Regions of Interest": "Montrer les Régions d'Intérêt", + "Show Logs": "Afficher Les Journaux", + "Show Regions of Interest": "Afficher les Régions d'Intérêt", "Show Stream HUD": "Afficher les Flux de PALETTE", "Silent": "Silencieux", "Simple": "Simple", "Size (mb)": "Taille (mo)", "Snapshot": "Instantané", - "Snapshot Flags": "Instantané De Drapeaux", + "Snapshot Flags": "Drapeaux Instantanés", "Snapshots": "Instantanés", "Sort By": "Trier Par", "Start": "Démarrer", @@ -508,12 +508,12 @@ "Stream": "Flux", "Stream Channel": "Canal De Flux De Données", "Stream Flags": "Flux De Drapeaux", - "Stream Key": "Flux De Clé", - "Stream Timestamp": "Flux D'Horodatage", + "Stream Key": "Clé de Flux", + "Stream Timestamp": "Horodatage du Flux", "Stream Type": "Type De Flux", "Stream Watermark": "Flux De Filigrane", "Stream to YouTube": "Stream sur YouTube", - "Stream to YouTube Flags": "Stream sur YouTube Drapeaux", + "Stream to YouTube Flags": "Diffuser vers des drapeaux YouTube", "StreamText": "

Cette section permettra de désigner le flux primaire, de méthode et de ses paramètres. Ce flux sera affiché dans le tableau de bord. Si vous choisissez d'utiliser HLS, JPEG, MJPEG, alors vous pouvez consommer du courant à travers d'autres programmes.

JPEG flux essentiellement désactive le principal cours d'eau et les utilisations de l'instantané bin pour obtenir des images.

", "Streamer": "Streamer", "Streams": "Flux", @@ -522,49 +522,49 @@ "Switch on for Still Image": "Interrupteur pour Toujours à l'Image", "System": "Système", "TCP": "TCP", - "TV Channel": "Chaîne de TÉLÉVISION", - "TV Channel Group": "Chaîne de TÉLÉVISION du Groupe", - "TV Channel ID": "Chaîne de TÉLÉVISION ID", - "Text Box Color": "Texte De La Boîte De Couleur", - "Text Color": "La Couleur Du Texte", + "TV Channel": "Chaîne TV", + "TV Channel Group": "Groupe de chaînes TV", + "TV Channel ID": "Identifiant de chaîne TV", + "Text Box Color": "Couleur de la Zone de Texte", + "Text Color": "Couleur du Texte", "Themes": "Thèmes", "There are no monitors that you can view with this account.": "Il n'y a pas les moniteurs que vous pouvez afficher avec ce compte.", "Time-lapse": "Time-lapse", - "Time-lapse Tool": "Time-lapse de l'Outil", - "Timeout": "Timeout", - "Timeout Reset on Next Motion": "Délai de Réinitialisation sur le Prochain Mouvement", + "Time-lapse Tool": "Outil Time-lapse", + "Timeout": "Délai d'attente", + "Timeout Reset on Next Motion": "Délai d'attente réinitialisé à au prochain mouvement", "Toggle Sidebar": "Basculer La Barre Latérale", "Top Left": "En Haut À Gauche", "Top Right": "En Haut À Droite", - "Traditional (Watch-Only, Includes Buffer)": "Traditionnel (Watch-Seulement, Comprend Tampon)", - "Traditional Recording": "Traditionnelle Enregistrement", - "Trigger Record": "Déclencheur D'Enregistrement", - "Trigger Successful": "Déclencheur De Succès", + "Traditional (Watch-Only, Includes Buffer)": "Traditionnel (Regarder uniquement, tampon inclus)", + "Traditional Recording": "Enregistrement Traditionnel", + "Trigger Record": "Enregistrement de Déclenchement", + "Trigger Successful": "Déclencheur Réussi", "UDP": "UDP", "URL": "URL", - "URL Stop Timeout": "URL Arrêter Délai Run stop URL après X millisecondes", + "URL Stop Timeout": "URL Arrêter le délai d'attente Run stop URL après X millisecondes", "US": "NOUS", "Unable to Launch": "Impossible de Lancer", - "UnabletoLaunchText": "Veuillez enregistrer le nouveau moniteur en premier. Tentez ensuite de lancer la région de l'éditeur.", + "UnabletoLaunchText": "Enregistrez d'abord le nouveau moniteur. Essayez ensuite de lancer l'éditeur de région.", "Uniform": "Uniforme", "Up": "Jusqu' Adresse URL", "Up Stop": "D'Arrêter Adresse URL", "Update": "Mise à jour", "Update to Development": "Mise à jour pour le Développement", - "Update to Master": "Mise à jour de Maître", - "Use Built-In": "Utilisez La Fonction De", + "Update to Master": "Mise à jour du Maître", + "Use Built-In": "Utilisez La Fonction intégré", "Username": "Nom d'utilisateur", "Value": "Valeur", "Video": "Vidéo", - "Video Bit Rate": "Le Débit Binaire Vidéo", + "Video Bit Rate": "Bitrate vidéo", "Video Codec": "Codec Vidéo", "Video Filter": "Filtre Vidéo", - "Video Finished": "Vidéo Fini", - "Video Length (minutes) and Motion Count per video": "Durée de la vidéo (en minutes) et le Mouvement Comte par vidéo", - "Video Limit": "Vidéo Limite", - "Video Record Rate": "Vidéo vitesse d'Enregistrement (IPS)", + "Video Finished": "Vidéo Terminée", + "Video Length (minutes) and Motion Count per video": "Durée de la vidéo (minutes) et nombre de mouvements par vidéo", + "Video Limit": "Limite Vidéo", + "Video Record Rate": "Taux d'enregistrement vidéo (IPS)", "Video Status": "État De La Vidéo", - "Video and Time Span (Minutes)": "La vidéo et la Durée (en Minutes)", + "Video and Time Span (Minutes)": "Vidéo et Durée (minutes)", "Videos": "Vidéos", "Videos List": "Liste Des Vidéos", "Warning": "Avertissement", @@ -573,7 +573,7 @@ "WebDAV": "WebDAV", "WebM (libvpx)": "WebM (libvpx)", "Webdav Error": "Webdav Erreur", - "WebdavErrorText": "Impossible d'enregistrer. Avez-vous rendre l'appareil dossiers à l'intérieur de votre choisi répertoire de sauvegarde?", + "WebdavErrorText": "Impossible de sauvegarder. Essayer de créer un répertoire dans votre dossier de sauvegarde", "Webhook": "Webhook", "Webhook URL": "Webhook URL", "Websocket": "Websocket", @@ -581,10 +581,10 @@ "Websocket Disconnected": "Websocket Déconnecté", "Width": "Largeur", "Yes": "Oui", - "Zoom In": "Zoom Dans l' Adresse URL", - "Zoom In Stop": "Zoom Arrêter Adresse URL", - "Zoom Out": "Zoom Adresse URL", - "Zoom Out Stop": "Zoom Arrêter Adresse URL", + "Zoom In": "Agrandir Adresse URL", + "Zoom In Stop": "Agrandir Arrêter Adresse URL", + "Zoom Out": "Dézoomer Adresse URL", + "Zoom Out Stop": "Dézoomer Arrêter Adresse URL", "a day": "un jour", "a few seconds": "quelques secondes", "a minute": "une minute", @@ -639,7 +639,7 @@ "monitorEditFailedMaxReached": "Votre compte a atteint le nombre maximum de caméras qui peuvent être créés. Parler à un administrateur si vous souhaitez que cela a changé.", "monitorEditText1": "Des Données non valides, Vérifiez la validité de l'importation de la chaîne.", "monitorEditText2": "Non Valide Les Détails De La Chaîne. Vérifiez pour voir c'est une chaîne JSON et non pas un objet normal d'être passé.", - "monitorGetText1": "demande incomplète, supprimer le dernier slash dans l'URL ou mettre valeur acceptable.", + "monitorGetText1": "demande incomplète, supprimer la dernière barre oblique de l'URL ou saisir une valeur acceptable", "months": "mois", "mpeg2_qsv": "MPEG2 (Quick Sync Video)", "mpeg4_cuvid": "MPEG4 CUVID", @@ -666,5 +666,5 @@ "vp8_cuvid": "VP8 NVENC (NVIDIA HW Accel)", "vp8_qsv": "VP8 (Quick Sync Video)", "vp9_cuvid": "VP9 NVENC (NVIDIA HW Accel)", - "years": "ans" + "years": "années" } diff --git a/libs/.gitignore b/libs/.gitignore new file mode 100644 index 0000000..df89f8a --- /dev/null +++ b/libs/.gitignore @@ -0,0 +1 @@ +customAutoLoad diff --git a/libs/auth.js b/libs/auth.js index bbc39a9..8db7804 100644 --- a/libs/auth.js +++ b/libs/auth.js @@ -35,6 +35,10 @@ module.exports = function(s,config,lang){ //check IP address of connecting user var finish=function(user){ if(s.api[params.auth].ip.indexOf('0.0.0.0')>-1||s.api[params.auth].ip.indexOf(params.ip)>-1){ + if(!user.lang){ + var details = s.parseJSON(user.details).lang + user.lang = s.getDefinitonFile(user.details.lang) || s.copySystemDefaultLanguage() + } cb(user); }else{ failed(); @@ -43,7 +47,10 @@ module.exports = function(s,config,lang){ //check if auth key is user's temporary session key if(s.group[params.ke]&&s.group[params.ke].users&&s.group[params.ke].users[params.auth]){ s.group[params.ke].users[params.auth].permissions={}; - cb(s.group[params.ke].users[params.auth]); + if(!s.group[params.ke].users[params.auth].lang){ + s.group[params.ke].users[params.auth].lang = s.copySystemDefaultLanguage() + } + cb(s.group[params.ke].users[params.auth]) }else{ //check if key is already in memory to save query time if(s.api[params.auth]&&s.api[params.auth].details){ @@ -185,6 +192,10 @@ module.exports = function(s,config,lang){ if(userFound === true){ return true }else{ + if(res)res.end(s.prettyPrint({ + ok: false, + msg: lang['Not Authorized'] + })) return false } } diff --git a/libs/basic.js b/libs/basic.js index 58c558c..250d238 100644 --- a/libs/basic.js +++ b/libs/basic.js @@ -12,7 +12,7 @@ module.exports = function(s,config){ if(s.isWin===true){ cmd = "Taskkill /IM ffmpeg.exe /F" }else{ - cmd = "ps aux | grep -ie ffmpeg | awk '{print $2}' | xargs kill -9" + cmd = "pkill -9 ffmpeg" } exec(cmd,{detached: true}) }; @@ -31,12 +31,14 @@ module.exports = function(s,config){ } } s.parseJSON = function(string){ + var parsed try{ - string = JSON.parse(string) + parsed = JSON.parse(string) }catch(err){ } - return string + if(!parsed)parsed = string + return parsed } s.stringJSON = function(json){ try{ @@ -224,4 +226,30 @@ module.exports = function(s,config){ break; } } + s.createTimeout = function(timeoutVar,timeoutLength,defaultLength,multiplier,callback){ + var theTimeout + if(!multiplier)multiplier = 1000 * 60 + if(!timeoutLength || timeoutLength === ''){ + theTimeout = defaultLength + }else{ + theTimeout = parseFloat(timeoutLength) * multiplier + } + clearTimeout(timeoutVar) + timeoutVar = setTimeout(function(){ + clearTimeout(timeoutVar) + delete(timeoutVar) + if(callback)callback() + },theTimeout) + } + Object.defineProperty(Array.prototype, 'chunk', { + value: function(chunkSize){ + var temporal = []; + + for (var i = 0; i < this.length; i+= chunkSize){ + temporal.push(this.slice(i,i+chunkSize)); + } + + return temporal; + } + }); } diff --git a/libs/cloudUploaders.js b/libs/cloudUploaders.js index cbc4128..f3a8ff9 100644 --- a/libs/cloudUploaders.js +++ b/libs/cloudUploaders.js @@ -2,7 +2,26 @@ var fs = require('fs'); var exec = require('child_process').exec; var spawn = require('child_process').spawn; var webdav = require("webdav-fs"); +var ssh2SftpClient = require('node-ssh') module.exports = function(s,config,lang){ + var addCloudUploader = function(opt){ + s.loadGroupAppExtender(opt.loadGroupAppExtender) + s.unloadGroupAppExtender(opt.unloadGroupAppExtender) + s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender) + s.deleteVideoFromCloudExtensions[opt.name] = opt.deleteVideoFromCloudExtensions + s.cloudDiskUseStartupExtensions[opt.name] = opt.cloudDiskUseStartupExtensions + s.beforeAccountSave(opt.beforeAccountSave) + s.onAccountSave(opt.onAccountSave) + s.cloudDisksLoader(opt.name) + } + var addSimpleUploader = function(opt){ + s.loadGroupAppExtender(opt.loadGroupAppExtender) + s.unloadGroupAppExtender(opt.unloadGroupAppExtender) + s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender) + s.beforeAccountSave(opt.beforeAccountSave) + s.onAccountSave(opt.onAccountSave) + s.onMonitorSave(opt.onMonitorSave) + } // WebDAV var beforeAccountSaveForWebDav = function(d){ //d = save event @@ -20,33 +39,33 @@ module.exports = function(s,config,lang){ } var loadWebDavForUser = function(e){ // e = user - var ar = JSON.parse(e.details); - if(ar.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){ + var userDetails = JSON.parse(e.details); + if(userDetails.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){ // { // webdav_user: "", // webdav_pass: "", // webdav_url: "", // webdav_dir: "", // } - ar = Object.assign(ar,config.cloudUploaders.WebDAV) + userDetails = Object.assign(userDetails,config.cloudUploaders.WebDAV) } //owncloud/webdav if(!s.group[e.ke].webdav && - ar.webdav_user&& - ar.webdav_user!==''&& - ar.webdav_pass&& - ar.webdav_pass!==''&& - ar.webdav_url&& - ar.webdav_url!=='' + userDetails.webdav_user&& + userDetails.webdav_user!==''&& + userDetails.webdav_pass&& + userDetails.webdav_pass!==''&& + userDetails.webdav_url&& + userDetails.webdav_url!=='' ){ - if(!ar.webdav_dir||ar.webdav_dir===''){ - ar.webdav_dir='/' + if(!userDetails.webdav_dir||userDetails.webdav_dir===''){ + userDetails.webdav_dir='/' } - ar.webdav_dir = s.checkCorrectPathEnding(ar.webdav_dir) + userDetails.webdav_dir = s.checkCorrectPathEnding(userDetails.webdav_dir) s.group[e.ke].webdav = webdav( - ar.webdav_url, - ar.webdav_user, - ar.webdav_pass + userDetails.webdav_url, + userDetails.webdav_user, + userDetails.webdav_pass ) } } @@ -174,8 +193,8 @@ module.exports = function(s,config,lang){ } var loadAmazonS3ForUser = function(e){ // e = user - var ar = JSON.parse(e.details) - if(ar.aws_use_global === '1' && config.cloudUploaders && config.cloudUploaders.AmazonS3){ + var userDetails = JSON.parse(e.details) + if(userDetails.aws_use_global === '1' && config.cloudUploaders && config.cloudUploaders.AmazonS3){ // { // aws_accessKeyId: "", // aws_secretAccessKey: "", @@ -183,30 +202,30 @@ module.exports = function(s,config,lang){ // aws_s3_bucket: "", // aws_s3_dir: "", // } - ar = Object.assign(ar,config.cloudUploaders.AmazonS3) + userDetails = Object.assign(userDetails,config.cloudUploaders.AmazonS3) } //Amazon S3 if(!s.group[e.ke].aws && !s.group[e.ke].aws_s3 && - ar.aws_s3 !== '0' && - ar.aws_accessKeyId !== ''&& - ar.aws_secretAccessKey && - ar.aws_secretAccessKey !== ''&& - ar.aws_region && - ar.aws_region !== ''&& - ar.aws_s3_bucket !== '' + userDetails.aws_s3 !== '0' && + userDetails.aws_accessKeyId !== ''&& + userDetails.aws_secretAccessKey && + userDetails.aws_secretAccessKey !== ''&& + userDetails.aws_region && + userDetails.aws_region !== ''&& + userDetails.aws_s3_bucket !== '' ){ - if(!ar.aws_s3_dir || ar.aws_s3_dir === '/'){ - ar.aws_s3_dir = '' + if(!userDetails.aws_s3_dir || userDetails.aws_s3_dir === '/'){ + userDetails.aws_s3_dir = '' } - if(ar.aws_s3_dir !== ''){ - ar.aws_s3_dir = s.checkCorrectPathEnding(ar.aws_s3_dir) + if(userDetails.aws_s3_dir !== ''){ + userDetails.aws_s3_dir = s.checkCorrectPathEnding(userDetails.aws_s3_dir) } s.group[e.ke].aws = new require("aws-sdk") s.group[e.ke].aws.config = new s.group[e.ke].aws.Config({ - accessKeyId: ar.aws_accessKeyId, - secretAccessKey: ar.aws_secretAccessKey, - region: ar.aws_region + accessKeyId: userDetails.aws_accessKeyId, + secretAccessKey: userDetails.aws_secretAccessKey, + region: userDetails.aws_region }) s.group[e.ke].aws_s3 = new s.group[e.ke].aws.S3(); } @@ -296,63 +315,67 @@ module.exports = function(s,config,lang){ } } var loadBackblazeB2ForUser = function(e){ - var ar = JSON.parse(e.details); + var userDetails = JSON.parse(e.details); try{ - if(ar.b2_use_global === '1' && config.cloudUploaders && config.cloudUploaders.BackblazeB2){ + if(userDetails.b2_use_global === '1' && config.cloudUploaders && config.cloudUploaders.BackblazeB2){ // { // bb_b2_accountId: "", // bb_b2_applicationKey: "", // bb_b2_bucket: "", // bb_b2_dir: "", // } - ar = Object.assign(ar,config.cloudUploaders.BackblazeB2) + userDetails = Object.assign(userDetails,config.cloudUploaders.BackblazeB2) } if(!s.group[e.ke].bb_b2 && - ar.bb_b2_accountId && - ar.bb_b2_accountId !=='' && - ar.bb_b2_applicationKey && - ar.bb_b2_applicationKey !=='' && - ar.bb_b2_bucket && - ar.bb_b2_bucket !== '' + userDetails.bb_b2_accountId && + userDetails.bb_b2_accountId !=='' && + userDetails.bb_b2_applicationKey && + userDetails.bb_b2_applicationKey !=='' && + userDetails.bb_b2_bucket && + userDetails.bb_b2_bucket !== '' ){ var B2 = require('backblaze-b2') - if(!ar.bb_b2_dir || ar.bb_b2_dir === '/'){ - ar.bb_b2_dir = '' + if(!userDetails.bb_b2_dir || userDetails.bb_b2_dir === '/'){ + userDetails.bb_b2_dir = '' } - if(ar.bb_b2_dir !== ''){ - ar.bb_b2_dir = s.checkCorrectPathEnding(ar.bb_b2_dir) + if(userDetails.bb_b2_dir !== ''){ + userDetails.bb_b2_dir = s.checkCorrectPathEnding(userDetails.bb_b2_dir) } - var b2 = new B2({ - accountId: ar.bb_b2_accountId, - applicationKey: ar.bb_b2_applicationKey - }) - s.group[e.ke].bb_b2 = b2 var backblazeErr = function(err){ // console.log(err) - s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.data}) + s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.data || err}) } - b2.authorize().then(function(resp){ - s.group[e.ke].bb_b2_downloadUrl = resp.data.downloadUrl - b2.listBuckets().then(function(resp){ - var buckets = resp.data.buckets - var bucketN = -2 - buckets.forEach(function(item,n){ - if(item.bucketName === ar.bb_b2_bucket){ - bucketN = n + var createB2Connection = function(){ + var b2 = new B2({ + accountId: userDetails.bb_b2_accountId, + applicationKey: userDetails.bb_b2_applicationKey + }) + b2.authorize().then(function(resp){ + s.group[e.ke].bb_b2_downloadUrl = resp.data.downloadUrl + b2.listBuckets().then(function(resp){ + var buckets = resp.data.buckets + var bucketN = -2 + buckets.forEach(function(item,n){ + if(item.bucketName === userDetails.bb_b2_bucket){ + bucketN = n + } + }) + if(bucketN > -1){ + s.group[e.ke].bb_b2_bucketId = buckets[bucketN].bucketId + }else{ + b2.createBucket( + userDetails.bb_b2_bucket, + 'allPublic' + ).then(function(resp){ + s.group[e.ke].bb_b2_bucketId = resp.data.bucketId + }).catch(backblazeErr) } - }) - if(bucketN > -1){ - s.group[e.ke].bb_b2_bucketId = buckets[bucketN].bucketId - }else{ - b2.createBucket( - ar.bb_b2_bucket, - 'allPublic' - ).then(function(resp){ - s.group[e.ke].bb_b2_bucketId = resp.data.bucketId - }).catch(backblazeErr) - } + }).catch(backblazeErr) }).catch(backblazeErr) - }).catch(backblazeErr) + s.group[e.ke].bb_b2 = b2 + } + createB2Connection() + s.group[e.ke].bb_b2_refreshTimer = setTimeout(createB2Connection,1000 * 60 * 60) } }catch(err){ s.debugLog(err) @@ -360,6 +383,7 @@ module.exports = function(s,config,lang){ } var unloadBackblazeB2ForUser = function(user){ s.group[user.ke].bb_b2 = null + clearTimeout(s.group[user.ke].bb_b2_refreshTimer) } var deleteVideoFromBackblazeB2 = function(e,video,callback){ // e = user @@ -431,156 +455,259 @@ module.exports = function(s,config,lang){ }) } } + //Wasabi Hot Cloud Storage + var beforeAccountSaveForWasabiHotCloudStorage = function(d){ + //d = save event + d.form.details.whcs_use_global=d.d.whcs_use_global + d.form.details.use_whcs=d.d.use_whcs + } + var cloudDiskUseStartupForWasabiHotCloudStorage = function(group,userDetails){ + group.cloudDiskUse['whcs'].name = 'Wasabi Hot Cloud Storage' + group.cloudDiskUse['whcs'].sizeLimitCheck = (userDetails.use_whcs_size_limit === '1') + if(!userDetails.whcs_size_limit || userDetails.whcs_size_limit === ''){ + group.cloudDiskUse['whcs'].sizeLimit = 10000 + }else{ + group.cloudDiskUse['whcs'].sizeLimit = parseFloat(userDetails.whcs_size_limit) + } + } + var loadWasabiHotCloudStorageForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details) + if(userDetails.whcs_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WasabiHotCloudStorage){ + // { + // whcs_accessKeyId: "", + // whcs_secretAccessKey: "", + // whcs_region: "", + // whcs_bucket: "", + // whcs_dir: "", + // } + userDetails = Object.assign(userDetails,config.cloudUploaders.WasabiHotCloudStorage) + } + //Wasabi Hot Cloud Storage + if(!s.group[e.ke].whcs && + userDetails.whcs !== '0' && + userDetails.whcs_accessKeyId !== ''&& + userDetails.whcs_secretAccessKey && + userDetails.whcs_secretAccessKey !== ''&& + userDetails.whcs_region && + userDetails.whcs_region !== ''&& + userDetails.whcs_bucket !== '' + ){ + if(!userDetails.whcs_dir || userDetails.whcs_dir === '/'){ + userDetails.whcs_dir = '' + } + if(userDetails.whcs_dir !== ''){ + userDetails.whcs_dir = s.checkCorrectPathEnding(userDetails.whcs_dir) + } + var AWS = new require("aws-sdk") + s.group[e.ke].whcs = AWS + var wasabiEndpoint = new AWS.Endpoint('s3.wasabisys.com') + s.group[e.ke].whcs.config = new s.group[e.ke].whcs.Config({ + endpoint: wasabiEndpoint, + accessKeyId: userDetails.whcs_accessKeyId, + secretAccessKey: userDetails.whcs_secretAccessKey, + region: userDetails.whcs_region + }) + s.group[e.ke].whcs = new s.group[e.ke].whcs.S3(); + } + } + var unloadWasabiHotCloudStorageForUser = function(user){ + s.group[user.ke].whcs = null + } + var deleteVideoFromWasabiHotCloudStorage = function(e,video,callback){ + // e = user + try{ + var videoDetails = JSON.parse(video.details) + }catch(err){ + var videoDetails = video.details + } + if(!videoDetails.location){ + videoDetails.location = video.href.split('wasabisys.com')[1] + } + s.group[e.ke].whcs.deleteObject({ + Bucket: s.group[e.ke].init.whcs_bucket, + Key: videoDetails.location, + }, function(err, data) { + if (err) console.log(err); + callback() + }); + } + var uploadVideoToWasabiHotCloudStorage = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - Wasabi Hot Cloud Storage + if(s.group[e.ke].whcs && s.group[e.ke].init.use_whcs !== '0' && s.group[e.ke].init.whcs_save === '1'){ + var ext = k.filename.split('.') + ext = ext[ext.length - 1] + var fileStream = fs.createReadStream(k.dir+k.filename); + fileStream.on('error', function (err) { + console.error(err) + }) + var saveLocation = s.group[e.ke].init.whcs_dir+e.ke+'/'+e.mid+'/'+k.filename + s.group[e.ke].whcs.upload({ + Bucket: s.group[e.ke].init.whcs_bucket, + Key: saveLocation, + Body:fileStream, + ACL:'public-read', + ContentType:'video/'+ext + },function(err,data){ + if(err){ + s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err}) + } + if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){ + var save = [ + e.mid, + e.ke, + k.startTime, + 1, + s.s({ + type : 'whcs', + location : saveLocation + }), + k.filesize, + k.endTime, + data.Location + ] + s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) + s.setCloudDiskUsedForGroup(e,{ + amount : k.filesizeMB, + storageType : 'whcs' + }) + s.purgeCloudDiskForGroup(e,'whcs') + } + }) + } + } //SFTP - // var beforeAccountSaveForSftp = function(d){ - // //d = save event - // d.form.details.use_sftp = d.d.use_sftp - // } - // var cloudDiskUseStartupForSftp = function(group,userDetails){ - // group.cloudDiskUse['sftp'].name = 'SFTP' - // group.cloudDiskUse['sftp'].sizeLimitCheck = (userDetails.use_aws_s3_size_limit === '1') - // if(!userDetails.aws_s3_size_limit || userDetails.aws_s3_size_limit === ''){ - // group.cloudDiskUse['sftp'].sizeLimit = 10000 - // }else{ - // group.cloudDiskUse['sftp'].sizeLimit = parseFloat(userDetails.aws_s3_size_limit) - // } - // } - // var loadSftpForUser = function(e){ - // // e = user - // var ar = JSON.parse(e.details); - // //SFTP - // if(!s.group[e.ke].sftp && - // !s.group[e.ke].sftp && - // ar.sftp !== '0' && - // ar.sftp_accessKeyId !== ''&& - // ar.sftp_secretAccessKey && - // ar.sftp_secretAccessKey !== ''&& - // ar.sftp_region && - // ar.sftp_region !== ''&& - // ar.sftp_bucket !== '' - // ){ - // if(!ar.sftp_dir || ar.sftp_dir === '/'){ - // ar.sftp_dir = '' - // } - // if(ar.sftp_dir !== ''){ - // ar.sftp_dir = s.checkCorrectPathEnding(ar.sftp_dir) - // } - // s.group[e.ke].sftp = new s.group[e.ke].sftp.S3(); - // s.group[e.ke].sftp = new require('ssh2-sftp-client')(); - // var connectionDetails = { - // host: ar.sftp_host, - // port: ar.sftp_port - // } - // if(!ar.sftp_port)ar.sftp_port = 22 - // if(ar.sftp_username)connectionDetails.username = ar.sftp_username - // if(ar.sftp_password)connectionDetails.password = ar.sftp_password - // if(ar.sftp_privateKey)connectionDetails.privateKey = ar.sftp_privateKey - // sftp.connect(connectionDetails).then(() => { - // return sftp.list('/pathname'); - // }).then((data) => { - // console.log(data, 'the data info'); - // }).catch((err) => { - // console.log(err, 'catch error'); - // }); - // } - // } - // var unloadSftpForUser = function(user){ - // s.group[user.ke].sftp = null - // } - // var deleteVideoFromSftp = function(e,video,callback){ - // // e = user - // try{ - // var videoDetails = JSON.parse(video.details) - // }catch(err){ - // var videoDetails = video.details - // } - // s.group[e.ke].sftp.deleteObject({ - // Bucket: s.group[e.ke].init.sftp_bucket, - // Key: videoDetails.location, - // }, function(err, data) { - // if (err) console.log(err); - // callback() - // }); - // } - // var uploadVideoToSftp = function(e,k){ - // //e = video object - // //k = temporary values - // if(!k)k={}; - // //cloud saver - SFTP - // if(s.group[e.ke].sftp && s.group[e.ke].init.use_sftp !== '0' && s.group[e.ke].init.sftp_save === '1'){ - // var fileStream = fs.createReadStream(k.dir+k.filename); - // fileStream.on('error', function (err) { - // console.error(err) - // }) - // var saveLocation = s.group[e.ke].init.sftp_dir+e.ke+'/'+e.mid+'/'+k.filename - // s.group[e.ke].sftp.upload({ - // Bucket: s.group[e.ke].init.sftp_bucket, - // Key: saveLocation, - // Body:fileStream, - // ACL:'public-read' - // },function(err,data){ - // if(err){ - // s.userLog(e,{type:lang['SFTP Upload Error'],msg:err}) - // } - // if(s.group[e.ke].init.sftp_log === '1' && data && data.Location){ - // var save = [ - // e.mid, - // e.ke, - // k.startTime, - // 1, - // s.s({ - // type : 'sftp', - // location : saveLocation - // }), - // k.filesize, - // k.endTime, - // data.Location - // ] - // s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) - // s.setCloudDiskUsedForGroup(e,{ - // amount : k.filesizeMB, - // storageType : 'sftp' - // }) - // s.purgeCloudDiskForGroup(e,'sftp') - // } - // }) - // } - // } + var sftpErr = function(err){ + // console.log(err) + s.userLog({mid:'$USER',ke:e.ke},{type:lang['SFTP Error'],msg:err.data || err}) + } + var beforeAccountSaveForSftp = function(d){ + //d = save event + d.form.details.use_sftp = d.d.use_sftp + } + var loadSftpForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details); + //SFTP + if(!s.group[e.ke].sftp && + !s.group[e.ke].sftp && + userDetails.sftp !== '0' && + userDetails.sftp_host && + userDetails.sftp_host !== ''&& + userDetails.sftp_port && + userDetails.sftp_port !== '' + ){ + if(!userDetails.sftp_dir || userDetails.sftp_dir === '/'){ + userDetails.sftp_dir = '' + } + if(userDetails.sftp_dir !== ''){ + userDetails.sftp_dir = s.checkCorrectPathEnding(userDetails.sftp_dir) + } + var sftp = new ssh2SftpClient() + var connectionDetails = { + host: userDetails.sftp_host, + port: userDetails.sftp_port + } + if(!userDetails.sftp_port)connectionDetails.port = 22 + if(userDetails.sftp_username && userDetails.sftp_username !== '')connectionDetails.username = userDetails.sftp_username + if(userDetails.sftp_password && userDetails.sftp_password !== '')connectionDetails.password = userDetails.sftp_password + if(userDetails.sftp_privateKey && userDetails.sftp_privateKey !== '')connectionDetails.privateKey = userDetails.sftp_privateKey + sftp.connect(connectionDetails).catch(sftpErr) + s.group[e.ke].sftp = sftp + } + } + var unloadSftpForUser = function(user){ + if(s.group[user.ke].sftp && s.group[user.ke].sftp.end)s.group[user.ke].sftp.end().then(function(){ + s.group[user.ke].sftp = null + }) + } + var uploadVideoToSftp = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - SFTP + if(s.group[e.ke].sftp && s.group[e.ke].init.use_sftp !== '0' && s.group[e.ke].init.sftp_save === '1'){ + var localPath = k.dir + k.filename + var saveLocation = s.group[e.ke].init.sftp_dir + e.ke + '/' + e.mid + '/' + k.filename + s.group[e.ke].sftp.putFile(localPath, saveLocation).catch(sftpErr) + } + } + var createSftpDirectory = function(monitorConfig){ + var monitorSaveDirectory = s.group[monitorConfig.ke].init.sftp_dir + monitorConfig.ke + '/' + monitorConfig.mid + s.group[monitorConfig.ke].sftp.mkdir(monitorSaveDirectory, true).catch(function(err){ + if(err.code !== 'ERR_ASSERTION'){ + sftpErr(err) + } + }) + } + var onMonitorSaveForSftp = function(monitorConfig){ + if(s.group[monitorConfig.ke].sftp && s.group[monitorConfig.ke].init.use_sftp !== '0' && s.group[monitorConfig.ke].init.sftp_save === '1'){ + createSftpDirectory(monitorConfig) + } + } + var onAccountSaveForSftp = function(group,userDetails,user){ + if(s.group[user.ke] && s.group[user.ke].sftp && s.group[user.ke].init.use_sftp !== '0' && s.group[user.ke].init.sftp_save === '1'){ + Object.keys(s.group[user.ke].mon_conf).forEach(function(monitorId){ + createSftpDirectory(s.group[user.ke].mon_conf[monitorId]) + }) + } + } //add the extenders //webdav - s.loadGroupAppExtender(loadWebDavForUser) - s.unloadGroupAppExtender(unloadWebDavForUser) - s.insertCompletedVideoExtender(uploadVideoToWebDav) - s.deleteVideoFromCloudExtensions['webdav'] = deleteVideoFromWebDav - s.cloudDiskUseStartupExtensions['webdav'] = cloudDiskUseStartupForWebDav - s.beforeAccountSave(beforeAccountSaveForWebDav) - s.onAccountSave(cloudDiskUseStartupForWebDav) - s.cloudDisksLoader('webdav') + addCloudUploader({ + name: 'webdav', + loadGroupAppExtender: loadWebDavForUser, + unloadGroupAppExtender: unloadWebDavForUser, + insertCompletedVideoExtender: uploadVideoToWebDav, + deleteVideoFromCloudExtensions: deleteVideoFromWebDav, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForWebDav, + beforeAccountSave: beforeAccountSaveForWebDav, + onAccountSave: cloudDiskUseStartupForWebDav, + }) //amazon s3 - s.loadGroupAppExtender(loadAmazonS3ForUser) - s.unloadGroupAppExtender(unloadAmazonS3ForUser) - s.insertCompletedVideoExtender(uploadVideoToAmazonS3) - s.deleteVideoFromCloudExtensions['s3'] = deleteVideoFromAmazonS3 - s.cloudDiskUseStartupExtensions['s3'] = cloudDiskUseStartupForAmazonS3 - s.beforeAccountSave(beforeAccountSaveForAmazonS3) - s.onAccountSave(cloudDiskUseStartupForAmazonS3) - s.cloudDisksLoader('s3') + addCloudUploader({ + name: 's3', + loadGroupAppExtender: loadAmazonS3ForUser, + unloadGroupAppExtender: unloadAmazonS3ForUser, + insertCompletedVideoExtender: uploadVideoToAmazonS3, + deleteVideoFromCloudExtensions: deleteVideoFromAmazonS3, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForAmazonS3, + beforeAccountSave: beforeAccountSaveForAmazonS3, + onAccountSave: cloudDiskUseStartupForAmazonS3, + }) //backblaze b2 - s.loadGroupAppExtender(loadBackblazeB2ForUser) - s.unloadGroupAppExtender(unloadBackblazeB2ForUser) - s.insertCompletedVideoExtender(uploadVideoToBackblazeB2) - s.deleteVideoFromCloudExtensions['b2'] = deleteVideoFromBackblazeB2 - s.cloudDiskUseStartupExtensions['b2'] = cloudDiskUseStartupForBackblazeB2 - s.beforeAccountSave(beforeAccountSaveForBackblazeB2) - s.onAccountSave(cloudDiskUseStartupForBackblazeB2) - s.cloudDisksLoader('b2') - //SFTP - // s.loadGroupAppExtender(loadSftpForUser) - // s.unloadGroupAppExtender(unloadSftpForUser) - // s.insertCompletedVideoExtender(uploadVideoToSftp) - // s.deleteVideoFromCloudExtensions['sftp'] = deleteVideoFromSftp - // s.cloudDiskUseStartupExtensions['sftp'] = cloudDiskUseStartupForSftp - // s.beforeAccountSave(beforeAccountSaveForSftp) - // s.onAccountSave(cloudDiskUseStartupForSftp) - // s.cloudDisksLoader('sftp') + addCloudUploader({ + name: 'b2', + loadGroupAppExtender: loadBackblazeB2ForUser, + unloadGroupAppExtender: unloadBackblazeB2ForUser, + insertCompletedVideoExtender: uploadVideoToBackblazeB2, + deleteVideoFromCloudExtensions: deleteVideoFromBackblazeB2, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForBackblazeB2, + beforeAccountSave: beforeAccountSaveForBackblazeB2, + onAccountSave: cloudDiskUseStartupForBackblazeB2, + }) + //wasabi + addCloudUploader({ + name: 'whcs', + loadGroupAppExtender: loadWasabiHotCloudStorageForUser, + unloadGroupAppExtender: unloadWasabiHotCloudStorageForUser, + insertCompletedVideoExtender: uploadVideoToWasabiHotCloudStorage, + deleteVideoFromCloudExtensions: deleteVideoFromWasabiHotCloudStorage, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForWasabiHotCloudStorage, + beforeAccountSave: beforeAccountSaveForWasabiHotCloudStorage, + onAccountSave: cloudDiskUseStartupForWasabiHotCloudStorage, + }) + //SFTP (Simple Uploader) + addSimpleUploader({ + name: 'sftp', + loadGroupAppExtender: loadSftpForUser, + unloadGroupAppExtender: unloadSftpForUser, + insertCompletedVideoExtender: uploadVideoToSftp, + beforeAccountSave: beforeAccountSaveForSftp, + onAccountSave: onAccountSaveForSftp, + onMonitorSave: onMonitorSaveForSftp, + }) } diff --git a/libs/codeTester.js b/libs/codeTester.js new file mode 100644 index 0000000..94d596f --- /dev/null +++ b/libs/codeTester.js @@ -0,0 +1,67 @@ +var fs = require('fs'); +var execSync = require('child_process').execSync; +module.exports = function(s,config,lang){ + var onFFmpegLoaded = function(ffmpeg){ + if(process.argv[2] && process.argv[2].indexOf('test') > -1){ + config.testMode = true + } + if(config.testMode === true){ + config.videosDir = s.mainDirectory + '/videosTest/' + config.port = 9999 + if(config.childNodes && config.childNodes.enabled === true && config.childNodes.mode === 'master'){ + config.childNodes.port = 9998 + } + s.ffmpegFunctions = ffmpeg + } + } + var onBeforeDatabaseLoad = function(ffmpeg){ + if(config.testMode === true){ + try{ + execSync('rm ' + s.mainDirectory + '/shinobi-test.sqlite') + }catch(err){ + + } + try{ + require('sqlite3') + }catch(err){ + execSync('npm install sqlite3 --unsafe-perm') + } + execSync('cp ' + s.mainDirectory + '/sql/shinobi.sample.sqlite ' + s.mainDirectory + '/shinobi-test.sqlite') + config.databaseType = 'sqlite3' + config.db = { + filename: s.mainDirectory + "/shinobi-test.sqlite" + } + } + } + var onProcessReady = function(){ + if(config.testMode === true){ + s.location.super = s.mainDirectory + '/super-test.json' + fs.writeFileSync(s.location.super,s.s([ + { + "mail":"admin@shinobi.video", + "pass":"21232f297a57a5a743894a0e4a801fc3", + "tokens":[ + "111" + ] + } + ],null,3)) + setTimeout(function(){ + require(s.mainDirectory + '/test/run.js')(s,config,lang,io) + },500) + } + } + var onProcessExit = function(){ + if(config.testMode === true){ + execSync('rm ' + s.mainDirectory + '/shinobi-test.sqlite') + execSync('rm ' + s.location.super) + execSync('rm -rf ' + config.videosDir) + console.log('---- Temporary Files Cleaned Up') + process.exit() + } + } + //attach event handlers + s.onFFmpegLoaded(onFFmpegLoaded) + s.onBeforeDatabaseLoad(onBeforeDatabaseLoad) + s.onProcessReady(onProcessReady) + s.onProcessExit(onProcessExit) +} diff --git a/libs/config.js b/libs/config.js index 185daf0..05e03bb 100644 --- a/libs/config.js +++ b/libs/config.js @@ -34,10 +34,11 @@ module.exports = function(s){ if(config.databaseLogs === undefined){config.databaseLogs=false} if(config.useUTC === undefined){config.useUTC=false} if(config.iconURL === undefined){config.iconURL = "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png"} - if(config.pipeAddition === undefined){config.pipeAddition=7}else{config.pipeAddition=parseInt(config.pipeAddition)} + if(config.pipeAddition === undefined){config.pipeAddition=10}else{config.pipeAddition=parseInt(config.pipeAddition)} if(config.hideCloudSaveUrls === undefined){config.hideCloudSaveUrls = true} if(config.insertOrphans === undefined){config.insertOrphans = true} if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 20} + if(config.detectorMergePamRegionTriggers === undefined){config.detectorMergePamRegionTriggers = false} //Child Nodes if(config.childNodes === undefined)config.childNodes = {}; //enabled @@ -50,6 +51,12 @@ module.exports = function(s){ if(config.childNodes.key === undefined)config.childNodes.key = [ '3123asdasdf1dtj1hjk23sdfaasd12asdasddfdbtnkkfgvesra3asdsd3123afdsfqw345' ]; - + if(config.cron.key === 'change_this_to_something_very_random__just_anything_other_than_this'){ + console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + console.error('!! Change your cron key in your conf.json. !!') + console.error(`!! If you're running Shinobi remotely you should do this now. !!`) + console.error('!! You can do this in the Super User panel or from terminal. !!') + console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') + } return config } diff --git a/libs/customAutoLoad.js b/libs/customAutoLoad.js new file mode 100644 index 0000000..70b6b86 --- /dev/null +++ b/libs/customAutoLoad.js @@ -0,0 +1,134 @@ +var fs = require('fs') +var express = require('express') +module.exports = function(s,config,lang,app,io){ + s.customAutoLoadModules = {} + s.customAutoLoadTree = { + pages: [], + PageBlocks: [], + LibsJs: [], + LibsCss: [], + adminPageBlocks: [], + adminLibsJs: [], + adminLibsCss: [], + superPageBlocks: [], + superLibsJs: [], + superLibsCss: [] + } + var folderPath = __dirname + '/customAutoLoad' + var search = function(searchFor,searchIn){return searchIn.indexOf(searchFor) > -1} + fs.readdir(folderPath,function(err,folderContents){ + if(!err && folderContents){ + folderContents.forEach(function(filename){ + s.customAutoLoadModules[filename] = {} + var customModulePath = folderPath + '/' + filename + if(filename.indexOf('.js') > -1){ + s.customAutoLoadModules[filename].type = 'file' + try{ + require(customModulePath)(s,config,lang,app,io) + }catch(err){ + console.log('Failed to Load Module : ' + filename) + console.log(err) + } + }else{ + if(fs.lstatSync(customModulePath).isDirectory()){ + s.customAutoLoadModules[filename].type = 'folder' + try{ + require(customModulePath)(s,config,lang,app,io) + fs.readdir(customModulePath,function(err,folderContents){ + folderContents.forEach(function(name){ + switch(name){ + case'web': + var webFolder = s.checkCorrectPathEnding(customModulePath) + 'web/' + fs.readdir(webFolder,function(err,webFolderContents){ + webFolderContents.forEach(function(name){ + switch(name){ + case'libs': + case'pages': + if(name === 'libs'){ + if(config.webPaths.home !== '/'){ + app.use('/libs',express.static(webFolder + '/libs')) + } + app.use(s.checkCorrectPathEnding(config.webPaths.home)+'libs',express.static(webFolder + '/libs')) + app.use(s.checkCorrectPathEnding(config.webPaths.admin)+'libs',express.static(webFolder + '/libs')) + app.use(s.checkCorrectPathEnding(config.webPaths.super)+'libs',express.static(webFolder + '/libs')) + } + var libFolder = webFolder + name + '/' + fs.readdir(libFolder,function(err,webFolderContents){ + webFolderContents.forEach(function(libName){ + var thirdLevelName = libFolder + libName + switch(libName){ + case'js': + case'css': + case'blocks': + fs.readdir(thirdLevelName,function(err,webFolderContents){ + webFolderContents.forEach(function(filename){ + var fullPath = thirdLevelName + '/' + filename + var blockPrefix = '' + switch(true){ + case search('super.',filename): + blockPrefix = 'super' + break; + case search('admin.',filename): + blockPrefix = 'admin' + break; + } + switch(libName){ + case'js': + s.customAutoLoadTree[blockPrefix + 'LibsJs'].push(filename) + break; + case'css': + s.customAutoLoadTree[blockPrefix + 'LibsCss'].push(filename) + break; + case'blocks': + s.customAutoLoadTree[blockPrefix + 'PageBlocks'].push(fullPath) + break; + } + }) + }) + break; + default: + if(libName.indexOf('.ejs') > -1){ + s.customAutoLoadTree.pages.push(thirdLevelName) + } + break; + } + }) + }) + break; + } + }) + }) + break; + case'languages': + var languagesFolder = s.checkCorrectPathEnding(customModulePath) + 'languages/' + fs.readdir(languagesFolder,function(err,files){ + if(err)return console.log(err); + files.forEach(function(filename){ + var fileData = require(languagesFolder + filename) + var rule = filename.replace('.json','') + if(config.language === rule){ + lang = Object.assign(lang,fileData) + } + if(s.loadedLanguages[rule]){ + s.loadedLanguages[rule] = Object.assign(s.loadedLanguages[rule],fileData) + }else{ + s.loadedLanguages[rule] = Object.assign(s.copySystemDefaultLanguage(),fileData) + } + }) + }) + break; + } + }) + }) + }catch(err){ + console.log('Failed to Load Module : ' + filename) + console.log(err) + } + } + } + }) + }else{ + fs.mkdirSync(folderPath) + } + }) +} diff --git a/libs/detector.js b/libs/detector.js index eac722f..0cc1135 100644 --- a/libs/detector.js +++ b/libs/detector.js @@ -1,6 +1,11 @@ -var P2P = require('pipe2pam'); +// Matrix In Region Libs > +var SAT = require('sat') +var V = SAT.Vector; +var P = SAT.Polygon; +// Matrix In Region Libs /> +var P2P = require('pipe2pam') // pamDiff is based on https://www.npmjs.com/package/pam-diff -var PamDiff = require('./detectorPamDiff.js'); +var PamDiff = require('pam-diff') module.exports = function(s,config){ s.createPamDiffEngine = function(e){ var width, @@ -46,61 +51,146 @@ module.exports = function(s,config){ [width,height], [width,0] ] - }; + } } e.triggerTimer = {} var regions = s.createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame) - - s.group[e.ke].mon[e.id].pamDiff = new PamDiff({ + var pamDiffOptions = { grayscale: 'luminosity', - regions : regions.forPam, - drawMatrix : e.details.detector_show_matrix - }); - s.group[e.ke].mon[e.id].p2p = new P2P(); - var sendTrigger = function(trigger){ - var detectorObject = { - f:'trigger', - id:e.id, - ke:e.ke, - name:trigger.name, - details:{ - plug:'built-in', - name:trigger.name, - reason:'motion', - confidence:trigger.percent - }, - plates:[], - imgHeight:e.details.detector_scale_y, - imgWidth:e.details.detector_scale_x - } - if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix] - var region = Object.values(regionJson).find(x => x.name == detectorObject.name) - s.checkMaximumSensitivity(e, region, detectorObject, function() { - s.checkTriggerThreshold(e, region, detectorObject, function() { - detectorObject.doObjectDetection = (s.ocv && e.details.detector_use_detect_object === '1') - s.triggerEvent(detectorObject) - }) - }) + regions : regions.forPam } - if(e.details.detector_noise_filter==='1'){ - if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {} - var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray - Object.keys(regions.notForPam).forEach(function(name){ - if(!noiseFilterArray[name])noiseFilterArray[name]=[]; - }) - s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { - data.trigger.forEach(function(trigger){ - s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){ - sendTrigger(trigger) + if(e.details.detector_show_matrix==='1'){ + pamDiffOptions.response = 'bounds' + } + s.group[e.ke].mon[e.id].pamDiff = new PamDiff(pamDiffOptions); + s.group[e.ke].mon[e.id].p2p = new P2P() + var regionArray = Object.values(regionJson) + if(config.detectorMergePamRegionTriggers === true){ + // merge pam triggers for performance boost + var buildTriggerEvent = function(trigger){ + var detectorObject = { + f:'trigger', + id:e.id, + ke:e.ke, + name:trigger.name, + details:{ + plug:'built-in', + name:trigger.name, + reason:'motion', + confidence:trigger.percent + }, + plates:[], + imgHeight:e.details.detector_scale_y, + imgWidth:e.details.detector_scale_x + } + if(trigger.merged){ + if(trigger.matrices)detectorObject.details.matrices = trigger.matrices + var filteredCount = 0 + var filteredCountSuccess = 0 + trigger.merged.forEach(function(triggerPiece){ + var region = regionArray.find(x => x.name == triggerPiece.name) + s.checkMaximumSensitivity(e, region, detectorObject, function(err1) { + s.checkTriggerThreshold(e, region, detectorObject, function(err2) { + ++filteredCount + if(!err1 && !err2)++filteredCountSuccess + if(filteredCount === trigger.merged.length && filteredCountSuccess > 0){ + detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1') + s.triggerEvent(detectorObject) + } + }) + }) + }) + }else{ + if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix] + var region = regionArray.find(x => x.name == detectorObject.name) + s.checkMaximumSensitivity(e, region, detectorObject, function(err1) { + s.checkTriggerThreshold(e, region, detectorObject, function(err2) { + if(!err1 && !err2){ + detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1') + s.triggerEvent(detectorObject) + } + }) + }) + } + } + if(e.details.detector_noise_filter==='1'){ + if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {} + var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray + Object.keys(regions.notForPam).forEach(function(name){ + if(!noiseFilterArray[name])noiseFilterArray[name]=[]; + }) + s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { + var filteredCount = 0 + var filteredCountSuccess = 0 + data.trigger.forEach(function(trigger){ + s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(err){ + ++filteredCount + if(!err)++filteredCountSuccess + if(filteredCount === data.trigger.length && filteredCountSuccess > 0){ + buildTriggerEvent(s.mergePamTriggers(data)) + } + }) }) }) - }) + }else{ + s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { + buildTriggerEvent(s.mergePamTriggers(data)) + }) + } }else{ - s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { - data.trigger.forEach(sendTrigger) - }) + //config.detectorMergePamRegionTriggers NOT true + //original behaviour, all regions have their own event. + var buildTriggerEvent = function(trigger){ + var detectorObject = { + f:'trigger', + id:e.id, + ke:e.ke, + name:trigger.name, + details:{ + plug:'built-in', + name:trigger.name, + reason:'motion', + confidence:trigger.percent + }, + plates:[], + imgHeight:e.details.detector_scale_y, + imgWidth:e.details.detector_scale_x + } + if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix] + var region = Object.values(regionJson).find(x => x.name == detectorObject.name) + s.checkMaximumSensitivity(e, region, detectorObject, function(err1) { + s.checkTriggerThreshold(e, region, detectorObject, function(err2) { + if(!err1 && ! err2){ + detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1') + s.triggerEvent(detectorObject) + } + }) + }) + } + if(e.details.detector_noise_filter==='1'){ + if(!s.group[e.ke].mon[e.id].noiseFilterArray)s.group[e.ke].mon[e.id].noiseFilterArray = {} + var noiseFilterArray = s.group[e.ke].mon[e.id].noiseFilterArray + Object.keys(regions.notForPam).forEach(function(name){ + if(!noiseFilterArray[name])noiseFilterArray[name]=[]; + }) + s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { + data.trigger.forEach(function(trigger){ + s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){ + s.createMatrixFromPamTrigger(trigger) + buildTriggerEvent(trigger) + }) + }) + }) + }else{ + s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => { + data.trigger.forEach(function(trigger){ + s.createMatrixFromPamTrigger(trigger) + buildTriggerEvent(trigger) + }) + }) + } } } @@ -164,17 +254,20 @@ module.exports = function(s,config){ theNoise = theNoise / noiseFilterArray[trigger.name].length; var triggerPercentWithoutNoise = trigger.percent - theNoise; if(triggerPercentWithoutNoise > regions.notForPam[trigger.name].sensitivity){ - callback(trigger) + callback(null,trigger) + }else{ + callback(true) } } - s.checkMaximumSensitivity = function(monitor, region, detectorObject, success) { + s.checkMaximumSensitivity = function(monitor, region, detectorObject, callback) { var logName = detectorObject.id + ':' + detectorObject.name var globalMaxSensitivity = parseInt(monitor.details.detector_max_sensitivity) || undefined var maxSensitivity = parseInt(region.max_sensitivity) || globalMaxSensitivity if (maxSensitivity === undefined || detectorObject.details.confidence <= maxSensitivity) { - success() + callback(null) } else { + callback(true) if (monitor.triggerTimer[detectorObject.name] !== undefined) { clearTimeout(monitor.triggerTimer[detectorObject.name].timeout) monitor.triggerTimer[detectorObject.name] = undefined @@ -182,10 +275,10 @@ module.exports = function(s,config){ } } - s.checkTriggerThreshold = function(monitor, region, detectorObject, success){ + s.checkTriggerThreshold = function(monitor, region, detectorObject, callback){ var threshold = parseInt(region.threshold) || globalThreshold if (threshold <= 1) { - success() + callback(null) } else { if (monitor.triggerTimer[detectorObject.name] === undefined) { monitor.triggerTimer[detectorObject.name] = { @@ -194,10 +287,11 @@ module.exports = function(s,config){ } } if (--monitor.triggerTimer[detectorObject.name].count == 0) { - success() + callback(null) clearTimeout(monitor.triggerTimer[detectorObject.name].timeout) monitor.triggerTimer[detectorObject.name] = undefined } else { + callback(true) var fps = parseFloat(monitor.details.detector_fps) || 2 if (monitor.triggerTimer[detectorObject.name].timeout !== null) clearTimeout(monitor.triggerTimer[detectorObject.name].timeout) @@ -207,4 +301,92 @@ module.exports = function(s,config){ } } } + s.mergePamTriggers = function(data){ + if(data.trigger.length > 1){ + var n = 0 + var sum = 0 + var name = [] + var matrices = [] + data.trigger.forEach(function(trigger){ + name.push(trigger.name + ' ('+trigger.percent+'%)') + ++n + sum += trigger.percent + s.createMatrixFromPamTrigger(trigger) + if(trigger.matrix)matrices.push(trigger.matrix) + }) + var average = sum / n + name = name.join(', ') + if(matrices.length === 0)matrices = null + var trigger = { + name: name, + percent: parseInt(average), + matrices: matrices, + merged: data.trigger + } + }else{ + var trigger = data.trigger[0] + s.createMatrixFromPamTrigger(trigger) + trigger.matrices = [trigger.matrix] + } + return trigger + } + s.isAtleastOneMatrixInRegion = function(regions,matrices,callback){ + var regionPolys = [] + var matrixPoints = [] + regions.forEach(function(region,n){ + var polyPoints = [] + region.points.forEach(function(point){ + polyPoints.push(new V(parseInt(point[0]),parseInt(point[1]))) + }) + regionPolys[n] = new P(new V(0,0), polyPoints) + }) + var collisions = [] + var foundInRegion = false + matrices.forEach(function(matrix){ + var matrixPoints = [ + new V(matrix.x,matrix.y), + new V(matrix.width,matrix.y), + new V(matrix.width,matrix.height), + new V(matrix.x,matrix.height) + ] + var matrixPoly = new P(new V(0,0), matrixPoints) + regionPolys.forEach(function(region,n){ + var response = new SAT.Response() + var collided = SAT.testPolygonPolygon(matrixPoly, region, response) + if(collided === true){ + collisions.push({ + matrix: matrix, + region: regions[n] + }) + foundInRegion = true + } + }) + }) + if(callback)callback(foundInRegion,collisions) + return foundInRegion + } + s.createMatrixFromPamTrigger = function(trigger){ + if( + trigger.minX && + trigger.maxX && + trigger.minY && + trigger.maxY + ){ + var coordinates = [ + {"x" : trigger.minX, "y" : trigger.minY}, + {"x" : trigger.maxX, "y" : trigger.minY}, + {"x" : trigger.maxX, "y" : trigger.maxY} + ] + var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2)); + var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2)) + trigger.matrix = { + x: coordinates[0].x, + y: coordinates[0].y, + width: width, + height: height, + tag: trigger.name + } + } + return trigger + } } diff --git a/libs/dropInEvents.js b/libs/dropInEvents.js new file mode 100644 index 0000000..d5f1790 --- /dev/null +++ b/libs/dropInEvents.js @@ -0,0 +1,208 @@ +var fs = require('fs') +var execSync = require('child_process').execSync +module.exports = function(s,config,lang,app,io){ + if(config.dropInEventServer === true){ + if(config.dropInEventDeleteFileAfterTrigger === undefined)config.dropInEventDeleteFileAfterTrigger = true + var authenticateUser = function(username,password,callback){ + var splitUsername = username.split('@') + if(splitUsername[1] !== 'Shinobi' && splitUsername[1] !== 'shinobi'){ + s.sqlQuery('SELECT ke,uid FROM Users WHERE mail=? AND (pass=? OR pass=?)',[ + username, + password, + s.createHash(password) + ],function(err,r){ + var user + if(r && r[0]){ + user = r[0] + } + callback(err,user) + }) + }else{ + s.sqlQuery('SELECT ke,uid FROM API WHERE code=? AND ke=?',[ + splitUsername[0], //code + password //ke + ],function(err,r){ + var apiKey + if(r && r[0]){ + apiKey = r[0] + } + callback(err,apiKey) + }) + } + } + var beforeMonitorsLoadedOnStartup = function(){ + if(!config.dropInEventsDir){ + config.dropInEventsDir = s.dir.streams + 'dropInEvents/' + } + s.dir.dropInEvents = s.checkCorrectPathEnding(config.dropInEventsDir) + //dropInEvents dir + if(!fs.existsSync(s.dir.dropInEvents)){ + fs.mkdirSync(s.dir.dropInEvents) + } + } + var getDropInEventDir = function(monitorConfig){ + var ke = monitorConfig.ke + var mid = monitorConfig.mid + var groupEventDropDir = s.dir.dropInEvents + ke + var monitorEventDropDir = groupEventDropDir + '/' + mid + '/' + return monitorEventDropDir + } + var onMonitorStop = function(monitorConfig){ + var ke = monitorConfig.ke + var mid = monitorConfig.mid + if(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher){ + s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher.close() + delete(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher) + } + var monitorEventDropDir = getDropInEventDir(monitorConfig) + if(fs.existsSync(monitorEventDropDir))execSync('rm -rf ' + monitorEventDropDir) + } + var onMonitorInit = function(monitorConfig){ + onMonitorStop(monitorConfig) + var ke = monitorConfig.ke + var mid = monitorConfig.mid + var monitorEventDropDir = getDropInEventDir(monitorConfig) + var groupEventDropDir = s.dir.dropInEvents + ke + if(!fs.existsSync(groupEventDropDir)){ + fs.mkdirSync(groupEventDropDir) + } + var monitorEventDropDir = groupEventDropDir + '/' + mid + '/' + if(!fs.existsSync(monitorEventDropDir)){ + fs.mkdirSync(monitorEventDropDir) + } + var fileQueue = {} + s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventFileQueue = fileQueue + var eventTrigger = function(eventType,filename){ + var filePath = monitorEventDropDir + filename + if(filename.indexOf('.jpg') > -1 || filename.indexOf('.jpeg') > -1){ + var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg' + fs.unlink(snapPath,function(err){ + fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath)) + s.triggerEvent({ + id: mid, + ke: ke, + details: { + confidence: 100, + name: filename, + plug: "dropInEvent", + reason: "dropInEvent" + } + }) + }) + }else{ + s.triggerEvent({ + id: mid, + ke: ke, + details: { + confidence: 100, + name: filename, + plug: "dropInEvent", + reason: "ftpServer" + } + }) + } + if(config.dropInEventDeleteFileAfterTrigger){ + setTimeout(function(){ + fs.unlink(filePath,function(err){ + + }) + },1000 * 60 * 5) + } + } + var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){ + if(fs.existsSync(monitorEventDropDir + filename)){ + clearTimeout(fileQueue[filename]) + fileQueue[filename] = setTimeout(function(){ + eventTrigger(eventType,filename) + },1200) + } + }) + s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher = directoryWatch + } + // FTP Server + if(config.ftpServer === true){ + if(!config.ftpServerPort)config.ftpServerPort = 21 + if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}` + config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort) + const FtpSrv = require('ftp-srv') + const ftpServer = new FtpSrv({ + url: config.ftpServerUrl, + // log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){} + }) + + ftpServer.on('login', (data, resolve, reject) => { + var username = data.username + var password = data.password + authenticateUser(username,password,function(err,user){ + if(user){ + resolve({root: s.dir.dropInEvents + user.ke}) + }else{ + // reject(new Error('Failed Authorization')) + } + }) + }) + + ftpServer.listen().then(() => { + s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`) + }).catch(function(err){ + s.systemLog(err) + }) + } + //add extensions + s.beforeMonitorsLoadedOnStartup(beforeMonitorsLoadedOnStartup) + s.onMonitorInit(onMonitorInit) + s.onMonitorStop(onMonitorStop) + } + // SMTP Server + // allow starting SMTP server without dropInEventServer + if(config.smtpServer === true){ + var SMTPServer = require("smtp-server").SMTPServer; + if(!config.smtpServerPort && (config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl)){config.smtpServerPort = 465}else if(!config.smtpServerPort){config.smtpServerPort = 25} + var smtpOptions = { + onAuth(auth, session, callback) { + var username = auth.username + var password = auth.password + authenticateUser(username,password,function(err,user){ + if(user){ + callback(null, {user: user.ke}) + }else{ + callback(new Error(lang.failedLoginText2)) + } + }) + }, + onRcptTo(address, session, callback) { + var split = address.address.split('@') + var monitorId = split[0] + var ke = session.user + if(s.group[ke].mon_conf[monitorId] && s.group[ke].mon[monitorId].isStarted === true){ + s.triggerEvent({ + id: monitorId, + ke: ke, + details: { + confidence: 100, + name: address.address, + plug: "dropInEvent", + reason: "smtpServer" + } + }) + }else{ + return callback(new Error(lang['No Monitor Exists with this ID.'])) + } + callback() + } + } + if(config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl && config.ssl.cert && config.ssl.key){ + var key = config.ssl.key || fs.readFileSync(config.smtpServerSsl.key) + var cert = config.ssl.cert || fs.readFileSync(config.smtpServerSsl.cert) + smtpOptions = Object.assign(smtpOptions,{ + secure: true, + key: config.ssl.key, + cert: config.ssl.cert + }) + } + var server = new SMTPServer(smtpOptions) + server.listen(config.smtpServerPort,function(){ + s.systemLog(`SMTP Server running on port ${config.smtpServerPort}...`) + }) + } +} diff --git a/libs/events.js b/libs/events.js index 9682fa7..13d6047 100644 --- a/libs/events.js +++ b/libs/events.js @@ -4,6 +4,25 @@ var exec = require('child_process').exec; var spawn = require('child_process').spawn; var request = require('request'); module.exports = function(s,config,lang){ + var addEventDetailsToString = function(eventData,string,addOps){ + //d = event data + if(!addOps)addOps = {} + var newString = string + '' + var d = Object.assign(eventData,addOps) + var detailString = s.stringJSON(d.details) + newString = newString + .replace(/{{TIME}}/g,d.currentTimestamp) + .replace(/{{REGION_NAME}}/g,d.details.name) + .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') + .replace(/{{MONITOR_ID}}/g,d.id) + .replace(/{{GROUP_KEY}}/g,d.ke) + .replace(/{{DETAILS}}/g,detailString) + if(d.details.confidence){ + newString = newString + .replace(/{{CONFIDENCE}}/g,d.details.confidence) + } + return newString + } s.filterEvents = function(x,d){ switch(x){ case'archive': @@ -36,15 +55,13 @@ module.exports = function(s,config,lang){ s.onEventTriggerBeforeFilterExtensions.forEach(function(extender){ extender(d,filter) }) - if(s.group[d.ke].mon[d.id].open){ - d.details.videoTime = s.group[d.ke].mon[d.id].open; - } var detailString = JSON.stringify(d.details); if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){ return s.systemLog(lang['No Monitor Found, Ignoring Request']) } d.mon=s.group[d.ke].mon_conf[d.id]; var currentConfig = s.group[d.ke].mon[d.id].details + var hasMatrices = (d.details.matrices && d.details.matrices.length > 0) //read filters if( currentConfig.use_detector_filters === '1' && @@ -160,7 +177,7 @@ module.exports = function(s,config,lang){ }) if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){ return - }else if(d.details.matrices && d.details.matrices.length > 0){ + }else if(hasMatrices){ var reviewedMatrix = [] d.details.matrices.forEach(function(matrix){ if(matrix)reviewedMatrix.push(matrix) @@ -193,6 +210,16 @@ module.exports = function(s,config,lang){ return } } + // check if object should be in region + if(hasMatrices && currentConfig.detector_obj_region === '1'){ + var regions = s.group[d.ke].mon[d.id].parsedObjects.cords + var isMatrixInRegions = s.isAtleastOneMatrixInRegion(regions,d.details.matrices) + if(isMatrixInRegions){ + s.debugLog('Matrix in region!') + }else{ + return + } + } // check modified indifference if(filter.indifference !== false && d.details.confidence < parseFloat(filter.indifference)){ // fails indifference check for modified indifference @@ -209,13 +236,29 @@ module.exports = function(s,config,lang){ frame : s.group[d.ke].mon[d.id].lastJpegDetectorFrame }) }else{ + if(currentConfig.detector_multi_trigger === '1'){ + s.getCamerasForMultiTrigger(d.mon).forEach(function(monitor){ + if(monitor.mid !== d.id){ + s.triggerEvent({ + id: monitor.mid, + ke: monitor.ke, + details: { + confidence: 100, + name: "multiTrigger", + plug: d.details.plug, + reason: d.details.reason + } + }) + } + }) + } //save this detection result in SQL, only coords. not image. - if(filter.save && currentConfig.detector_save==='1'){ - s.sqlQuery('INSERT INTO Events (ke,mid,details) VALUES (?,?,?)',[d.ke,d.id,detailString]) + if(filter.save && currentConfig.detector_save === '1'){ + s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,new Date()]) } if(currentConfig.detector_notrigger === '1'){ var detector_notrigger_timeout - if(!currentConfig.detector_notrigger_timeout||currentConfig.detector_notrigger_timeout===''){ + if(!currentConfig.detector_notrigger_timeout||currentConfig.detector_notrigger_timeout === ''){ detector_notrigger_timeout = 10 } detector_notrigger_timeout = parseFloat(currentConfig.detector_notrigger_timeout)*1000*60; @@ -270,17 +313,7 @@ module.exports = function(s,config,lang){ }) if(filter.webhook && currentConfig.detector_webhook === '1'){ - var detector_webhook_url = currentConfig.detector_webhook_url - .replace(/{{TIME}}/g,d.currentTimestamp) - .replace(/{{REGION_NAME}}/g,d.details.name) - .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') - .replace(/{{MONITOR_ID}}/g,d.id) - .replace(/{{GROUP_KEY}}/g,d.ke) - .replace(/{{DETAILS}}/g,detailString) - if(d.details.confidence){ - detector_webhook_url = detector_webhook_url - .replace(/{{CONFIDENCE}}/g,d.details.confidence) - } + var detector_webhook_url = addEventDetailsToString(d,currentConfig.detector_webhook_url) request({url:detector_webhook_url,method:'GET',encoding:null},function(err,data){ if(err){ s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}}) @@ -289,28 +322,8 @@ module.exports = function(s,config,lang){ } if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].mon[d.id].detector_command){ - var detector_command_timeout - if(!currentConfig.detector_command_timeout||currentConfig.detector_command_timeout===''){ - detector_command_timeout = 1000*60*10; - }else{ - detector_command_timeout = parseFloat(currentConfig.detector_command_timeout)*1000*60; - } - s.group[d.ke].mon[d.id].detector_command=setTimeout(function(){ - clearTimeout(s.group[d.ke].mon[d.id].detector_command); - delete(s.group[d.ke].mon[d.id].detector_command); - - },detector_command_timeout); - var detector_command = currentConfig.detector_command - .replace(/{{TIME}}/g,d.currentTimestamp) - .replace(/{{REGION_NAME}}/g,d.details.name) - .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') - .replace(/{{MONITOR_ID}}/g,d.id) - .replace(/{{GROUP_KEY}}/g,d.ke) - .replace(/{{DETAILS}}/g,detailString) - if(d.details.confidence){ - detector_command = detector_command - .replace(/{{CONFIDENCE}}/g,d.details.confidence) - } + s.createTimeout(s.group[d.ke].mon[d.id].detector_command,currentConfig.detector_command_timeout,10) + var detector_command = addEventDetailsToString(d,currentConfig.detector_command) exec(detector_command,{detached: true}) } } @@ -319,14 +332,18 @@ module.exports = function(s,config,lang){ s.tx(d.cx,'DETECTOR_'+d.ke+d.id); } s.createEventBasedRecording = function(d){ + d.mon = s.group[d.ke].mon_conf[d.id] var currentConfig = s.group[d.ke].mon[d.id].details + if(currentConfig.detector !== '1'){ + return + } var detector_timeout if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){ detector_timeout = 10 }else{ detector_timeout = parseFloat(currentConfig.detector_timeout) } - if(currentConfig.watchdog_reset !== '1' || !s.group[d.ke].mon[d.id].eventBasedRecording.timeout){ + if(currentConfig.watchdog_reset === '1' || !s.group[d.ke].mon[d.id].eventBasedRecording.timeout){ clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout) s.group[d.ke].mon[d.id].eventBasedRecording.timeout = setTimeout(function(){ s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = true @@ -336,40 +353,28 @@ module.exports = function(s,config,lang){ },detector_timeout * 1000 * 60) } if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){ - if(!d.auth){ - d.auth = s.gid(60) - } - if(!s.api[d.auth]){ - s.api[d.auth] = { - system: 1, - ip: '0.0.0.0', - details: {}, - lang: lang - } - } s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false; var runRecord = function(){ var filename = s.formattedTime()+'.mp4' - s.userLog(d,{type:"Traditional Recording",msg:"Started"}) + s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Started"]}) //-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+' - s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i http://'+config.ip+':'+config.port+'/'+d.auth+'/hls/'+d.ke+'/'+d.id+'/detectorStream.m3u8 -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"'))) + s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"'))) var ffmpegError=''; var error s.group[d.ke].mon[d.id].eventBasedRecording.process.stderr.on('data',function(data){ - s.userLog(d,{type:"Traditional Recording",msg:data.toString()}) + s.userLog(d,{type:lang["Traditional Recording"],msg:data.toString()}) }) s.group[d.ke].mon[d.id].eventBasedRecording.process.on('close',function(){ if(!s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd){ - s.userLog(d,{type:"Traditional Recording",msg:"Detector Recording Process Exited Prematurely. Restarting."}) + s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Process Exited Prematurely. Restarting."]}) runRecord() return } s.insertCompletedVideo(d.mon,{ file : filename }) - s.userLog(d,{type:"Traditional Recording",msg:"Detector Recording Complete"}) - delete(s.api[d.auth]) - s.userLog(d,{type:"Traditional Recording",msg:'Clear Recorder Process'}) + s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Detector Recording Complete"]}) + s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Clear Recorder Process"]}) delete(s.group[d.ke].mon[d.id].eventBasedRecording.process) clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout) delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout) @@ -385,5 +390,12 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.id].eventBasedRecording.allowEnd = true; s.group[e.ke].mon[e.id].eventBasedRecording.process.kill('SIGTERM'); } + // var stackedProcesses = s.group[e.ke].mon[e.id].eventBasedRecording.stackable + // Object.keys(stackedProcesses).forEach(function(key){ + // var item = stackedProcesses[key] + // clearTimeout(item.timeout) + // item.allowEnd = true; + // item.process.kill('SIGTERM'); + // }) } } diff --git a/libs/extenders.js b/libs/extenders.js index 6278fda..8059c79 100644 --- a/libs/extenders.js +++ b/libs/extenders.js @@ -1,5 +1,15 @@ module.exports = function(s,config){ ////// USER ////// + s.onSocketAuthenticationExtensions = [] + s.onSocketAuthentication = function(callback){ + s.onSocketAuthenticationExtensions.push(callback) + } + // + s.loadGroupExtensions = [] + s.loadGroupExtender = function(callback){ + s.loadGroupExtensions.push(callback) + } + // s.loadGroupAppExtensions = [] s.loadGroupAppExtender = function(callback){ s.loadGroupAppExtensions.push(callback) @@ -30,6 +40,11 @@ module.exports = function(s,config){ s.onTwoFactorAuthCodeNotificationExtensions.push(callback) } // + s.onStalePurgeLockExtensions = [] + s.onStalePurgeLock = function(callback){ + s.onStalePurgeLockExtensions.push(callback) + } + // s.cloudDiskUseStartupExtensions = {} ////// EVENTS ////// @@ -51,8 +66,81 @@ module.exports = function(s,config){ s.onMonitorInit = function(callback){ s.onMonitorInitExtensions.push(callback) } + // + s.onMonitorStartExtensions = [] + s.onMonitorStart = function(callback){ + s.onMonitorStartExtensions.push(callback) + } + // + s.onMonitorStopExtensions = [] + s.onMonitorStop = function(callback){ + s.onMonitorStopExtensions.push(callback) + } + // + s.onMonitorSaveExtensions = [] + s.onMonitorSave = function(callback){ + s.onMonitorSaveExtensions.push(callback) + } + // + s.onMonitorUnexpectedExitExtensions = [] + s.onMonitorUnexpectedExit = function(callback){ + s.onMonitorUnexpectedExitExtensions.push(callback) + } + // s.onDetectorNoTriggerTimeoutExtensions = [] s.onDetectorNoTriggerTimeout = function(callback){ s.onDetectorNoTriggerTimeoutExtensions.push(callback) } + // + s.onFfmpegCameraStringCreationExtensions = [] + s.onFfmpegCameraStringCreation = function(callback){ + s.onFfmpegCameraStringCreationExtensions.push(callback) + } + // + s.onMonitorPingFailedExtensions = [] + s.onMonitorPingFailed = function(callback){ + s.onMonitorPingFailedExtensions.push(callback) + } + // + s.onMonitorDiedExtensions = [] + s.onMonitorDied = function(callback){ + s.onMonitorDiedExtensions.push(callback) + } + + ///////// SYSTEM //////// + s.onProcessReadyExtensions = [] + s.onProcessReady = function(callback){ + s.onProcessReadyExtensions.push(callback) + } + // + s.onProcessExitExtensions = [] + s.onProcessExit = function(callback){ + s.onProcessExitExtensions.push(callback) + } + // + s.onBeforeDatabaseLoadExtensions = [] + s.onBeforeDatabaseLoad = function(callback){ + s.onBeforeDatabaseLoadExtensions.push(callback) + } + // + s.onFFmpegLoadedExtensions = [] + s.onFFmpegLoaded = function(callback){ + s.onFFmpegLoadedExtensions.push(callback) + } + // + s.beforeMonitorsLoadedOnStartupExtensions = [] + s.beforeMonitorsLoadedOnStartup = function(callback){ + s.beforeMonitorsLoadedOnStartupExtensions.push(callback) + } + // + s.onWebSocketConnectionExtensions = [] + s.onWebSocketConnection = function(callback){ + s.onWebSocketConnectionExtensions.push(callback) + } + // + s.onWebSocketDisconnectionExtensions = [] + s.onWebSocketDisconnection = function(callback){ + s.onWebSocketDisconnectionExtensions.push(callback) + } + // } diff --git a/libs/ffmpeg.js b/libs/ffmpeg.js index 409156e..e5b7bad 100644 --- a/libs/ffmpeg.js +++ b/libs/ffmpeg.js @@ -104,6 +104,9 @@ module.exports = function(s,config,onFinish){ ffmpeg.completeCheck = function(){ ffmpeg.checkVersion(function(){ ffmpeg.checkHwAccelMethods(function(){ + s.onFFmpegLoadedExtensions.forEach(function(extender){ + extender(ffmpeg) + }) onFinish(ffmpeg) }) }) @@ -132,7 +135,9 @@ module.exports = function(s,config,onFinish){ string += ' -map '+v.map }) }else{ - string += ' -map 0:0' + var primaryMap = '0:0' + if(e.details.primary_input && e.details.primary_input !== '')primaryMap = e.details.primary_input + string += ' -map ' + primaryMap } } return string; @@ -379,7 +384,16 @@ module.exports = function(s,config,onFinish){ //x = temporary values //check if CUDA is enabled e.isStreamer = (e.type === 'dashcam'|| e.type === 'socket') - if(e.details.accelerator === '1' && e.details.hwaccel === 'cuvid' && e.details.hwaccel_vcodec === ('h264_cuvid' || 'hevc_cuvid' || 'mjpeg_cuvid' || 'mpeg4_cuvid')){ + e.coProcessor = false + if( + e.details.use_coprocessor === '1' && + e.details.accelerator === '1' && + e.isStreamer === false && + (!e.details.input_maps || e.details.input_maps.length === 0) && + (e.details.snap === '1' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64' || e.details.detector === '1') + ){ + e.coProcessor = true + }else if(e.details.accelerator === '1' && e.details.hwaccel === 'cuvid' && e.details.hwaccel_vcodec === ('h264_cuvid' || 'hevc_cuvid' || 'mjpeg_cuvid' || 'mpeg4_cuvid')){ e.cudaEnabled = true } // @@ -560,9 +574,11 @@ module.exports = function(s,config,onFinish){ x.pipe+=x.preset_stream+x.stream_acodec+x.stream_vcodec+' -f hls'+x.cust_stream+' -hls_time '+x.hls_time+' -hls_list_size '+x.hls_list_size+' -start_number 0 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'s.m3u8"'; break; case'mjpeg': + if(e.coProcessor === false){ if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} x.pipe+=' -an -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+' pipe:1'; + } break; case'h265': x.cust_stream+=' -movflags +frag_keyframe+empty_moov+default_base_moof -metadata title="Shinobi H.265 Stream" -reset_timestamps 1' @@ -575,9 +591,11 @@ module.exports = function(s,config,onFinish){ x.pipe+=' -f hevc'+x.stream_acodec+x.stream_vcodec+x.cust_stream+' pipe:1'; break; case'b64':case'':case undefined:case null://base64 - if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; - if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} - x.pipe+=' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + if(e.coProcessor === false){ + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe+=' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + } break; default: x.pipe='' @@ -585,11 +603,12 @@ module.exports = function(s,config,onFinish){ } if(e.details.stream_channels){ e.details.stream_channels.forEach(function(v,n){ + // if(v.stream_type === 'mjpeg')e.coProcessor = true; x.pipe += s.createStreamChannel(e,n+config.pipeAddition,v) }) } //api - snapshot bin/ cgi.bin (JPEG Mode) - if(e.details.snap === '1'){ + if(e.details.snap === '1' && e.coProcessor === false){ if(e.details.input_map_choices&&e.details.input_map_choices.snap){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.snap) @@ -628,7 +647,7 @@ module.exports = function(s,config,onFinish){ x.dimensions = e.details.stream_scale_x+'x'+e.details.stream_scale_y; } //record - segmenting - x.segment=' -f segment -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"'; + x.segment=' -f segment -segment_format_options movflags=faststart+frag_keyframe+empty_moov -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"'; //record - set defaults for extension, video quality switch(e.ext){ case'mp4': @@ -732,40 +751,67 @@ module.exports = function(s,config,onFinish){ x.record_string+=x.vcodec+x.record_fps+x.record_video_filters+x.record_dimensions+x.segment; } } + ffmpeg.buildAudioDetector = function(e,x){ + if(e.details.detector_audio === '1'){ + if(e.details.input_map_choices&&e.details.input_map_choices.detector_audio){ + //add input feed map + x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector_audio) + }else{ + x.pipe += ' -map 0:a' + } + x.pipe += ' -acodec pcm_s16le -f s16le -ac 1 -ar 16000 pipe:6' + } + } ffmpeg.buildMainDetector = function(e,x){ //e = monitor object //x = temporary values x.cust_detect = ' ' //detector - plugins, motion - if(e.details.detector === '1' && e.details.detector_send_frames === '1'){ - if(e.details.input_map_choices&&e.details.input_map_choices.detector){ + var sendFramesGlobally = (e.details.detector_send_frames === '1') + var sendFramesToObjectDetector = (e.details.detector_send_frames_object !== '0' && e.details.detector_use_detect_object === '1') + if(e.details.detector === '1' && (sendFramesGlobally || sendFramesToObjectDetector) && e.coProcessor === false){ + if(sendFramesGlobally && e.details.input_map_choices && e.details.input_map_choices.detector){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) } - if(!e.details.detector_fps||e.details.detector_fps===''){e.details.detector_fps=2} - if(e.details.detector_scale_x&&e.details.detector_scale_x!==''&&e.details.detector_scale_y&&e.details.detector_scale_y!==''){x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y}else{x.dratio=' -s 320x240'} + if(!e.details.detector_fps || e.details.detector_fps === ''){x.detector_fps = 2}else{x.detector_fps = parseInt(e.details.detector_fps)} + if(e.details.detector_scale_x && e.details.detector_scale_x !== '' && e.details.detector_scale_y && e.details.detector_scale_y !== ''){x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y}else{x.dratio=' -s 320x240'} if(e.details.cust_detect&&e.details.cust_detect!==''){x.cust_detect+=e.details.cust_detect;} - x.detector_vf = ['fps='+e.details.detector_fps] + if(sendFramesGlobally)x.pipe += ' -r ' + x.detector_fps + x.dratio + x.cust_detect + x.detector_vf = [] if(e.cudaEnabled){ x.detector_vf.push('hwdownload,format=nv12') } - x.detector_vf = '-vf "'+x.detector_vf.join(',')+'"' - if(e.details.detector_pam==='1'){ - if(e.cudaEnabled){ + if(sendFramesGlobally && x.detector_vf.length > 0)x.pipe += ' -vf "'+x.detector_vf.join(',')+'"' + + var h264Output = ' -q:v 1 -an -c:v libx264 -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'detectorStreamX.m3u8"' + if(e.details.detector_pam === '1'){ + if(sendFramesGlobally && e.cudaEnabled){ x.pipe += ' -vf "hwdownload,format=nv12"' } - x.pipe+=' -an -c:v pam -pix_fmt gray -f image2pipe -r '+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3' + if(sendFramesGlobally)x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe pipe:3' if(e.details.detector_use_detect_object === '1'){ //for object detection x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) - x.pipe += ' -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4'; + if(e.details.detector_scale_x_object&&e.details.detector_scale_x_object!==''&&e.details.detector_scale_y_object&&e.details.detector_scale_y_object!==''){x.dobjratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object}else{x.dobjratio=x.dratio} + x.pipe += ' -r ' + x.detector_fps + x.dobjratio + x.cust_detect + if(e.details.detector_h264 === '1'){ + x.pipe += h264Output + }else{ + x.pipe += ' -an -f singlejpeg pipe:4' + } + } + }else if(sendFramesGlobally){ + if(e.details.detector_h264 === '1'){ + x.pipe += h264Output + }else{ + x.pipe += ' -an -f singlejpeg pipe:3' } - }else{ - x.pipe+=' -f image2pipe '+x.detector_vf+x.cust_detect+x.dratio+' pipe:3'; } } //Traditional Recording Buffer if(e.details.detector=='1'&&e.details.detector_trigger=='1'&&e.details.detector_record_method==='sip'){ + if(e.details.cust_sip_record && e.details.cust_sip_record !== ''){x.pipe += ' ' + e.details.cust_sip_record} if(e.details.input_map_choices&&e.details.input_map_choices.detector_sip_buffer){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector_sip_buffer) @@ -809,7 +855,6 @@ module.exports = function(s,config,onFinish){ if(!e.details.detector_buffer_hls_list_size||e.details.detector_buffer_hls_list_size===''){e.details.detector_buffer_hls_list_size='4'} if(!e.details.detector_buffer_start_number||e.details.detector_buffer_start_number===''){e.details.detector_buffer_start_number='0'} if(!e.details.detector_buffer_live_start_index||e.details.detector_buffer_live_start_index===''){e.details.detector_buffer_live_start_index='-3'} - if(e.details.detector_buffer_vcodec.indexOf('_vaapi')>-1){ if(x.hwaccel.indexOf('-vaapi_device')>-1){ x.detector_buffer_filters.push('format=nv12') @@ -830,7 +875,13 @@ module.exports = function(s,config,onFinish){ if(x.detector_buffer_filters.length>0){ x.pipe+=' -vf '+x.detector_buffer_filters.join(',') } - x.pipe+=x.detector_buffer_fps+x.detector_buffer_acodec+' -c:v '+e.details.detector_buffer_vcodec+' -f hls -tune '+e.details.detector_buffer_tune+' -g '+e.details.detector_buffer_g+' -hls_time '+e.details.detector_buffer_hls_time+' -hls_list_size '+e.details.detector_buffer_hls_list_size+' -start_number '+e.details.detector_buffer_start_number+' -live_start_index '+e.details.detector_buffer_live_start_index+' -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'detectorStream.m3u8"' + x.pipe += x.detector_buffer_fps+x.detector_buffer_acodec+' -c:v '+e.details.detector_buffer_vcodec+' -f hls -tune '+e.details.detector_buffer_tune+' -g '+e.details.detector_buffer_g+' -hls_time '+e.details.detector_buffer_hls_time+' -hls_list_size '+e.details.detector_buffer_hls_list_size+' -start_number '+e.details.detector_buffer_start_number+' -live_start_index '+e.details.detector_buffer_live_start_index+' -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'detectorStream.m3u8"' + } + } + ffmpeg.buildCoProcessorFeed = function(e,x){ + if(e.coProcessor === true){ + // the coProcessor ffmpeg consumes this HLS stream (no audio, frames only) + x.pipe += ' -q:v 1 -an -c:v copy -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'coProcessor.m3u8"' } } ffmpeg.assembleMainPieces = function(e,x){ @@ -849,6 +900,10 @@ module.exports = function(s,config,onFinish){ case'mjpeg': x.ffmpegCommandString += ' -reconnect 1 -f mjpeg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"'; break; + case'rtmp': + if(!e.details.rtmp_key)e.details.rtmp_key = '' + x.ffmpegCommandString += x.cust_input+x.hwaccel+` -i "rtmp://127.0.0.1:1935/${e.ke + '_' + e.mid + '_' + e.details.rtmp_key}"`; + break; case'h264':case'hls':case'mp4': x.ffmpegCommandString += x.cust_input+x.hwaccel+' -i "'+e.url+'"'; break; @@ -883,7 +938,12 @@ module.exports = function(s,config,onFinish){ ffmpeg.buildMainInput(e,x) ffmpeg.buildMainStream(e,x) ffmpeg.buildMainRecording(e,x) + ffmpeg.buildAudioDetector(e,x) ffmpeg.buildMainDetector(e,x) + ffmpeg.buildCoProcessorFeed(e,x) + s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){ + extender(e,x) + }) ffmpeg.assembleMainPieces(e,x) ffmpeg.createPipeArray(e,x) //hold ffmpeg command for log stream @@ -895,9 +955,9 @@ module.exports = function(s,config,onFinish){ } if(!config.ffmpegDir){ ffmpeg.checkForWindows(function(){ - ffmpeg.checkForUnix(function(){ - ffmpeg.checkForFfbinary(function(){ - ffmpeg.checkForNpmStatic(function(){ + ffmpeg.checkForFfbinary(function(){ + ffmpeg.checkForNpmStatic(function(){ + ffmpeg.checkForUnix(function(){ console.log('No FFmpeg found.') }) }) diff --git a/libs/ffmpegCoProcessor.js b/libs/ffmpegCoProcessor.js new file mode 100644 index 0000000..6010cbe --- /dev/null +++ b/libs/ffmpegCoProcessor.js @@ -0,0 +1,210 @@ +var spawn = require('child_process').spawn; +module.exports = function(s,config,lang,ffmpeg){ + ffmpeg.buildCoProcessorInput = function(e,x){ + if(e.details.userLoglevel&&e.details.userLoglevel!==''){x.loglevel='-loglevel '+e.details.userLoglevel;}else{x.loglevel='-loglevel error'} + x.input = x.loglevel+' -re -i '+e.sdir+'coProcessor.m3u8' + } + ffmpeg.buildCoProcessorStream = function(e,x){ + x.stream_video_filters = [] + //stream - timestamp + if(e.details.stream_timestamp&&e.details.stream_timestamp=="1"&&e.details.vcodec!=='copy'){ + //font + if(e.details.stream_timestamp_font&&e.details.stream_timestamp_font!==''){x.stream_timestamp_font=e.details.stream_timestamp_font}else{x.stream_timestamp_font='/usr/share/fonts/truetype/freefont/FreeSans.ttf'} + //position x + if(e.details.stream_timestamp_x&&e.details.stream_timestamp_x!==''){x.stream_timestamp_x=e.details.stream_timestamp_x}else{x.stream_timestamp_x='(w-tw)/2'} + //position y + if(e.details.stream_timestamp_y&&e.details.stream_timestamp_y!==''){x.stream_timestamp_y=e.details.stream_timestamp_y}else{x.stream_timestamp_y='0'} + //text color + if(e.details.stream_timestamp_color&&e.details.stream_timestamp_color!==''){x.stream_timestamp_color=e.details.stream_timestamp_color}else{x.stream_timestamp_color='white'} + //box color + if(e.details.stream_timestamp_box_color&&e.details.stream_timestamp_box_color!==''){x.stream_timestamp_box_color=e.details.stream_timestamp_box_color}else{x.stream_timestamp_box_color='0x00000000@1'} + //text size + if(e.details.stream_timestamp_font_size&&e.details.stream_timestamp_font_size!==''){x.stream_timestamp_font_size=e.details.stream_timestamp_font_size}else{x.stream_timestamp_font_size='10'} + + x.stream_video_filters.push('drawtext=fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size); + } + //stream - watermark for -vf + if(e.details.stream_watermark&&e.details.stream_watermark=="1"&&e.details.stream_watermark_location&&e.details.stream_watermark_location!==''){ + switch(e.details.stream_watermark_position){ + case'tl'://top left + x.stream_watermark_position='10:10' + break; + case'tr'://top right + x.stream_watermark_position='main_w-overlay_w-10:10' + break; + case'bl'://bottom left + x.stream_watermark_position='10:main_h-overlay_h-10' + break; + default://bottom right + x.stream_watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2' + break; + } + x.stream_video_filters.push('movie='+e.details.stream_watermark_location+'[watermark],[in][watermark]overlay='+x.stream_watermark_position+'[out]'); + } + //stream - rotation + if(e.details.rotate_stream&&e.details.rotate_stream!==""&&e.details.rotate_stream!=="no"&&e.details.stream_vcodec!=='copy'){ + x.stream_video_filters.push('transpose='+e.details.rotate_stream); + } + if(e.details.svf&&e.details.svf!==''){ + x.stream_video_filters.push(e.details.svf) + } + if(x.stream_video_filters.length>0){ + x.stream_video_filters=' -vf '+x.stream_video_filters.join(',') + }else{ + x.stream_video_filters='' + } + if(e.details.cust_stream&&e.details.cust_stream!==''){x.cust_stream=' '+e.details.cust_stream}else{x.cust_stream=''} + if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps=''} + if(e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64'){ + x.cust_stream += x.stream_fps + } + switch(e.details.stream_type){ + case'mjpeg': + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe += ' -an -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+' pipe:1'; + break; + case'b64':case'':case undefined:case null://base64 + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe += ' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + break; + } + } + ffmpeg.buildCoProcessorDetector = function(e,x){ + //detector frames + x.cust_detect=' ' + if(e.details.detector === '1'){ + if(e.details.detector_fps && e.details.detector_fps !== ''){ + x.detector_fps = e.details.detector_fps + }else{ + x.detector_fps = '2' + } + if(e.details.detector_scale_x && e.details.detector_scale_x !== '' && e.details.detector_scale_y && e.details.detector_scale_y !== ''){ + x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y + }else{ + x.dratio=' -s 320x240' + } + + if(e.details.cust_detect&&e.details.cust_detect!==''){x.cust_detect+=e.details.cust_detect;} + if(e.details.detector_pam==='1'){ + x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe -r '+x.detector_fps+x.cust_detect+x.dratio+' pipe:3' + if(e.details.detector_use_detect_object === '1'){ + if(e.details.detector_use_motion === '1'){ + if(e.details.detector_scale_x_object && e.details.detector_scale_x_object !== '' && e.details.detector_scale_y_object && e.details.detector_scale_y_object !== ''){ + x.dratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object + } + if(e.details.detector_fps_object && e.details.detector_fps_object !== ''){ + x.detector_fps = e.details.detector_fps_object + } + } + //for object detection + x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) + x.pipe += ' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:4'; + } + }else{ + x.pipe+=' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:3'; + } + } + } + ffmpeg.buildCoProcessorJpegApi = function(e,x){ + //snapshot frames + if(e.details.snap === '1'){ + if(!e.details.snap_fps || e.details.snap_fps === ''){e.details.snap_fps = 1} + if(e.details.snap_vf && e.details.snap_vf !== ''){x.snap_vf=' -vf '+e.details.snap_vf}else{x.snap_vf=''} + if(e.details.snap_scale_x && e.details.snap_scale_x !== '' && e.details.snap_scale_y && e.details.snap_scale_y !== ''){x.snap_ratio = ' -s '+e.details.snap_scale_x+'x'+e.details.snap_scale_y}else{x.snap_ratio=''} + if(e.details.cust_snap && e.details.cust_snap !== ''){x.cust_snap = ' '+e.details.cust_snap}else{x.cust_snap=''} + x.pipe += ' -update 1 -r '+e.details.snap_fps+x.cust_snap+x.snap_ratio+x.snap_vf+' "'+e.sdir+'s.jpg" -y'; + } + } + ffmpeg.buildCoProcessorPipeArray = function(e,x){ + x.stdioPipes = []; + var times = config.pipeAddition; + if(e.details.stream_channels){ + times+=e.details.stream_channels.length + } + for(var i=0; i < times; i++){ + x.stdioPipes.push('pipe') + } + } + s.ffmpegCoProcessor = function(e){ + if(e.coProcessor === false)return; + var x = {} + x.pipe = '' + ffmpeg.buildCoProcessorInput(e,x) + ffmpeg.buildCoProcessorStream(e,x) + ffmpeg.buildCoProcessorDetector(e,x) + ffmpeg.buildCoProcessorJpegApi(e,x) + ffmpeg.buildCoProcessorPipeArray(e,x) + var commandString = x.input + x.pipe + if(commandString === x.input){ + return false + } + s.group[e.ke].mon[e.mid].coProcessorCmd = commandString + return spawn(config.ffmpegDir,s.splitForFFPMEG((commandString).replace(/\s+/g,' ').trim()),{detached: true,stdio:x.stdioPipes}) + } + s.coSpawnLauncher = function(e){ + if(s.group[e.ke].mon[e.id].isStarted === true && e.coProcessor === true){ + s.coSpawnClose(e) + s.group[e.ke].mon[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e) + if(s.group[e.ke].mon[e.id].coSpawnProcessor === false){ + return + } + s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}}); + s.group[e.ke].mon[e.id].coSpawnProcessorExit = function(){ + s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}}); + setTimeout(function(){ + s.coSpawnLauncher(e) + },2000) + } + s.group[e.ke].mon[e.id].coSpawnProcessor.on('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit) + s.group[e.ke].mon[e.id].coSpawnProcessor.on('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit) + var checkLog = function(d,x){return d.indexOf(x)>-1;} + s.group[e.ke].mon[e.id].coSpawnProcessor.stderr.on('data',function(d){ + d=d.toString(); + switch(true){ + case checkLog(d,'deprecated pixel format used'): + case checkLog(d,'[hls @'): + case checkLog(d,'Past duration'): + case checkLog(d,'Last message repeated'): + case checkLog(d,'pkt->duration = 0'): + case checkLog(d,'Non-monotonous DTS'): + case checkLog(d,'NULL @'): + return + break; + } + s.userLog(e,{type:lang.coProcessor,msg:d}); + }) + if(e.frame_to_stream){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream) + } + if(e.details.detector === '1'){ + s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke}) + //frames from motion detect + if(e.details.detector_pam === '1'){ + s.createPamDiffEngine(e) + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].mon[e.id].p2p).pipe(s.group[e.ke].mon[e.id].pamDiff) + if(e.details.detector_use_detect_object === '1'){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[4].on('data',function(d){ + s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d + }) + } + }else if(s.isAtleatOneDetectorPluginConnected){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].on('data',function(d){ + s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d}); + }) + } + } + } + } + s.coSpawnClose = function(e){ + if(s.group[e.ke].mon[e.id].coSpawnProcessor){ + s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit); + s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit); + s.group[e.ke].mon[e.id].coSpawnProcessor.stdin.pause() + s.group[e.ke].mon[e.id].coSpawnProcessor.kill() + delete(s.group[e.ke].mon[e.id].coSpawnProcessor) + s.userLog(e,{type:lang['coProcessor Stopped'],msg:{msg:lang.coProcessorTextStopped+' : '+e.id}}); + } + } +} diff --git a/libs/language.js b/libs/language.js index 93fbe72..f58232d 100644 --- a/libs/language.js +++ b/libs/language.js @@ -18,42 +18,50 @@ module.exports = function(s,config){ var definitions = require(s.location.definitions+'/en_CA.json'); } //load languages dynamically + s.copySystemDefaultLanguage = function(){ + //en_CA + return Object.assign(lang,{}) + } s.loadedLanguages={} - s.loadedLanguages[config.language]=lang; + s.loadedLanguages[config.language] = s.copySystemDefaultLanguage() s.getLanguageFile = function(rule){ if(rule && rule !== ''){ var file = s.loadedLanguages[file] if(!file){ try{ s.loadedLanguages[rule] = require(s.location.languages+'/'+rule+'.json') - s.loadedLanguages[rule] = Object.assign(lang,s.loadedLanguages[rule]) + s.loadedLanguages[rule] = Object.assign(s.copySystemDefaultLanguage(),s.loadedLanguages[rule]) file = s.loadedLanguages[rule] }catch(err){ - file = lang + file = s.copySystemDefaultLanguage() } } }else{ - file = lang + file = s.copySystemDefaultLanguage() } return file } //load defintions dynamically + s.copySystemDefaultDefinitions = function(){ + //en_CA + return Object.assign(definitions,{}) + } s.loadedDefinitons={} - s.loadedDefinitons[config.language]=definitions; + s.loadedDefinitons[config.language] = s.copySystemDefaultDefinitions() s.getDefinitonFile = function(rule){ if(rule && rule !== ''){ var file = s.loadedDefinitons[file] if(!file){ try{ s.loadedDefinitons[rule] = require(s.location.definitions+'/'+rule+'.json') - s.loadedDefinitons[rule] = Object.assign(definitions,s.loadedDefinitons[rule]) + s.loadedDefinitons[rule] = Object.assign(s.copySystemDefaultDefinitions(),s.loadedDefinitons[rule]) file = s.loadedDefinitons[rule] }catch(err){ - file = definitions + file = s.copySystemDefaultDefinitions() } } }else{ - file = definitions + file = s.copySystemDefaultDefinitions() } return file } diff --git a/libs/monitor.js b/libs/monitor.js index 1c0a425..f1820b4 100644 --- a/libs/monitor.js +++ b/libs/monitor.js @@ -6,6 +6,7 @@ var Mp4Frag = require('mp4frag'); var onvif = require('node-onvif'); var request = require('request'); var connectionTester = require('connection-tester') +var SoundDetection = require('shinobi-sound-detection') var URL = require('url') module.exports = function(s,config,lang){ s.initiateMonitorObject = function(e){ @@ -21,6 +22,7 @@ module.exports = function(s,config,lang){ if(!s.group[e.ke].mon[e.mid].eventBasedRecording){s.group[e.ke].mon[e.mid].eventBasedRecording={}}; if(!s.group[e.ke].mon[e.mid].watch){s.group[e.ke].mon[e.mid].watch={}}; if(!s.group[e.ke].mon[e.mid].fixingVideos){s.group[e.ke].mon[e.mid].fixingVideos={}}; + if(!s.group[e.ke].mon[e.mid].parsedObjects){s.group[e.ke].mon[e.mid].parsedObjects={}}; if(!s.group[e.ke].mon[e.mid].isStarted){s.group[e.ke].mon[e.mid].isStarted = false}; if(s.group[e.ke].mon[e.mid].delete){clearTimeout(s.group[e.ke].mon[e.mid].delete)} if(!s.group[e.ke].mon_conf){s.group[e.ke].mon_conf={}} @@ -103,7 +105,7 @@ module.exports = function(s,config,lang){ var snapBuffer = [] var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f image2pipe pipe:1').split(' '),{detached: true}) snapProcess.stdout.on('data',function(data){ - snapBuffer.push(data) + if(snapBuffer)snapBuffer.push(data) }) snapProcess.stderr.on('data',function(data){ console.log(data.toString()) @@ -163,6 +165,9 @@ module.exports = function(s,config,lang){ var streamDirItems = fs.readdirSync(pathDir) var items = [] var copiedItems = [] + var videoLength = s.group[monitor.ke].mon_conf[monitor.id].details.detector_send_video_length + if(!videoLength || videoLength === '')videoLength = '10' + if(videoLength.length === 1)videoLength = '0' + videoLength var createMerged = function(copiedItems){ var allts = pathDir+items.join('_') fs.stat(allts,function(err,stats){ @@ -170,7 +175,7 @@ module.exports = function(s,config,lang){ //not exist var cat = 'cat '+copiedItems.join(' ')+' > '+allts exec(cat,function(){ - var merger = spawn(config.ffmpegDir,s.splitForFFPMEG(('-re -i '+allts+' -acodec copy -vcodec copy '+pathDir+mergedFile))) + var merger = spawn(config.ffmpegDir,s.splitForFFPMEG(('-re -i '+allts+' -acodec copy -vcodec copy -t 00:00:' + videoLength + ' '+pathDir+mergedFile))) merger.stderr.on('data',function(data){ s.userLog(monitor,{type:"Buffer Merge",msg:data.toString()}) }) @@ -198,7 +203,7 @@ module.exports = function(s,config,lang){ } }) items.sort() - items = items.slice(items.length - 5,items.length) + // items = items.slice(items.length - 5,items.length) items.forEach(function(filename){ try{ var tempFilename = filename.split('.') @@ -218,6 +223,64 @@ module.exports = function(s,config,lang){ }) return items } + s.mergeRecordedVideos = function(videoRows,groupKey,callback){ + var tempDir = s.dir.streams + groupKey + '/' + var pathDir = s.dir.fileBin + groupKey + '/' + var streamDirItems = fs.readdirSync(pathDir) + var items = [] + var mergedFile = [] + videoRows.forEach(function(video){ + var filepath = s.getVideoDirectory(video) + s.formattedTime(video.time) + '.' + video.ext + if( + filepath.indexOf('.mp4') > -1 + // || filename.indexOf('.webm') > -1 + ){ + mergedFile.push(s.formattedTime(video.time)) + items.push(filepath) + } + }) + mergedFile.sort() + mergedFile = mergedFile.join('_') + '.mp4' + var mergedFilepath = pathDir + mergedFile + var mergedRawFilepath = pathDir + 'raw_' + mergedFile + items.sort() + fs.stat(mergedFilepath,function(err,stats){ + if(err){ + //not exist + var tempScriptPath = tempDir + s.gid(5) + '.sh' + var cat = 'cat '+items.join(' ')+' > '+mergedRawFilepath + fs.writeFileSync(tempScriptPath,cat,'utf8') + exec('sh ' + tempScriptPath,function(){ + s.userLog({ + ke: groupKey, + mid: '$USER' + },{type:lang['Videos Merge'],msg:mergedFile}) + var merger = spawn(config.ffmpegDir,s.splitForFFPMEG(('-re -loglevel warning -i ' + mergedRawFilepath + ' -acodec copy -vcodec copy ' + mergedFilepath))) + merger.stderr.on('data',function(data){ + s.userLog({ + ke: groupKey, + mid: '$USER' + },{type:lang['Videos Merge'],msg:data.toString()}) + }) + merger.on('close',function(){ + s.file('delete',mergedRawFilepath) + s.file('delete',tempScriptPath) + setTimeout(function(){ + fs.stat(mergedFilepath,function(err,stats){ + if(!err)s.file('delete',mergedFilepath) + }) + },1000 * 60 * 60 * 24) + delete(merger) + callback(mergedFilepath,mergedFile) + }) + }) + }else{ + //file exist + callback(mergedFilepath,mergedFile) + } + }) + return items + } s.cameraDestroy = function(x,e,p){ if(s.group[e.ke]&&s.group[e.ke].mon[e.id]&&s.group[e.ke].mon[e.id].spawn !== undefined){ @@ -266,6 +329,7 @@ module.exports = function(s,config,lang){ if(s.group[e.ke].mon[e.id].childNode){ s.cx({f:'kill',d:s.cleanMonitorObject(e)},s.group[e.ke].mon[e.id].childNodeId) }else{ + s.coSpawnClose(e) if(!x||x===1){return}; p=x.pid; if(s.group[e.ke].mon_conf[e.id].type===('dashcam'||'socket'||'jpeg'||'pipe')){ @@ -282,12 +346,20 @@ module.exports = function(s,config,lang){ s.cameraCheckObjectsInDetails = function(e){ //parse Objects (['detector_cascades','cords','detector_filters','input_map_choices']).forEach(function(v){ - if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){ + if(e.details && e.details[v]){ try{ - if(e.details[v] === '') e.details[v] = '{}' - e.details[v]=JSON.parse(e.details[v]); - if(!e.details[v])e.details[v]={}; - s.group[e.ke].mon[e.id].details = e.details; + if(!e.details[v] || e.details[v] === '')e.details[v] = '{}' + e.details[v] = s.parseJSON(e.details[v]) + if(!e.details[v])e.details[v] = {} + s.group[e.ke].mon[e.id].details = e.details + switch(v){ + case'cords': + s.group[e.ke].mon[e.id].parsedObjects[v] = Object.values(s.parseJSON(e.details[v])) + break; + default: + s.group[e.ke].mon[e.id].parsedObjects[v] = s.parseJSON(e.details[v]) + break; + } }catch(err){ } @@ -595,6 +667,14 @@ module.exports = function(s,config,lang){ // exec('chmod -R 777 '+e.sdir,function(err){ // // }) + var binDir = s.dir.fileBin + e.ke + '/' + if (!fs.existsSync(binDir)){ + fs.mkdirSync(binDir) + } + binDir = s.dir.fileBin + e.ke + '/' + e.id + '/' + if (!fs.existsSync(binDir)){ + fs.mkdirSync(binDir) + } return setStreamDir } s.stripAuthFromHost = function(e){ @@ -741,6 +821,9 @@ module.exports = function(s,config,lang){ } s.fatalCameraError(e,'Process Unexpected Exit'); s.orphanedVideoCheck(e,2,null,true) + s.onMonitorUnexpectedExitExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) } } s.group[e.ke].mon[e.id].spawn.on('end',s.group[e.ke].mon[e.id].spawn_exit) @@ -773,7 +856,47 @@ module.exports = function(s,config,lang){ if(e.type==='jpeg'){ s.cameraPullJpegStream(e) } - if(e.details.detector === '1'){ + if(e.details.detector_audio === '1'){ + var triggerLevel + var triggerLevelMax + if(e.details.detector_audio_min_db && e.details.detector_audio_min_db !== ''){ + triggerLevel = parseInt(e.details.detector_audio_min_db) + }else{ + triggerLevel = 5 + } + if(e.details.detector_audio_max_db && e.details.detector_audio_max_db !== ''){ + triggerLevelMax = parseInt(e.details.detector_audio_max_db) + } + var audioDetector = new SoundDetection({ + format: { + bitDepth: 16, + numberOfChannels: 1, + signed: true + }, + triggerLevel: triggerLevel, + triggerLevelMax: triggerLevelMax + },function(dB) { + s.triggerEvent({ + f:'trigger', + id:e.id, + ke:e.ke, + name: 'db', + details:{ + plug:'audio', + name:'db', + reason:'soundChange', + confidence:dB + }, + plates:[], + imgHeight:e.details.detector_scale_y, + imgWidth:e.details.detector_scale_x + }) + }) + s.group[e.ke].mon[e.id].audioDetector = audioDetector + audioDetector.start() + s.group[e.ke].mon[e.id].spawn.stdio[6].pipe(audioDetector.streamDecoder) + } + if(e.details.detector === '1' && e.coProcessor === false){ s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke}) //frames from motion detect if(e.details.detector_pam === '1'){ @@ -784,34 +907,10 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d }) } - }else if(s.ocv){ - if(s.ocv.connectionType !== 'ram'){ - s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){ - s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d}); - }) - }else{ - s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){ - if(!s.group[e.ke].mon[e.id].detectorFrameSaveBuffer){ - s.group[e.ke].mon[e.id].detectorFrameSaveBuffer=[d] - }else{ - s.group[e.ke].mon[e.id].detectorFrameSaveBuffer.push(d) - } - if(d[d.length-2] === 0xFF && d[d.length-1] === 0xD9){ - var buffer = Buffer.concat(s.group[e.ke].mon[e.id].detectorFrameSaveBuffer); - var frameLocation = s.dir.streams + e.ke + '/' + e.id + '/' + s.gid(5) + '.jpg' - if(s.ocv){ - fs.writeFile(frameLocation,buffer,function(err){ - if(err){ - s.debugLog(err) - }else{ - s.ocvTx({f:'frameFromRam',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frameLocation:frameLocation}) - } - }) - } - s.group[e.ke].mon[e.id].detectorFrameSaveBuffer = null; - } - }) - } + }else if(s.isAtleatOneDetectorPluginConnected){ + s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){ + s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d}); + }) } } //frames to stream @@ -862,7 +961,11 @@ module.exports = function(s,config,lang){ break; } if(e.frameToStream){ - s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frameToStream) + if(e.coProcessor === true && e.details.stream_type === ('b64'||'mjpeg')){ + + }else{ + s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frameToStream) + } } if(e.details.stream_channels && e.details.stream_channels !== ''){ var createStreamEmitter = function(channel,number){ @@ -908,6 +1011,13 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.id].spawn.stderr.on('data',function(d){ d=d.toString(); switch(true){ + case checkLog(d,'No space left on device'): + s.checkUserPurgeLock(e.ke) + s.purgeDiskForGroup(e) + break; + case checkLog(d,'error while decoding'): + s.userLog(e,{type:lang['Error While Decoding'],msg:lang.ErrorWhileDecodingText}); + break; case checkLog(d,'[hls @'): case checkLog(d,'Past duration'): case checkLog(d,'Last message repeated'): @@ -935,10 +1045,11 @@ module.exports = function(s,config,lang){ case checkLog(d,'mjpeg_decode_dc'): case checkLog(d,'bad vlc'): case checkLog(d,'error dc'): + case checkLog(d,'No route to host'): s.launchMonitorProcesses(e) break; case /T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]./.test(d): - var filename = d.split('.')[0]+'.'+e.ext + var filename = d.split('.')[0].split(' [')[0].trim()+'.'+e.ext s.insertCompletedVideo(e,{ file : filename },function(err){ @@ -996,7 +1107,7 @@ module.exports = function(s,config,lang){ e.detector_notrigger_timeout = parseFloat(e.details.detector_notrigger_timeout)*1000*60; s.group[e.ke].mon[e.id].detector_notrigger_timeout_function = function(){ s.onDetectorNoTriggerTimeoutExtensions.forEach(function(extender){ - extender(r,e) + extender(e) }) } clearInterval(s.group[e.ke].mon[e.id].detector_notrigger_timeout) @@ -1009,7 +1120,11 @@ module.exports = function(s,config,lang){ if(s.group[e.ke].mon[e.id].isStarted === true){ fs.stat(e.sdir+'s.jpg',function(err,snap){ var notStreaming = function(){ - s.launchMonitorProcesses(e) + if(e.coProcessor === true){ + s.coSpawnLauncher(e) + }else{ + s.launchMonitorProcesses(e) + } s.userLog(e,{type:lang['Camera is not streaming'],msg:{msg:lang['Restarting Process']}}) s.orphanedVideoCheck(e,2,null,true) } @@ -1034,13 +1149,6 @@ module.exports = function(s,config,lang){ //check if ffmpeg is recording s.group[e.ke].mon[e.id].fswatch = fs.watch(e.dir, {encoding : 'utf8'}, (event, filename) => { switch(event){ - case'rename': - try{ - s.group[e.ke].mon[e.id].open = filename.split('.')[0] - }catch(err){ - s.debugLog('Failed to split filename : ',filename) - } - break; case'change': s.resetRecordingCheck(e) break; @@ -1094,11 +1202,22 @@ module.exports = function(s,config,lang){ ){ s.cameraFilterFfmpegLog(e) } + if(e.coProcessor === true){ + setTimeout(function(){ + s.coSpawnLauncher(e) + },6000) + } + s.onMonitorStartExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) }else{ + s.onMonitorPingFailedExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) s.userLog(e,{type:lang["Ping Failed"],msg:lang.skipPingText1}); s.fatalCameraError(e,"Ping Failed");return; - } - } + } + } if( e.type !== 'socket' && e.type !== 'dashcam' && @@ -1136,6 +1255,9 @@ module.exports = function(s,config,lang){ if(o.success === true){ startVideoProcessor() }else{ + s.onMonitorPingFailedExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) s.userLog(e,{type:lang["Ping Failed"],msg:lang.skipPingText1}); s.fatalCameraError(e,"Ping Failed");return; } @@ -1200,7 +1322,10 @@ module.exports = function(s,config,lang){ }else{ s.cameraDestroy(s.group[e.ke].mon[e.id].spawn,e) } - s.sendMonitorStatus({id:e.id,ke:e.ke,status:lang.Died}); + s.sendMonitorStatus({id:e.id,ke:e.ke,status:lang.Died}) + s.onMonitorDiedExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) } s.isWatchCountable = function(d){ try{ @@ -1307,6 +1432,9 @@ module.exports = function(s,config,lang){ } s.tx(txData,'GRP_'+form.ke) callback(!endData.ok,endData) + s.onMonitorSaveExtensions.forEach(function(extender){ + extender(Object.assign(s.group[form.ke].mon_conf[form.mid],{}),form,endData) + }) }) } s.camera = function(x,e,cn){ @@ -1399,6 +1527,9 @@ module.exports = function(s,config,lang){ var wantedStatus = lang.Idle } s.sendMonitorStatus({id:e.id,ke:e.ke,status:wantedStatus}) + s.onMonitorStopExtensions.forEach(function(extender){ + extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) + }) break; case'start':case'record'://watch or record monitor url s.initiateMonitorObject({ke:e.ke,mid:e.id}) @@ -1420,8 +1551,8 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.mid].isRecording = false } //set up fatal error handler - if(e.details.fatal_max===''){ - e.details.fatal_max = 10 + if(e.details.fatal_max === ''){ + e.details.fatal_max = 0 }else{ e.details.fatal_max = parseFloat(e.details.fatal_max) } @@ -1439,4 +1570,78 @@ module.exports = function(s,config,lang){ } if(typeof cn === 'function'){setTimeout(function(){cn()},1000)} } + // + s.activateMonitorStates = function(groupKey,stateName,user,callback){ + var endData = { + ok: false + } + s.findPreset([groupKey,'monitorStates',stateName],function(notFound,preset){ + if(notFound === false){ + var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND ' + var monitorQuery = [] + var sqlQueryValues = [groupKey] + var monitorPresets = {} + preset.details.monitors.forEach(function(monitor){ + monitorQuery.push('mid=?') + sqlQueryValues.push(monitor.mid) + monitorPresets[monitor.mid] = monitor + }) + sqlQuery += '('+monitorQuery.join(' OR ')+')' + s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){ + if(monitors && monitors[0]){ + monitors.forEach(function(monitor){ + s.checkDetails(monitor) + s.checkDetails(monitorPresets[monitor.mid]) + var monitorPreset = monitorPresets[monitor.mid] + monitorPreset.details = Object.assign(monitor.details,monitorPreset.details) + monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset)) + monitor.details = JSON.stringify(monitor.details) + s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){ + + },user) + }) + endData.ok = true + s.tx({f:'change_group_state',ke:groupKey,name:stateName},'GRP_'+groupKey) + callback(endData) + }else{ + endData.msg = user.lang['State Configuration has no monitors associated'] + callback(endData) + } + }) + }else{ + endData.msg = user.lang['State Configuration Not Found'] + callback(endData) + } + }) + } + s.getCamerasForMultiTrigger = function(monitor){ + var list={} + var cameras=[] + var group + try{ + group=JSON.parse(monitor.details.group_detector_multi) + if(!group){group=[]} + }catch(err){ + group=[] + } + group.forEach(function(b){ + Object.keys(s.group[monitor.ke].mon_conf).forEach(function(v){ + try{ + var groups = JSON.parse(s.group[monitor.ke].mon_conf[v].details.groups) + if(!groups){ + groups=[] + } + }catch(err){ + groups=[] + } + if(!list[v]&&groups.indexOf(b)>-1){ + list[v]={} + if(s.group[monitor.ke].mon_conf[v].mode !== 'stop'){ + cameras.push(Object.assign({},s.group[monitor.ke].mon_conf[v])) + } + } + }) + }) + return cameras + } } diff --git a/libs/notification.js b/libs/notification.js index 70118e9..9d24930 100644 --- a/libs/notification.js +++ b/libs/notification.js @@ -1,3 +1,4 @@ +var fs = require("fs") var Discord = require("discord.js") module.exports = function(s,config,lang){ //discord bot @@ -21,16 +22,27 @@ module.exports = function(s,config,lang){ text: "Shinobi Systems" } },data) - bot.channels.get(s.group[groupKey].init.discordbot_channel).send({ - embed: sendBody, - files: files - }).catch(err => { - if(err){ - s.userLog({ke:groupKey,mid:'$USER'},{type:lang.DiscordErrorText,msg:err}) - s.group[groupKey].discordBot = null - s.loadGroupApps({ke:groupKey}) - } - }) + var discordChannel = bot.channels.get(s.group[groupKey].init.discordbot_channel) + if(discordChannel && discordChannel.send){ + discordChannel.send({ + embed: sendBody, + files: files + }).catch(err => { + if(err){ + s.userLog({ke:groupKey,mid:'$USER'},{type:lang.DiscordErrorText,msg:err}) + s.group[groupKey].discordBot = null + s.loadGroupApps({ke:groupKey}) + } + }) + }else{ + s.userLog({ + ke: groupKey, + mid: '$USER' + },{ + type: lang.DiscordErrorText, + msg: 'Check the Channel ID' + }) + } } var onEventTriggerBeforeFilterForDiscord = function(d,filter){ filter.discord = true @@ -131,7 +143,13 @@ module.exports = function(s,config,lang){ ){ s.group[user.ke].discordBot = new Discord.Client() s.group[user.ke].discordBot.on('ready', () => { - console.log(`${user.mail} : Discord Bot Logged in as ${s.group[user.ke].discordBot.user.tag}!`) + s.userLog({ + ke: user.ke, + mid: '$USER' + },{ + type: lang.DiscordLoggedIn, + msg: s.group[user.ke].discordBot.user.tag + }) }) s.group[user.ke].discordBot.login(ar.discordbot_token) } diff --git a/libs/plugins.js b/libs/plugins.js index 24eaf51..e7f1edf 100644 --- a/libs/plugins.js +++ b/libs/plugins.js @@ -17,28 +17,76 @@ module.exports = function(s,config,lang){ break; } } - //multi plugin connections - s.connectedPlugins={} + s.connectedPlugins = {} + s.connectedDetectorPlugins = {} + s.detectorPluginArray = [] + s.isAtleatOneDetectorPluginConnected = false + s.addDetectorPlugin = function(name,d){ + s.connectedDetectorPlugins[d.plug] = { + started: s.timeObject(), + id: d.id, + plug: d.plug, + notice: d.notice, + connectionType: d.connectionType + } + s.resetDetectorPluginArray() + } + s.removeDetectorPlugin = function(name){ + delete(s.connectedDetectorPlugins[name]) + s.resetDetectorPluginArray(name) + } + s.resetDetectorPluginArray = function(){ + pluginArray = [] + Object.keys(s.connectedPlugins).forEach(function(name){ + var plugin = s.connectedPlugins[name] + if(plugin.plugged === true && plugin.type === 'detector'){ + pluginArray.push(name) + } + }) + if(pluginArray.length > 0)s.isAtleatOneDetectorPluginConnected = true + s.detectorPluginArray = pluginArray + } + s.sendToAllDetectors = function(data){ + s.detectorPluginArray.forEach(function(name){ + s.connectedPlugins[name].tx(data) + }) + } + s.sendDetectorInfoToClient = function(data,txFunction){ + s.detectorPluginArray.forEach(function(name){ + var detectorData = Object.assign(data,{ + notice: s.connectedDetectorPlugins[name].notice, + plug: name + }) + txFunction(detectorData) + }) + } + // s.sendToDetectorsInChain = function(){ + // + // } s.pluginInitiatorSuccess=function(mode,d,cn){ s.systemLog('pluginInitiatorSuccess',d) - if(mode==='client'){ - //is in client mode (camera.js is client) - cn.pluginEngine=d.plug - if(!s.connectedPlugins[d.plug]){ - s.connectedPlugins[d.plug]={plug:d.plug} + if(!s.connectedPlugins[d.plug]){ + s.connectedPlugins[d.plug]={ + plug: d.plug, + type: d.type } + } + s.connectedPlugins[d.plug].plugged = true + if(mode==='client'){ + s.connectedPlugins[d.plug].tx = function(x){return cn.emit('f',x)} + //is in client mode (camera.js is client) + cn.pluginEngine = d.plug s.systemLog('Connected to plugin : Detector - '+d.plug+' - '+d.type) switch(d.type){ default:case'detector': - s.ocv = { - started: s.timeObject(), + cn.detectorPlugin = d.plug + s.addDetectorPlugin(d.plug,{ id: cn.id, plug: d.plug, notice: d.notice, isClientPlugin: true, connectionType: d.connectionType - }; - cn.ocv = 1; + }) s.tx({f:'detector_plugged',plug:d.plug,notice:d.notice},'CPU') break; } @@ -46,21 +94,18 @@ module.exports = function(s,config,lang){ //is in host mode (camera.js is client) switch(d.type){ default:case'detector': - s.ocv = { - started:s.timeObject(), + s.addDetectorPlugin(d.plug,{ id:"host", plug:d.plug, notice:d.notice, isHostPlugin:true, connectionType: d.connectionType - }; + }) + s.tx({f:'detector_plugged',plug:d.plug,notice:d.notice},'CPU') break; } } - s.connectedPlugins[d.plug].plugged=true s.tx({f:'readPlugins',ke:d.ke},'CPU') - s.ocvTx({f:'api_key',key:d.plug}) - s.api[d.plug]={pluginEngine:d.plug,permissions:{},details:{},ip:'0.0.0.0'}; } s.pluginInitiatorFail=function(mode,d,cn){ if(s.connectedPlugins[d.plug])s.connectedPlugins[d.plug].plugged=false @@ -73,7 +118,10 @@ module.exports = function(s,config,lang){ } if(config.plugins&&config.plugins.length>0){ config.plugins.forEach(function(v){ - s.connectedPlugins[v.id]={plug:v.id} + s.connectedPlugins[v.id]={ + plug: v.id, + type: v.type + } if(v.enabled===false){return} if(v.mode==='host'){ //is in host mode (camera.js is client) @@ -102,7 +150,13 @@ module.exports = function(s,config,lang){ socket.on('ocv',s.pluginEventController); socket.on('disconnect', function(){ s.connectedPlugins[v.id].plugged=false - delete(s.api[v.id]) + if(v.type === 'detector'){ + s.tx({f:'detector_unplugged',plug:v.id},'CPU') + s.removeDetectorPlugin(v.id) + s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){ + s.tx(data,'CPU') + }) + } s.systemLog('Plugin Disconnected : '+v.id) s.connectedPlugins[v.id].reconnector = setInterval(function(){ if(socket.connected===true){ diff --git a/libs/process.js b/libs/process.js index 122d2f1..2ae560b 100644 --- a/libs/process.js +++ b/libs/process.js @@ -7,6 +7,9 @@ module.exports = function(process,__dirname){ }); // [CTRL] + [C] = exit process.on('SIGINT', function() { + s.onProcessExitExtensions.forEach(function(extender){ + extender() + }) console.log('Shinobi is Exiting...') process.exit(); }); diff --git a/libs/rtmpserver.js b/libs/rtmpserver.js new file mode 100644 index 0000000..2253a86 --- /dev/null +++ b/libs/rtmpserver.js @@ -0,0 +1,23 @@ +module.exports = function(s,config,lang){ + if(config.rtmpServer){ + var defaultRtmpServerConfig = { + port: 1935, + chunk_size: 60000, + gop_cache: true, + ping: 60, + ping_timeout: 30 + } + var runningRtmpServerConfig + if(config.rtmpServer instanceof Object === 'false'){ + runningRtmpServerConfig = defaultRtmpServerConfig + }else{ + runningRtmpServerConfig = Object.assign(defaultRtmpServerConfig,config.rtmpServer) + } + s.systemLog(`RTMP Server Running on port ${runningRtmpServerConfig.port}...`) + var NodeRtmpServer = require('./rtmpserver/node_rtmp_server') + var nmcs = new NodeRtmpServer({ + rtmp: runningRtmpServerConfig + }) + nmcs.run() + } +} diff --git a/libs/rtmpserver/node_core_amf.js b/libs/rtmpserver/node_core_amf.js new file mode 100644 index 0000000..18bca14 --- /dev/null +++ b/libs/rtmpserver/node_core_amf.js @@ -0,0 +1,1184 @@ +/** + * Created by delian on 3/12/14. + * This module provides encoding and decoding of the AMF0 and AMF3 format + */ +const Logger = require('./node_core_logger'); + +const amf3dRules = { + 0x00: amf3decUndefined, + 0x01: amf3decNull, + 0x02: amf3decFalse, + 0x03: amf3decTrue, + 0x04: amf3decInteger, + 0x05: amf3decDouble, + 0x06: amf3decString, + 0x07: amf3decXmlDoc, + 0x08: amf3decDate, + 0x09: amf3decArray, + 0x0A: amf3decObject, + 0x0B: amf3decXml, + 0x0C: amf3decByteArray //, + // 0x0D: amf3decVecInt, + // 0x0E: amf3decVecUInt, + // 0x0F: amf3decVecDouble, + // 0x10: amf3decVecObject, + // 0x11: amf3decDict // No dictionary support for the moment! +}; + +const amf3eRules = { + 'string': amf3encString, + 'integer': amf3encInteger, + 'double': amf3encDouble, + 'xml': amf3encXmlDoc, + 'object': amf3encObject, + 'array': amf3encArray, + 'sarray': amf3encArray, + 'binary': amf3encByteArray, + 'true': amf3encTrue, + 'false': amf3encFalse, + 'undefined': amf3encUndefined, + 'null': amf3encNull +}; + +const amf0dRules = { + 0x00: amf0decNumber, + 0x01: amf0decBool, + 0x02: amf0decString, + 0x03: amf0decObject, + // 0x04: amf0decMovie, // Reserved + 0x05: amf0decNull, + 0x06: amf0decUndefined, + 0x07: amf0decRef, + 0x08: amf0decArray, + // 0x09: amf0decObjEnd, // Should never happen normally + 0x0A: amf0decSArray, + 0x0B: amf0decDate, + 0x0C: amf0decLongString, + // 0x0D: amf0decUnsupported, // Has been never originally implemented by Adobe! + // 0x0E: amf0decRecSet, // Has been never originally implemented by Adobe! + 0x0F: amf0decXmlDoc, + 0x10: amf0decTypedObj, + 0x11: amf0decSwitchAmf3 +}; + +const amf0eRules = { + 'string': amf0encString, + 'integer': amf0encNumber, + 'double': amf0encNumber, + 'xml': amf0encXmlDoc, + 'object': amf0encObject, + 'array': amf0encArray, + 'sarray': amf0encSArray, + 'binary': amf0encString, + 'true': amf0encBool, + 'false': amf0encBool, + 'undefined': amf0encUndefined, + 'null': amf0encNull +}; + +function amfType(o) { + let jsType = typeof o; + + if (o === null) return 'null'; + if (jsType == 'undefined') return 'undefined'; + if (jsType == 'number') { + if (parseInt(o) == o) return 'integer'; + return 'double'; + } + if (jsType == 'boolean') return o ? 'true' : 'false'; + if (jsType == 'string') return 'string'; + if (jsType == 'object') { + if (o instanceof Array) { + if (o.sarray) return 'sarray'; + return 'array'; + } + return 'object'; + } + throw new Error('Unsupported type!') +} + +// AMF3 implementation + +/** + * AMF3 Decode undefined value + * @returns {{len: number, value: undefined}} + */ +function amf3decUndefined() { + return { len: 1, value: undefined } +} + +/** + * AMF3 Encode undefined value + * @returns {Buffer} + */ +function amf3encUndefined() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x00); + return buf; +} + +/** + * AMF3 Decode null + * @returns {{len: number, value: null}} + */ +function amf3decNull() { + return { len: 1, value: null } +} + +/** + * AMF3 Encode null + * @returns {Buffer} + */ +function amf3encNull() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x01); + return buf; +} + +/** + * AMF3 Decode false + * @returns {{len: number, value: boolean}} + */ +function amf3decFalse() { + return { len: 1, value: false } +} + +/** + * AMF3 Encode false + * @returns {Buffer} + */ +function amf3encFalse() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x02); + return buf; +} + +/** + * AMF3 Decode true + * @returns {{len: number, value: boolean}} + */ +function amf3decTrue() { + return { len: 1, value: true } +} + +/** + * AMF3 Encode true + * @returns {Buffer} + */ +function amf3encTrue() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x03); + return buf; +} + +/** + * Generic decode of AMF3 UInt29 values + * @param buf + * @returns {{len: number, value: number}} + */ +function amf3decUI29(buf) { + let val = 0; + let len = 1; + let b; + + do { + b = buf.readUInt8(len++); + val = (val << 7) + (b & 0x7F); + } while (len < 5 || b > 0x7F); + + if (len == 5) val = val | b; // Preserve the major bit of the last byte + + return { len: len, value: val } +} + +/** + * Generic encode of AMF3 UInt29 value + * @param num + * @returns {Buffer} + */ +function amf3encUI29(num) { + let len = 0; + if (num < 0x80) len = 1; + if (num < 0x4000) len = 2; + if (num < 0x200000) len = 3; + if (num >= 0x200000) len = 4; + let buf = Buffer.alloc(len); + switch (len) { + case 1: + buf.writeUInt8(num, 0); + break; + case 2: + buf.writeUInt8(num & 0x7F, 0); + buf.writeUInt8((num >> 7) | 0x80, 1); + break; + case 3: + buf.writeUInt8(num & 0x7F, 0); + buf.writeUInt8((num >> 7) & 0x7F, 1); + buf.writeUInt8((num >> 14) | 0x80, 2); + break; + case 4: + buf.writeUInt8(num & 0xFF, 0); + buf.writeUInt8((num >> 8) & 0x7F, 1); + buf.writeUInt8((num >> 15) | 0x7F, 2); + buf.writeUInt8((num >> 22) | 0x7F, 3); + break; + } + return buf; +} + +/** + * AMF3 Decode an integer + * @param buf + * @returns {{len: number, value: number}} + */ +function amf3decInteger(buf) { // Invert the integer + let resp = amf3decUI29(buf); + if (resp.value > 0x0FFFFFFF) resp.value = (resp.value & 0x0FFFFFFF) - 0x10000000; + return resp; +} + +/** + * AMF3 Encode an integer + * @param num + * @returns {Buffer} + */ +function amf3encInteger(num) { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x4, 0); + return Buffer.concat([buf, amf3encUI29(num & 0x3FFFFFFF)]); // This AND will auto convert the sign bit! +} + +/** + * AMF3 Decode String + * @param buf + * @returns {{len: *, value: (*|String)}} + */ +function amf3decString(buf) { + let sLen = amf3decUI29(buf); + let s = sLen & 1; + sLen = sLen >> 1; // The real length without the lowest bit + if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; + throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! +} + +/** + * AMF3 Encode String + * @param str + * @returns {Buffer} + */ +function amf3encString(str) { + let sLen = amf3encUI29(str.length << 1); + let buf = Buffer.alloc(1); + buf.writeUInt8(0x6, 0); + return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); +} + +/** + * AMF3 Decode XMLDoc + * @param buf + * @returns {{len: *, value: (*|String)}} + */ +function amf3decXmlDoc(buf) { + let sLen = amf3decUI29(buf); + let s = sLen & 1; + sLen = sLen >> 1; // The real length without the lowest bit + if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; + throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! +} + +/** + * AMF3 Encode XMLDoc + * @param str + * @returns {Buffer} + */ +function amf3encXmlDoc(str) { + let sLen = amf3encUI29(str.length << 1); + let buf = Buffer.alloc(1); + buf.writeUInt8(0x7, 0); + return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); +} + +/** + * AMF3 Decode Generic XML + * @param buf + * @returns {{len: *, value: (*|String)}} + */ +function amf3decXml(buf) { + let sLen = amf3decUI29(buf); + let s = sLen & 1; + sLen = sLen >> 1; // The real length without the lowest bit + if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5).toString('utf8') }; + throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! +} + +/** + * AMF3 Encode Generic XML + * @param str + * @returns {Buffer} + */ +function amf3encXml(str) { + let sLen = amf3encUI29(str.length << 1); + let buf = Buffer.alloc(1); + buf.writeUInt8(0x0B, 0); + return Buffer.concat([buf, sLen, Buffer.from(str, 'utf8')]); +} + +/** + * AMF3 Decide Byte Array + * @param buf + * @returns {{len: *, value: (Array|string|*|Buffer|Blob)}} + */ +function amf3decByteArray(buf) { + let sLen = amf3decUI29(buf); + let s = sLen & 1; // TODO: Check if we follow the same rule! + sLen = sLen >> 1; // The real length without the lowest bit + if (s) return { len: sLen.value + 5, value: buf.slice(5, sLen.value + 5) }; + throw new Error("Error, we have a need to decode a String that is a Reference"); // TODO: Implement references! +} + +/** + * AMF3 Encode Byte Array + * @param str + * @returns {Buffer} + */ +function amf3encByteArray(str) { + let sLen = amf3encUI29(str.length << 1); + let buf = Buffer.alloc(1); + buf.writeUInt8(0x0C, 0); + return Buffer.concat([buf, sLen, (typeof str == 'string') ? Buffer.from(str, 'binary') : str]); +} + +/** + * AMF3 Decode Double + * @param buf + * @returns {{len: number, value: (*|Number)}} + */ +function amf3decDouble(buf) { + return { len: 9, value: buf.readDoubleBE(1) } +} + +/** + * AMF3 Encode Double + * @param num + * @returns {Buffer} + */ +function amf3encDouble(num) { + let buf = Buffer.alloc(9); + buf.writeUInt8(0x05, 0); + buf.writeDoubleBE(num, 1); + return buf; +} + +/** + * AMF3 Decode Date + * @param buf + * @returns {{len: *, value: (*|Number)}} + */ +function amf3decDate(buf) { // The UI29 should be 1 + let uTz = amf3decUI29(buf); + let ts = buf.readDoubleBE(uTz.len); + return { len: uTz.len + 8, value: ts } +} + +/** + * AMF3 Encode Date + * @param ts + * @returns {Buffer} + */ +function amf3encDate(ts) { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x8, 0); + let tsBuf = Buffer.alloc(8); + tsBuf.writeDoubleBE(ts, 0); + return Buffer.concat([buf, amf3encUI29(1), tsBuf]); // We always do 1 +} + +/** + * AMF3 Decode Array + * @param buf + * @returns {{len: *, value: *}} + */ +function amf3decArray(buf) { + let count = amf3decUI29(buf.slice(1)); + let obj = amf3decObject(buf.slice(count.len)); + if (count.value % 2 == 1) throw new Error("This is a reference to another array, which currently we don't support!"); + return { len: count.len + obj.len, value: obj.value } +} + +/** + * AMF3 Encode Array + */ +function amf3encArray() { + throw new Error('Encoding arrays is not supported yet!'); // TODO: Implement encoding of arrays +} + +/** + * AMF3 Decode Object + * @param buf + */ +function amf3decObject(buf) { + let obj = {}; + let pos = 0; + return obj; +} + +/** + * AMF3 Encode Object + * @param o + */ +function amf3encObject(o) { + +} + +// AMF0 Implementation + +/** + * AMF0 Decode Number + * @param buf + * @returns {{len: number, value: (*|Number)}} + */ +function amf0decNumber(buf) { + return { len: 9, value: buf.readDoubleBE(1) } +} + +/** + * AMF0 Encode Number + * @param num + * @returns {Buffer} + */ +function amf0encNumber(num) { + let buf = Buffer.alloc(9); + buf.writeUInt8(0x00, 0); + buf.writeDoubleBE(num, 1); + return buf; +} + +/** + * AMF0 Decode Boolean + * @param buf + * @returns {{len: number, value: boolean}} + */ +function amf0decBool(buf) { + return { len: 2, value: (buf.readUInt8(1) != 0) } +} + +/** + * AMF0 Encode Boolean + * @param num + * @returns {Buffer} + */ +function amf0encBool(num) { + let buf = Buffer.alloc(2); + buf.writeUInt8(0x01, 0); + buf.writeUInt8((num ? 1 : 0), 1); + return buf; +} + +/** + * AMF0 Decode Null + * @returns {{len: number, value: null}} + */ +function amf0decNull() { + return { len: 1, value: null } +} + +/** + * AMF0 Encode Null + * @returns {Buffer} + */ +function amf0encNull() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x05, 0); + return buf; +} + +/** + * AMF0 Decode Undefined + * @returns {{len: number, value: undefined}} + */ +function amf0decUndefined() { + return { len: 1, value: undefined } +} + +/** + * AMF0 Encode Undefined + * @returns {Buffer} + */ +function amf0encUndefined() { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x06, 0); + return buf; +} + +/** + * AMF0 Decode Date + * @param buf + * @returns {{len: number, value: (*|Number)}} + */ +function amf0decDate(buf) { + // let s16 = buf.readInt16BE(1); + let ts = buf.readDoubleBE(3); + return { len: 11, value: ts } +} + +/** + * AMF0 Encode Date + * @param ts + * @returns {Buffer} + */ +function amf0encDate(ts) { + let buf = Buffer.alloc(11); + buf.writeUInt8(0x0B, 0); + buf.writeInt16BE(0, 1); + buf.writeDoubleBE(ts, 3); + return buf; +} + +/** + * AMF0 Decode Object + * @param buf + * @returns {{len: number, value: {}}} + */ +function amf0decObject(buf) { // TODO: Implement references! + let obj = {}; + let iBuf = buf.slice(1); + let len = 1; + // Logger.debug('ODec',iBuf.readUInt8(0)); + while (iBuf.readUInt8(0) != 0x09) { + // Logger.debug('Field', iBuf.readUInt8(0), iBuf); + let prop = amf0decUString(iBuf); + // Logger.debug('Got field for property', prop); + len += prop.len; + if(iBuf.length < prop.len) { + break; + } + if (iBuf.slice(prop.len).readUInt8(0) == 0x09) { + len++; + // Logger.debug('Found the end property'); + break; + } // END Object as value, we shall leave + if (prop.value == '') break; + let val = amf0DecodeOne(iBuf.slice(prop.len)); + // Logger.debug('Got field for value', val); + obj[prop.value] = val.value; + len += val.len; + iBuf = iBuf.slice(prop.len + val.len); + } + return { len: len, value: obj } +} + +/** + * AMF0 Encode Object + */ +function amf0encObject(o) { + if (typeof o !== 'object') return; + + let data = Buffer.alloc(1); + data.writeUInt8(0x03, 0); // Type object + let k; + for (k in o) { + data = Buffer.concat([data, amf0encUString(k), amf0EncodeOne(o[k])]); + } + let termCode = Buffer.alloc(1); + termCode.writeUInt8(0x09, 0); + return Buffer.concat([data, amf0encUString(''), termCode]); +} + +/** + * AMF0 Decode Reference + * @param buf + * @returns {{len: number, value: string}} + */ +function amf0decRef(buf) { + let index = buf.readUInt16BE(1); + return { len: 3, value: 'ref' + index } +} + +/** + * AMF0 Encode Reference + * @param index + * @returns {Buffer} + */ +function amf0encRef(index) { + let buf = Buffer.alloc(3); + buf.writeUInt8(0x07, 0); + buf.writeUInt16BE(index, 1); + return buf; +} + +/** + * AMF0 Decode String + * @param buf + * @returns {{len: *, value: (*|string|String)}} + */ +function amf0decString(buf) { + let sLen = buf.readUInt16BE(1); + return { len: 3 + sLen, value: buf.toString('utf8', 3, 3 + sLen) } +} + +/** + * AMF0 Decode Untyped (without the type byte) String + * @param buf + * @returns {{len: *, value: (*|string|String)}} + */ +function amf0decUString(buf) { + let sLen = buf.readUInt16BE(0); + return { len: 2 + sLen, value: buf.toString('utf8', 2, 2 + sLen) } +} + +/** + * Do AMD0 Encode of Untyped String + * @param s + * @returns {Buffer} + */ +function amf0encUString(str) { + let data = Buffer.from(str, 'utf8'); + let sLen = Buffer.alloc(2); + sLen.writeUInt16BE(data.length, 0); + return Buffer.concat([sLen, data]); +} + +/** + * AMF0 Encode String + * @param str + * @returns {Buffer} + */ +function amf0encString(str) { + let buf = Buffer.alloc(3); + buf.writeUInt8(0x02, 0); + buf.writeUInt16BE(str.length, 1); + return Buffer.concat([buf, Buffer.from(str, 'utf8')]); +} + +/** + * AMF0 Decode Long String + * @param buf + * @returns {{len: *, value: (*|string|String)}} + */ +function amf0decLongString(buf) { + let sLen = buf.readUInt32BE(1); + return { len: 5 + sLen, value: buf.toString('utf8', 5, 5 + sLen) } +} + +/** + * AMF0 Encode Long String + * @param str + * @returns {Buffer} + */ +function amf0encLongString(str) { + let buf = Buffer.alloc(5); + buf.writeUInt8(0x0C, 0); + buf.writeUInt32BE(str.length, 1); + return Buffer.concat([buf, Buffer.from(str, 'utf8')]); +} + +/** + * AMF0 Decode Array + * @param buf + * @returns {{len: *, value: ({}|*)}} + */ +function amf0decArray(buf) { + // let count = buf.readUInt32BE(1); + let obj = amf0decObject(buf.slice(4)); + return { len: 5 + obj.len, value: obj.value } +} + +/** + * AMF0 Encode Array + */ +function amf0encArray(a) { + let l = 0; + if (a instanceof Array) l = a.length; else l = Object.keys(a).length; + Logger.debug('Array encode', l, a); + let buf = Buffer.alloc(5); + buf.writeUInt8(8, 0); + buf.writeUInt32BE(l, 1); + let data = amf0encObject(a); + return Buffer.concat([buf, data.slice(1)]); +} + +/** + * AMF0 Encode Binary Array into binary Object + * @param aData + * @returns {Buffer} + */ +function amf0cnletray2Object(aData) { + let buf = Buffer.alloc(1); + buf.writeUInt8(0x3, 0); // Object id + return Buffer.concat([buf, aData.slice(5)]); +} + +/** + * AMF0 Encode Binary Object into binary Array + * @param oData + * @returns {Buffer} + */ +function amf0cnvObject2Array(oData) { + let buf = Buffer.alloc(5); + let o = amf0decObject(oData); + let l = Object.keys(o).length; + buf.writeUInt32BE(l, 1); + return Buffer.concat([buf, oData.slice(1)]); +} + +/** + * AMF0 Decode XMLDoc + * @param buf + * @returns {{len: *, value: (*|string|String)}} + */ +function amf0decXmlDoc(buf) { + let sLen = buf.readUInt16BE(1); + return { len: 3 + sLen, value: buf.toString('utf8', 3, 3 + sLen) } +} + +/** + * AMF0 Encode XMLDoc + * @param str + * @returns {Buffer} + */ +function amf0encXmlDoc(str) { // Essentially it is the same as string + let buf = Buffer.alloc(3); + buf.writeUInt8(0x0F, 0); + buf.writeUInt16BE(str.length, 1); + return Buffer.concat([buf, Buffer.from(str, 'utf8')]); +} + +/** + * AMF0 Decode Strict Array + * @param buf + * @returns {{len: number, value: Array}} + */ +function amf0decSArray(buf) { + let a = []; + let len = 5; + let ret; + for (let count = buf.readUInt32BE(1); count; count--) { + ret = amf0DecodeOne(buf.slice(len)); + a.push(ret.value); + len += ret.len; + } + return { len: len, value: amf0markSArray(a) } +} + +/** + * AMF0 Encode Strict Array + * @param a Array + */ +function amf0encSArray(a) { + Logger.debug('Do strict array!'); + let buf = Buffer.alloc(5); + buf.writeUInt8(0x0A, 0); + buf.writeUInt32BE(a.length, 1); + let i; + for (i = 0; i < a.length; i++) { + buf = Buffer.concat([buf, amf0EncodeOne(a[i])]); + } + return buf; +} + +function amf0markSArray(a) { + Object.defineProperty(a, 'sarray', { value: true }); + return a; +} + +/** + * AMF0 Decode Typed Object + * @param buf + * @returns {{len: number, value: ({}|*)}} + */ +function amf0decTypedObj(buf) { + let className = amf0decString(buf); + let obj = amf0decObject(buf.slice(className.len - 1)); + obj.value.__className__ = className.value; + return { len: className.len + obj.len - 1, value: obj.value } +} + +/** + * AMF0 Decode Switch AMF3 Object + * @param buf + * @returns {{len: number, value: ({}|*)}} + */ +function amf0decSwitchAmf3(buf) { + let r = amf3DecodeOne(buf.slice(1)); + return r; +} + +/** + * AMF0 Encode Typed Object + */ +function amf0encTypedObj() { + throw new Error("Error: SArray encoding is not yet implemented!"); // TODO: Error +} + +/** + * Decode one value from the Buffer according to the applied rules + * @param rules + * @param buffer + * @returns {*} + */ +function amfXDecodeOne(rules, buffer) { + if (!rules[buffer.readUInt8(0)]) { + Logger.error('Unknown field', buffer.readUInt8(0)); + return null; + } + return rules[buffer.readUInt8(0)](buffer); +} + +/** + * Decode one AMF0 value + * @param buffer + * @returns {*} + */ +function amf0DecodeOne(buffer) { + return amfXDecodeOne(amf0dRules, buffer); +} + +/** + * Decode one AMF3 value + * @param buffer + * @returns {*} + */ +function amf3DecodeOne(buffer) { + return amfXDecodeOne(amf3dRules, buffer); +} + +/** + * Decode a whole buffer of AMF values according to rules and return in array + * @param rules + * @param buffer + * @returns {Array} + */ +function amfXDecode(rules, buffer) { + // We shall receive clean buffer and will respond with an array of values + let resp = []; + let res; + for (let i = 0; i < buffer.length;) { + res = amfXDecodeOne(rules, buffer.slice(i)); + i += res.len; + resp.push(res.value); // Add the response + } + return resp; +} + +/** + * Decode a buffer of AMF3 values + * @param buffer + * @returns {Array} + */ +function amf3Decode(buffer) { + return amfXDecode(amf3dRules, buffer); +} + +/** + * Decode a buffer of AMF0 values + * @param buffer + * @returns {Array} + */ +function amf0Decode(buffer) { + return amfXDecode(amf0dRules, buffer); +} + +/** + * Encode one AMF value according to rules + * @param rules + * @param o + * @returns {*} + */ +function amfXEncodeOne(rules, o) { + // Logger.debug('amfXEncodeOne type',o,amfType(o),rules[amfType(o)]); + let f = rules[amfType(o)]; + if (f) return f(o); + throw new Error('Unsupported type for encoding!'); +} + +/** + * Encode one AMF0 value + * @param o + * @returns {*} + */ +function amf0EncodeOne(o) { + return amfXEncodeOne(amf0eRules, o); +} + +/** + * Encode one AMF3 value + * @param o + * @returns {*} + */ +function amf3EncodeOne(o) { + return amfXEncodeOne(amf3eRules, o); +} + +/** + * Encode an array of values into a buffer + * @param a + * @returns {Buffer} + */ +function amf3Encode(a) { + let buf = Buffer.alloc(0); + a.forEach(function (o) { + buf = Buffer.concat([buf, amf3EncodeOne(o)]); + }); + return buf; +} + +/** + * Encode an array of values into a buffer + * @param a + * @returns {Buffer} + */ +function amf0Encode(a) { + let buf = Buffer.alloc(0); + a.forEach(function (o) { + buf = Buffer.concat([buf, amf0EncodeOne(o)]); + }); + return buf; +} + + +const rtmpCmdCode = { + "_result": ["transId", "cmdObj", "info"], + "_error": ["transId", "cmdObj", "info", "streamId"], // Info / Streamid are optional + "onStatus": ["transId", "cmdObj", "info"], + "releaseStream": ["transId", "cmdObj", "streamName"], + "getStreamLength": ["transId", "cmdObj", "streamId"], + "getMovLen": ["transId", "cmdObj", "streamId"], + "FCPublish": ["transId", "cmdObj", "streamName"], + "FCUnpublish": ["transId", "cmdObj", "streamName"], + "onFCPublish": ["transId", "cmdObj", "info"], + "connect": ["transId", "cmdObj", "args"], + "call": ["transId", "cmdObj", "args"], + "createStream": ["transId", "cmdObj"], + "close": ["transId", "cmdObj"], + "play": ["transId", "cmdObj", "streamName", "start", "duration", "reset"], + "play2": ["transId", "cmdObj", "params"], + "deleteStream": ["transId", "cmdObj", "streamId"], + "closeStream": ["transId", "cmdObj"], + "receiveAudio": ["transId", "cmdObj", "bool"], + "receiveVideo": ["transId", "cmdObj", "bool"], + "publish": ["transId", "cmdObj", "streamName", "type"], + "seek": ["transId", "cmdObj", "ms"], + "pause": ["transId", "cmdObj", "pause", "ms"] +}; + +const rtmpDataCode = { + "@setDataFrame": ["method", "dataObj"], + "onMetaData": ["dataObj"], + "|RtmpSampleAccess": ["bool1", "bool2"], +}; + + +/** + * Decode a data! + * @param dbuf + * @returns {{cmd: (*|string|String|*), value: *}} + */ +function decodeAmf0Data(dbuf) { + let buffer = dbuf; + let resp = {}; + + let cmd = amf0DecodeOne(buffer); + if(cmd) { + resp.cmd = cmd.value; + buffer = buffer.slice(cmd.len); + + if (rtmpDataCode[cmd.value]) { + rtmpDataCode[cmd.value].forEach(function (n) { + if (buffer.length > 0) { + let r = amf0DecodeOne(buffer); + if(r) { + buffer = buffer.slice(r.len); + resp[n] = r.value; + } + } + }); + } else { + Logger.error('Unknown command', resp); + } + } + + return resp +} + +/** + * Decode a command! + * @param dbuf + * @returns {{cmd: (*|string|String|*), value: *}} + */ +function decodeAMF0Cmd(dbuf) { + let buffer = dbuf; + let resp = {}; + + let cmd = amf0DecodeOne(buffer); + resp.cmd = cmd.value; + buffer = buffer.slice(cmd.len); + + if (rtmpCmdCode[cmd.value]) { + rtmpCmdCode[cmd.value].forEach(function (n) { + if (buffer.length > 0) { + let r = amf0DecodeOne(buffer); + buffer = buffer.slice(r.len); + resp[n] = r.value; + } + }); + } else { + Logger.error('Unknown command', resp); + } + return resp +} + +/** + * Encode AMF0 Command + * @param opt + * @returns {*} + */ +function encodeAMF0Cmd(opt) { + let data = amf0EncodeOne(opt.cmd); + + if (rtmpCmdCode[opt.cmd]) { + rtmpCmdCode[opt.cmd].forEach(function (n) { + if (opt.hasOwnProperty(n)) + data = Buffer.concat([data, amf0EncodeOne(opt[n])]); + }); + } else { + Logger.error('Unknown command', opt); + } + // Logger.debug('Encoded as',data.toString('hex')); + return data +} + +function encodeAMF0Data(opt) { + let data = amf0EncodeOne(opt.cmd); + + if (rtmpDataCode[opt.cmd]) { + rtmpDataCode[opt.cmd].forEach(function (n) { + if (opt.hasOwnProperty(n)) + data = Buffer.concat([data, amf0EncodeOne(opt[n])]); + }); + } else { + Logger.error('Unknown data', opt); + } + // Logger.debug('Encoded as',data.toString('hex')); + return data +} + +/** + * + * @param dbuf + * @returns {{}} + */ +function decodeAMF3Cmd(dbuf) { + let buffer = dbuf; + let resp = {}; + + let cmd = amf3DecodeOne(buffer); + resp.cmd = cmd.value; + buffer = buffer.slice(cmd.len); + + if (rtmpCmdCode[cmd.value]) { + rtmpCmdCode[cmd.value].forEach(function (n) { + if (buffer.length > 0) { + let r = amf3DecodeOne(buffer); + buffer = buffer.slice(r.len); + resp[n] = r.value; + } + }); + } else { + Logger.error('Unknown command', resp); + } + return resp +} + +/** + * Encode AMF3 Command + * @param opt + * @returns {*} + */ +function encodeAMF3Cmd(opt) { + let data = amf0EncodeOne(opt.cmd); + + if (rtmpCmdCode[opt.cmd]) { + rtmpCmdCode[opt.cmd].forEach(function (n) { + if (opt.hasOwnProperty(n)) + data = Buffer.concat([data, amf3EncodeOne(opt[n])]); + }); + } else { + Logger.error('Unknown command', opt); + } + return data +} + +module.exports = { + decodeAmf3Cmd: decodeAMF3Cmd, + encodeAmf3Cmd: encodeAMF3Cmd, + decodeAmf0Cmd: decodeAMF0Cmd, + encodeAmf0Cmd: encodeAMF0Cmd, + decodeAmf0Data: decodeAmf0Data, + encodeAmf0Data: encodeAMF0Data, + amfType: amfType, + amf0Encode: amf0Encode, + amf0EncodeOne: amf0EncodeOne, + amf0Decode: amf0Decode, + amf0DecodeOne: amf0DecodeOne, + amf3Encode: amf3Encode, + amf3EncodeOne: amf3EncodeOne, + amf3Decode: amf3Decode, + amf3DecodeOne: amf3DecodeOne, + amf0cnvA2O: amf0cnletray2Object, + amf0cnvO2A: amf0cnvObject2Array, + amf0markSArray: amf0markSArray, + amf0decArray: amf0decArray, + amf0decBool: amf0decBool, + amf0decDate: amf0decDate, + amf0decLongString: amf0decLongString, + amf0decNull: amf0decNull, + amf0decNumber: amf0decNumber, + amf0decObject: amf0decObject, + amf0decRef: amf0decRef, + amf0decSArray: amf0decSArray, + amf0decString: amf0decString, + amf0decTypedObj: amf0decTypedObj, + amf0decUndefined: amf0decUndefined, + amf0decXmlDoc: amf0decXmlDoc, + amf0encArray: amf0encArray, + amf0encBool: amf0encBool, + amf0encDate: amf0encDate, + amf0encLongString: amf0encLongString, + amf0encNull: amf0encNull, + amf0encNumber: amf0encNumber, + amf0encObject: amf0encObject, + amf0encRef: amf0encRef, + amf0encSArray: amf0encSArray, + amf0encString: amf0encString, + amf0encTypedObj: amf0encTypedObj, + amf0encUndefined: amf0encUndefined, + amf0encXmlDoc: amf0encXmlDoc, + amf3decArray: amf3decArray, + amf3decByteArray: amf3decByteArray, + amf3decDate: amf3decDate, + amf3decDouble: amf3decDouble, + amf3decFalse: amf3decFalse, + amf3decInteger: amf3decInteger, + amf3decNull: amf3decNull, + amf3decObject: amf3decObject, + amf3decString: amf3decString, + amf3decTrue: amf3decTrue, + amf3decUI29: amf3decUI29, + amf3decUndefined: amf3decUndefined, + amf3decXml: amf3decXml, + amf3decXmlDoc: amf3decXmlDoc, + amf3encArray: amf3encArray, + amf3encByteArray: amf3encByteArray, + amf3encDate: amf3encDate, + amf3encDouble: amf3encDouble, + amf3encFalse: amf3encFalse, + amf3encInteger: amf3encInteger, + amf3encNull: amf3encNull, + amf3encObject: amf3encObject, + amf3encString: amf3encString, + amf3encTrue: amf3encTrue, + amf3encUI29: amf3encUI29, + amf3encUndefined: amf3encUndefined, + amf3encXml: amf3encXml, + amf3encXmlDoc: amf3encXmlDoc +}; \ No newline at end of file diff --git a/libs/rtmpserver/node_core_av.js b/libs/rtmpserver/node_core_av.js new file mode 100644 index 0000000..70b8bd7 --- /dev/null +++ b/libs/rtmpserver/node_core_av.js @@ -0,0 +1,501 @@ +// +// Created by Mingliang Chen on 17/12/21. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// + +const Bitop = require('./node_core_bitop'); +const AAC_SAMPLE_RATE = [ + 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, + 7350, 0, 0, 0 +]; + +const AAC_CHANNELS = [ + 0, 1, 2, 3, 4, 5, 6, 8 +]; + +const AUDIO_CODEC_NAME = [ + '', + 'ADPCM', + 'MP3', + 'LinearLE', + 'Nellymoser16', + 'Nellymoser8', + 'Nellymoser', + 'G711A', + 'G711U', + '', + 'AAC', + 'Speex', + '', + '', + 'MP3-8K', + 'DeviceSpecific', + 'Uncompressed' +]; + +const AUDIO_SOUND_RATE = [ + 5512, 11025, 22050, 44100 +]; + +const VIDEO_CODEC_NAME = [ + '', + 'Jpeg', + 'Sorenson-H263', + 'ScreenVideo', + 'On2-VP6', + 'On2-VP6-Alpha', + 'ScreenVideo2', + 'H264', + '', + '', + '', + '', + 'H265' +]; + +function getObjectType(bitop) { + let audioObjectType = bitop.read(5); + if (audioObjectType === 31) { + audioObjectType = bitop.read(6) + 32; + } + return audioObjectType; +} + +function getSampleRate(bitop, info) { + info.sampling_index = bitop.read(4); + return info.sampling_index == 0x0f ? bitop.read(24) : AAC_SAMPLE_RATE[info.sampling_index]; +} + +function readAACSpecificConfig(aacSequenceHeader) { + let info = {}; + let bitop = new Bitop(aacSequenceHeader); + bitop.read(16); + info.object_type = getObjectType(bitop); + info.sample_rate = getSampleRate(bitop, info); + info.chan_config = bitop.read(4); + if (info.chan_config < AAC_CHANNELS.length) { + info.channels = AAC_CHANNELS[info.chan_config]; + } + info.sbr = -1; + info.ps = -1; + if (info.object_type == 5 || info.object_type == 29) { + if (info.object_type == 29) { + info.ps = 1; + } + info.ext_object_type = 5; + info.sbr = 1; + info.sample_rate = getSampleRate(bitop, info); + info.object_type = getObjectType(bitop); + } + + return info; +} + +function getAACProfileName(info) { + switch (info.object_type) { + case 1: + return 'Main'; + case 2: + if (info.ps > 0) { + return 'HEv2'; + } + if (info.sbr > 0) { + return 'HE'; + } + return 'LC'; + case 3: + return 'SSR'; + case 4: + return 'LTP'; + case 5: + return 'SBR'; + default: + return ''; + } +} + +function readH264SpecificConfig(avcSequenceHeader) { + let info = {}; + let profile_idc, width, height, crop_left, crop_right, + crop_top, crop_bottom, frame_mbs_only, n, cf_idc, + num_ref_frames; + let bitop = new Bitop(avcSequenceHeader); + bitop.read(48); + info.width = 0; + info.height = 0; + + do { + info.profile = bitop.read(8); + info.compat = bitop.read(8); + info.level = bitop.read(8); + info.nalu = (bitop.read(8) & 0x03) + 1; + info.nb_sps = bitop.read(8) & 0x1F; + if (info.nb_sps == 0) { + break; + } + /* nal size */ + bitop.read(16); + + /* nal type */ + if (bitop.read(8) != 0x67) { + break; + } + /* SPS */ + profile_idc = bitop.read(8); + + /* flags */ + bitop.read(8); + + /* level idc */ + bitop.read(8); + + /* SPS id */ + bitop.read_golomb(); + + if (profile_idc == 100 || profile_idc == 110 || + profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || + profile_idc == 83 || profile_idc == 86 || profile_idc == 118) { + /* chroma format idc */ + cf_idc = bitop.read_golomb(); + + if (cf_idc == 3) { + + /* separate color plane */ + bitop.read(1); + } + + /* bit depth luma - 8 */ + bitop.read_golomb(); + + /* bit depth chroma - 8 */ + bitop.read_golomb(); + + /* qpprime y zero transform bypass */ + bitop.read(1); + + /* seq scaling matrix present */ + if (bitop.read(1)) { + + for (n = 0; n < (cf_idc != 3 ? 8 : 12); n++) { + + /* seq scaling list present */ + if (bitop.read(1)) { + + /* TODO: scaling_list() + if (n < 6) { + } else { + } + */ + } + } + } + } + + /* log2 max frame num */ + bitop.read_golomb(); + + /* pic order cnt type */ + switch (bitop.read_golomb()) { + case 0: + + /* max pic order cnt */ + bitop.read_golomb(); + break; + + case 1: + + /* delta pic order alwys zero */ + bitop.read(1); + + /* offset for non-ref pic */ + bitop.read_golomb(); + + /* offset for top to bottom field */ + bitop.read_golomb(); + + /* num ref frames in pic order */ + num_ref_frames = bitop.read_golomb(); + + for (n = 0; n < num_ref_frames; n++) { + + /* offset for ref frame */ + bitop.read_golomb(); + } + } + + /* num ref frames */ + info.avc_ref_frames = bitop.read_golomb(); + + /* gaps in frame num allowed */ + bitop.read(1); + + /* pic width in mbs - 1 */ + width = bitop.read_golomb(); + + /* pic height in map units - 1 */ + height = bitop.read_golomb(); + + /* frame mbs only flag */ + frame_mbs_only = bitop.read(1); + + if (!frame_mbs_only) { + + /* mbs adaprive frame field */ + bitop.read(1); + } + + /* direct 8x8 inference flag */ + bitop.read(1); + + /* frame cropping */ + if (bitop.read(1)) { + + crop_left = bitop.read_golomb(); + crop_right = bitop.read_golomb(); + crop_top = bitop.read_golomb(); + crop_bottom = bitop.read_golomb(); + + } else { + crop_left = 0; + crop_right = 0; + crop_top = 0; + crop_bottom = 0; + } + info.level = info.level / 10.0; + info.width = (width + 1) * 16 - (crop_left + crop_right) * 2; + info.height = (2 - frame_mbs_only) * (height + 1) * 16 - (crop_top + crop_bottom) * 2; + + } while (0); + + return info; +} + +function HEVCParsePtl(bitop, hevc, max_sub_layers_minus1) { + let general_ptl = {}; + + general_ptl.profile_space = bitop.read(2); + general_ptl.tier_flag = bitop.read(1); + general_ptl.profile_idc = bitop.read(5); + general_ptl.profile_compatibility_flags = bitop.read(32); + general_ptl.general_progressive_source_flag = bitop.read(1); + general_ptl.general_interlaced_source_flag = bitop.read(1); + general_ptl.general_non_packed_constraint_flag = bitop.read(1); + general_ptl.general_frame_only_constraint_flag = bitop.read(1); + bitop.read(32); + bitop.read(12); + general_ptl.level_idc = bitop.read(8); + + general_ptl.sub_layer_profile_present_flag = []; + general_ptl.sub_layer_level_present_flag = []; + + for (let i = 0; i < max_sub_layers_minus1; i++) { + general_ptl.sub_layer_profile_present_flag[i] = bitop.read(1); + general_ptl.sub_layer_level_present_flag[i] = bitop.read(1); + } + + if (max_sub_layers_minus1 > 0) { + for (let i = max_sub_layers_minus1; i < 8; i++) { + bitop.read(2) + } + } + + general_ptl.sub_layer_profile_space = []; + general_ptl.sub_layer_tier_flag = []; + general_ptl.sub_layer_profile_idc = []; + general_ptl.sub_layer_profile_compatibility_flag = []; + general_ptl.sub_layer_progressive_source_flag = []; + general_ptl.sub_layer_interlaced_source_flag = []; + general_ptl.sub_layer_non_packed_constraint_flag = []; + general_ptl.sub_layer_frame_only_constraint_flag = []; + general_ptl.sub_layer_level_idc = []; + + for (let i = 0; i < max_sub_layers_minus1; i++) { + if (general_ptl.sub_layer_profile_present_flag[i]) { + general_ptl.sub_layer_profile_space[i] = bitop.read(2); + general_ptl.sub_layer_tier_flag[i] = bitop.read(1); + general_ptl.sub_layer_profile_idc[i] = bitop.read(5); + general_ptl.sub_layer_profile_compatibility_flag[i] = bitop.read(32); + general_ptl.sub_layer_progressive_source_flag[i] = bitop.read(1); + general_ptl.sub_layer_interlaced_source_flag[i] = bitop.read(1); + general_ptl.sub_layer_non_packed_constraint_flag[i] = bitop.read(1); + general_ptl.sub_layer_frame_only_constraint_flag[i] = bitop.read(1); + bitop.read(32); + bitop.read(12); + } + if (general_ptl.sub_layer_level_present_flag[i]) { + general_ptl.sub_layer_level_idc[i] = bitop.read(8); + } + else { + general_ptl.sub_layer_level_idc[i] = 1; + } + } + return general_ptl; +} + +function HEVCParseSPS(SPS, hevc) { + let psps = {}; + let NumBytesInNALunit = SPS.length; + let NumBytesInRBSP = 0; + let rbsp_array = []; + let bitop = new Bitop(SPS); + + bitop.read(1);//forbidden_zero_bit + bitop.read(6);//nal_unit_type + bitop.read(6);//nuh_reserved_zero_6bits + bitop.read(3);//nuh_temporal_id_plus1 + + for (let i = 2; i < NumBytesInNALunit; i++) { + if (i + 2 < NumBytesInNALunit && bitop.look(24) == 0x000003) { + rbsp_array.push(bitop.read(8)); + rbsp_array.push(bitop.read(8)); + i += 2; + let emulation_prevention_three_byte = bitop.read(8); /* equal to 0x03 */ + } else { + rbsp_array.push(bitop.read(8)); + } + } + let rbsp = Buffer.from(rbsp_array); + let rbspBitop = new Bitop(rbsp); + psps.sps_video_parameter_set_id = rbspBitop.read(4); + psps.sps_max_sub_layers_minus1 = rbspBitop.read(3); + psps.sps_temporal_id_nesting_flag = rbspBitop.read(1); + psps.profile_tier_level = HEVCParsePtl(rbspBitop, hevc, psps.sps_max_sub_layers_minus1); + psps.sps_seq_parameter_set_id = rbspBitop.read_golomb(); + psps.chroma_format_idc = rbspBitop.read_golomb(); + if (psps.chroma_format_idc == 3) { + psps.separate_colour_plane_flag = rbspBitop.read(1); + } else { + psps.separate_colour_plane_flag = 0; + } + psps.pic_width_in_luma_samples = rbspBitop.read_golomb(); + psps.pic_height_in_luma_samples = rbspBitop.read_golomb(); + psps.conformance_window_flag = rbspBitop.read(1); + if (psps.conformance_window_flag) { + let vert_mult = 1 + (psps.chroma_format_idc < 2); + let horiz_mult = 1 + (psps.chroma_format_idc < 3); + psps.conf_win_left_offset = rbspBitop.read_golomb() * horiz_mult; + psps.conf_win_right_offset = rbspBitop.read_golomb() * horiz_mult; + psps.conf_win_top_offset = rbspBitop.read_golomb() * vert_mult; + psps.conf_win_bottom_offset = rbspBitop.read_golomb() * vert_mult; + } + // Logger.debug(psps); + return psps; +} + +function readHEVCSpecificConfig(hevcSequenceHeader) { + let info = {}; + info.width = 0; + info.height = 0; + info.profile = 0; + info.level = 0; + // let bitop = new Bitop(hevcSequenceHeader); + // bitop.read(48); + hevcSequenceHeader = hevcSequenceHeader.slice(5); + + do { + let hevc = {}; + if (hevcSequenceHeader.length < 23) { + break; + } + + hevc.configurationVersion = hevcSequenceHeader[0]; + if (hevc.configurationVersion != 1) { + break; + } + hevc.general_profile_space = (hevcSequenceHeader[1] >> 6) & 0x03; + hevc.general_tier_flag = (hevcSequenceHeader[1] >> 5) & 0x01; + hevc.general_profile_idc = hevcSequenceHeader[1] & 0x1F; + hevc.general_profile_compatibility_flags = (hevcSequenceHeader[2] << 24) | (hevcSequenceHeader[3] << 16) | (hevcSequenceHeader[4] << 8) | hevcSequenceHeader[5]; + hevc.general_constraint_indicator_flags = ((hevcSequenceHeader[6] << 24) | (hevcSequenceHeader[7] << 16) | (hevcSequenceHeader[8] << 8) | hevcSequenceHeader[9]); + hevc.general_constraint_indicator_flags = (hevc.general_constraint_indicator_flags << 16) | (hevcSequenceHeader[10] << 8) | hevcSequenceHeader[11]; + hevc.general_level_idc = hevcSequenceHeader[12]; + hevc.min_spatial_segmentation_idc = ((hevcSequenceHeader[13] & 0x0F) << 8) | hevcSequenceHeader[14]; + hevc.parallelismType = hevcSequenceHeader[15] & 0x03; + hevc.chromaFormat = hevcSequenceHeader[16] & 0x03; + hevc.bitDepthLumaMinus8 = hevcSequenceHeader[17] & 0x07; + hevc.bitDepthChromaMinus8 = hevcSequenceHeader[18] & 0x07; + hevc.avgFrameRate = (hevcSequenceHeader[19] << 8) | hevcSequenceHeader[20]; + hevc.constantFrameRate = (hevcSequenceHeader[21] >> 6) & 0x03; + hevc.numTemporalLayers = (hevcSequenceHeader[21] >> 3) & 0x07; + hevc.temporalIdNested = (hevcSequenceHeader[21] >> 2) & 0x01; + hevc.lengthSizeMinusOne = hevcSequenceHeader[21] & 0x03; + let numOfArrays = hevcSequenceHeader[22]; + let p = hevcSequenceHeader.slice(23); + for (let i = 0; i < numOfArrays; i++) { + if (p.length < 3) { + brak; + } + let nalutype = p[0]; + let n = (p[1]) << 8 | p[2]; + // Logger.debug(nalutype, n); + p = p.slice(3); + for (let j = 0; j < n; j++) { + if (p.length < 2) { + break; + } + k = (p[0] << 8) | p[1]; + // Logger.debug('k', k); + if (p.length < 2 + k) { + break; + } + p = p.slice(2); + if (nalutype == 33) { + //SPS + let sps = Buffer.alloc(k); + p.copy(sps, 0, 0, k); + // Logger.debug(sps, sps.length); + hevc.psps = HEVCParseSPS(sps, hevc); + info.profile = hevc.general_profile_idc; + info.level = hevc.general_level_idc / 30.0; + info.width = hevc.psps.pic_width_in_luma_samples - (hevc.psps.conf_win_left_offset + hevc.psps.conf_win_right_offset); + info.height = hevc.psps.pic_height_in_luma_samples - (hevc.psps.conf_win_top_offset + hevc.psps.conf_win_bottom_offset); + } + p = p.slice(k); + } + } + } while (0); + + return info; +} + +function readAVCSpecificConfig(avcSequenceHeader) { + let codec_id = avcSequenceHeader[0] & 0x0f; + if (codec_id == 7) { + return readH264SpecificConfig(avcSequenceHeader); + } else if (codec_id == 12) { + return readHEVCSpecificConfig(avcSequenceHeader); + } +} + + +function getAVCProfileName(info) { + switch (info.profile) { + case 1: + return 'Main'; + case 2: + return 'Main 10'; + case 3: + return 'Main Still Picture'; + case 66: + return 'Baseline'; + case 77: + return 'Main'; + case 100: + return 'High'; + default: + return ''; + } +} + +module.exports = { + AUDIO_SOUND_RATE, + AUDIO_CODEC_NAME, + VIDEO_CODEC_NAME, + readAACSpecificConfig, + getAACProfileName, + readAVCSpecificConfig, + getAVCProfileName, +}; diff --git a/libs/rtmpserver/node_core_bitop.js b/libs/rtmpserver/node_core_bitop.js new file mode 100644 index 0000000..3c4b0cb --- /dev/null +++ b/libs/rtmpserver/node_core_bitop.js @@ -0,0 +1,54 @@ + +class Bitop { + constructor(buffer) { + this.buffer = buffer; + this.buflen = buffer.length; + this.bufpos = 0; + this.bufoff = 0; + this.iserro = false; + } + + read(n) { + let v = 0; + let d = 0; + while (n) { + if (n < 0 || this.bufpos >= this.buflen) { + this.iserro = true; + return 0; + } + + this.iserro = false; + d = this.bufoff + n > 8 ? 8 - this.bufoff : n; + + v <<= d; + v += (this.buffer[this.bufpos] >> (8 - this.bufoff - d)) & (0xff >> (8 - d)) + + this.bufoff += d; + n -= d; + + if (this.bufoff == 8) { + this.bufpos++; + this.bufoff = 0; + } + } + return v; + } + + look(n) { + let p = this.bufpos; + let o = this.bufoff; + let v = this.read(n); + this.bufpos = p; + this.bufoff = o; + return v; + } + + read_golomb() { + let n; + for (n = 0; this.read(1) == 0 && !this.iserro; n++); + return (1 << n) + this.read(n) - 1; + } + } + + module.exports = Bitop; + \ No newline at end of file diff --git a/libs/rtmpserver/node_core_ctx.js b/libs/rtmpserver/node_core_ctx.js new file mode 100644 index 0000000..9ea36a8 --- /dev/null +++ b/libs/rtmpserver/node_core_ctx.js @@ -0,0 +1,17 @@ +// +// Created by Mingliang Chen on 18/3/2. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// +const EventEmitter = require('events'); + +let sessions = new Map(); +let publishers = new Map(); +let idlePlayers = new Set(); +let nodeEvent = new EventEmitter(); +let stat = { + inbytes: 0, + outbytes: 0, + accepted: 0 +}; +module.exports = { sessions, publishers, idlePlayers, nodeEvent, stat }; \ No newline at end of file diff --git a/libs/rtmpserver/node_core_logger.js b/libs/rtmpserver/node_core_logger.js new file mode 100644 index 0000000..332fc33 --- /dev/null +++ b/libs/rtmpserver/node_core_logger.js @@ -0,0 +1,53 @@ +const chalk = require('chalk'); + +LOG_TYPES = { + NONE: 0, + ERROR: 1, + NORMAL: 2, + DEBUG: 3, + FFDEBUG: 4 +}; + +let logType = LOG_TYPES.NORMAL; + +const setLogType = (type) => { + if (typeof type !== 'number') return; + + logType = type; +}; + +const logTime = () => { + let nowDate = new Date(); + return nowDate.toLocaleDateString() + ' ' + nowDate.toLocaleTimeString([], { hour12: false }); +}; + +const log = (...args) => { + if (logType < LOG_TYPES.NORMAL) return; + + console.log(logTime(), process.pid, chalk.bold.green('[INFO]'), ...args); +}; + +const error = (...args) => { + if (logType < LOG_TYPES.ERROR) return; + + console.log(logTime(), process.pid, chalk.bold.red('[ERROR]'), ...args); +}; + +const debug = (...args) => { + if (logType < LOG_TYPES.DEBUG) return; + + console.log(logTime(), process.pid, chalk.bold.blue('[DEBUG]'), ...args); +}; + +const ffdebug = (...args) => { + if (logType < LOG_TYPES.FFDEBUG) return; + + console.log(logTime(), process.pid, chalk.bold.blue('[FFDEBUG]'), ...args); +}; + +module.exports = { + LOG_TYPES, + setLogType, + + log, error, debug, ffdebug +} \ No newline at end of file diff --git a/libs/rtmpserver/node_core_utils.js b/libs/rtmpserver/node_core_utils.js new file mode 100644 index 0000000..da0e9fb --- /dev/null +++ b/libs/rtmpserver/node_core_utils.js @@ -0,0 +1,95 @@ +// +// Created by Mingliang Chen on 17/8/23. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// +const Crypto = require('crypto'); +const EventEmitter = require('events'); +const { spawn } = require('child_process'); +const readline = require('readline'); +const context = require('./node_core_ctx'); + +function generateNewSessionID() { + let sessionID = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWKYZ0123456789'; + const numPossible = possible.length; + do { + for (let i = 0; i < 8; i++) { + sessionID += possible.charAt((Math.random() * numPossible) | 0); + } + } while (context.sessions.has(sessionID)) + return sessionID; +} + +function genRandomName() { + let name = ''; + const possible = 'abcdefghijklmnopqrstuvwxyz0123456789'; + const numPossible = possible.length; + for (let i = 0; i < 4; i++) { + name += possible.charAt((Math.random() * numPossible) | 0); + } + + return name; +} + +function verifyAuth(signStr, streamId, secretKey) { + if (signStr === undefined) { + return false; + } + let now = Date.now() / 1000 | 0; + let exp = parseInt(signStr.split('-')[0]); + let shv = signStr.split('-')[1]; + let str = streamId + '-' + exp + '-' + secretKey; + if (exp < now) { + return false; + } + let md5 = Crypto.createHash('md5'); + let ohv = md5.update(str).digest('hex'); + return shv === ohv; +} + +function getFFmpegVersion(ffpath) { + return new Promise((resolve, reject) => { + let ffmpeg_exec = spawn(ffpath, ['-version']); + let version = ''; + ffmpeg_exec.on('error', (e) => { + reject(e); + }); + ffmpeg_exec.stdout.on('data', (data) => { + try { + version = data.toString().split(/(?:\r\n|\r|\n)/g)[0].split('\ ')[2]; + } catch (e) { + } + }); + ffmpeg_exec.on('close', (code) => { + resolve(version); + }); + }); +} + +function getFFmpegUrl() { + let url = ''; + switch (process.platform) { + case 'darwin': + url = 'https://ffmpeg.zeranoe.com/builds/macos64/static/ffmpeg-latest-macos64-static.zip'; + break; + case 'win32': + url = 'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip | https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-latest-win32-static.zip'; + break; + case 'linux': + url = 'https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz | https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-32bit-static.tar.xz'; + break; + default: + url = 'http://ffmpeg.org/download.html'; + break; + } + return url; +} + +module.exports = { + generateNewSessionID, + verifyAuth, + genRandomName, + getFFmpegVersion, + getFFmpegUrl +} diff --git a/libs/rtmpserver/node_rtmp_handshake.js b/libs/rtmpserver/node_rtmp_handshake.js new file mode 100644 index 0000000..2add436 --- /dev/null +++ b/libs/rtmpserver/node_rtmp_handshake.js @@ -0,0 +1,113 @@ +// +// Created by Mingliang Chen on 17/8/1. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// +// const Logger = require('./node_core_logger'); + +const Crypto = require('crypto'); + +const MESSAGE_FORMAT_0 = 0; +const MESSAGE_FORMAT_1 = 1; +const MESSAGE_FORMAT_2 = 2; + +const RTMP_SIG_SIZE = 1536; +const SHA256DL = 32; + +const RandomCrud = Buffer.from([ + 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, + 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, + 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae +]) + +const GenuineFMSConst = 'Genuine Adobe Flash Media Server 001'; +const GenuineFMSConstCrud = Buffer.concat([Buffer.from(GenuineFMSConst, 'utf8'), RandomCrud]); + +const GenuineFPConst = 'Genuine Adobe Flash Player 001'; +const GenuineFPConstCrud = Buffer.concat([Buffer.from(GenuineFPConst, 'utf8'), RandomCrud]); + +function calcHmac(data, key) { + let hmac = Crypto.createHmac('sha256', key); + hmac.update(data); + return hmac.digest(); +} + +function GetClientGenuineConstDigestOffset(buf) { + let offset = buf[0] + buf[1] + buf[2] + buf[3]; + offset = (offset % 728) + 12; + return offset; +} + +function GetServerGenuineConstDigestOffset(buf) { + let offset = buf[0] + buf[1] + buf[2] + buf[3]; + offset = (offset % 728) + 776; + return offset; +} + +function detectClientMessageFormat(clientsig) { + let computedSignature, msg, providedSignature, sdl; + sdl = GetServerGenuineConstDigestOffset(clientsig.slice(772, 776)); + msg = Buffer.concat([clientsig.slice(0, sdl), clientsig.slice(sdl + SHA256DL)], 1504); + computedSignature = calcHmac(msg, GenuineFPConst); + providedSignature = clientsig.slice(sdl, sdl + SHA256DL); + if (computedSignature.equals(providedSignature)) { + return MESSAGE_FORMAT_2; + } + sdl = GetClientGenuineConstDigestOffset(clientsig.slice(8, 12)); + msg = Buffer.concat([clientsig.slice(0, sdl), clientsig.slice(sdl + SHA256DL)], 1504); + computedSignature = calcHmac(msg, GenuineFPConst); + providedSignature = clientsig.slice(sdl, sdl + SHA256DL); + if (computedSignature.equals(providedSignature)) { + return MESSAGE_FORMAT_1; + } + return MESSAGE_FORMAT_0; +} + +function generateS1(messageFormat) { + let randomBytes = Crypto.randomBytes(RTMP_SIG_SIZE - 8); + let handshakeBytes = Buffer.concat([Buffer.from([0, 0, 0, 0, 1, 2, 3, 4]), randomBytes], RTMP_SIG_SIZE); + + let serverDigestOffset + if (messageFormat === 1) { + serverDigestOffset = GetClientGenuineConstDigestOffset(handshakeBytes.slice(8, 12)); + } else { + serverDigestOffset = GetServerGenuineConstDigestOffset(handshakeBytes.slice(772, 776)); + } + + msg = Buffer.concat([handshakeBytes.slice(0, serverDigestOffset), handshakeBytes.slice(serverDigestOffset + SHA256DL)], RTMP_SIG_SIZE - SHA256DL); + hash = calcHmac(msg, GenuineFMSConst); + hash.copy(handshakeBytes, serverDigestOffset, 0, 32); + return handshakeBytes; +} + +function generateS2(messageFormat, clientsig, callback) { + let randomBytes = Crypto.randomBytes(RTMP_SIG_SIZE - 32); + let challengeKeyOffset; + if (messageFormat === 1) { + challengeKeyOffset = GetClientGenuineConstDigestOffset(clientsig.slice(8, 12)); + } else { + challengeKeyOffset = GetServerGenuineConstDigestOffset(clientsig.slice(772, 776)); + } + let challengeKey = clientsig.slice(challengeKeyOffset, challengeKeyOffset + 32); + let hash = calcHmac(challengeKey, GenuineFMSConstCrud); + let signature = calcHmac(randomBytes, hash); + let s2Bytes = Buffer.concat([randomBytes, signature], RTMP_SIG_SIZE); + return s2Bytes +} + +function generateS0S1S2(clientsig) { + let clientType = Buffer.alloc(1, 3); + let messageFormat = detectClientMessageFormat(clientsig); + let allBytes; + if (messageFormat === MESSAGE_FORMAT_0) { + // Logger.debug('[rtmp handshake] using simple handshake.'); + allBytes = Buffer.concat([clientType, clientsig, clientsig]); + } else { + // Logger.debug('[rtmp handshake] using complex handshake.'); + allBytes = Buffer.concat([clientType, generateS1(messageFormat), generateS2(messageFormat, clientsig)]); + } + return allBytes; +} + +module.exports = { generateS0S1S2 }; diff --git a/libs/rtmpserver/node_rtmp_server.js b/libs/rtmpserver/node_rtmp_server.js new file mode 100644 index 0000000..76c907a --- /dev/null +++ b/libs/rtmpserver/node_rtmp_server.js @@ -0,0 +1,50 @@ +// +// Created by Mingliang Chen on 17/8/1. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// +// const Logger = require('./node_core_logger'); + +const Net = require('net'); +const NodeRtmpSession = require('./node_rtmp_session'); +const NodeCoreUtils = require('./node_core_utils'); + +const context = require('./node_core_ctx'); + +const RTMP_PORT = 1935; + +class NodeRtmpServer { + constructor(config) { + config.rtmp.port = this.port = config.rtmp.port ? config.rtmp.port : RTMP_PORT; + this.tcpServer = Net.createServer((socket) => { + let session = new NodeRtmpSession(config, socket); + session.run(); + }) + } + + run() { + this.tcpServer.listen(this.port, () => { + // Logger.log(`Node Media Rtmp Server started on port: ${this.port}`); + }); + + this.tcpServer.on('error', (e) => { + // Logger.error(`Node Media Rtmp Server ${e}`); + }); + + this.tcpServer.on('close', () => { + // Logger.log('Node Media Rtmp Server Close.'); + }); + } + + stop() { + this.tcpServer.close(); + context.sessions.forEach((session, id) => { + if (session instanceof NodeRtmpSession) { + session.socket.destroy(); + context.sessions.delete(id); + } + }); + } +} + +module.exports = NodeRtmpServer diff --git a/libs/rtmpserver/node_rtmp_session.js b/libs/rtmpserver/node_rtmp_session.js new file mode 100644 index 0000000..93105fe --- /dev/null +++ b/libs/rtmpserver/node_rtmp_session.js @@ -0,0 +1,1248 @@ +// +// Created by Mingliang Chen on 18/4/1. +// illuspas[a]gmail.com +// Copyright (c) 2018 Nodemedia. All rights reserved. +// +// const EventEmitter = require('events'); +const QueryString = require('querystring'); +const AV = require('./node_core_av'); +const { AUDIO_SOUND_RATE, AUDIO_CODEC_NAME, VIDEO_CODEC_NAME } = require('./node_core_av'); + +const AMF = require('./node_core_amf'); +const Handshake = require('./node_rtmp_handshake'); +const NodeCoreUtils = require('./node_core_utils'); +const context = require('./node_core_ctx'); +// const Logger = require('./node_core_logger'); + +const N_CHUNK_STREAM = 8; +const RTMP_VERSION = 3; +const RTMP_HANDSHAKE_SIZE = 1536; +const RTMP_HANDSHAKE_UNINIT = 0; +const RTMP_HANDSHAKE_0 = 1; +const RTMP_HANDSHAKE_1 = 2; +const RTMP_HANDSHAKE_2 = 3; + +const RTMP_PARSE_INIT = 0; +const RTMP_PARSE_BASIC_HEADER = 1; +const RTMP_PARSE_MESSAGE_HEADER = 2; +const RTMP_PARSE_EXTENDED_TIMESTAMP = 3; +const RTMP_PARSE_PAYLOAD = 4; + +const MAX_CHUNK_HEADER = 18; + +const RTMP_CHUNK_TYPE_0 = 0; // 11-bytes: timestamp(3) + length(3) + stream type(1) + stream id(4) +const RTMP_CHUNK_TYPE_1 = 1; // 7-bytes: delta(3) + length(3) + stream type(1) +const RTMP_CHUNK_TYPE_2 = 2; // 3-bytes: delta(3) +const RTMP_CHUNK_TYPE_3 = 3; // 0-byte + +const RTMP_CHANNEL_PROTOCOL = 2; +const RTMP_CHANNEL_INVOKE = 3; +const RTMP_CHANNEL_AUDIO = 4; +const RTMP_CHANNEL_VIDEO = 5; +const RTMP_CHANNEL_DATA = 6; + +const rtmpHeaderSize = [11, 7, 3, 0]; + +/* Protocol Control Messages */ +const RTMP_TYPE_SET_CHUNK_SIZE = 1; +const RTMP_TYPE_ABORT = 2; +const RTMP_TYPE_ACKNOWLEDGEMENT = 3; // bytes read report +const RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE = 5; // server bandwidth +const RTMP_TYPE_SET_PEER_BANDWIDTH = 6; // client bandwidth + +/* User Control Messages Event (4) */ +const RTMP_TYPE_EVENT = 4; + +const RTMP_TYPE_AUDIO = 8; +const RTMP_TYPE_VIDEO = 9; + +/* Data Message */ +const RTMP_TYPE_FLEX_STREAM = 15; // AMF3 +const RTMP_TYPE_DATA = 18; // AMF0 + +/* Shared Object Message */ +const RTMP_TYPE_FLEX_OBJECT = 16; // AMF3 +const RTMP_TYPE_SHARED_OBJECT = 19; // AMF0 + +/* Command Message */ +const RTMP_TYPE_FLEX_MESSAGE = 17; // AMF3 +const RTMP_TYPE_INVOKE = 20; // AMF0 + +/* Aggregate Message */ +const RTMP_TYPE_METADATA = 22; + +const RTMP_CHUNK_SIZE = 128; +const RTMP_PING_TIME = 60000; +const RTMP_PING_TIMEOUT = 30000; + +const STREAM_BEGIN = 0x00; +const STREAM_EOF = 0x01; +const STREAM_DRY = 0x02; +const STREAM_EMPTY = 0x1f; +const STREAM_READY = 0x20; + +const RtmpPacket = { + create: (fmt = 0, cid = 0) => { + return { + header: { + fmt: fmt, + cid: cid, + timestamp: 0, + length: 0, + type: 0, + stream_id: 0 + }, + clock: 0, + payload: null, + capacity: 0, + bytes: 0 + }; + } +}; +class NodeRtmpSession { + constructor(config, socket) { + + this.config = config; + this.socket = socket; + this.id = NodeCoreUtils.generateNewSessionID(); + this.ip = socket.remoteAddress; + this.TAG = 'rtmp'; + // this.eventEmitter = new EventEmitter(); + + this.handshakePayload = Buffer.alloc(RTMP_HANDSHAKE_SIZE); + this.handshakeState = RTMP_HANDSHAKE_UNINIT; + this.handshakeBytes = 0; + + this.parserBuffer = Buffer.alloc(MAX_CHUNK_HEADER); + this.parserState = RTMP_PARSE_INIT; + this.parserBytes = 0; + this.parserBasicBytes = 0; + this.parserPacket = null; + this.inPackets = new Map(); + + this.inChunkSize = RTMP_CHUNK_SIZE; + this.outChunkSize = config.rtmp.chunk_size ? config.rtmp.chunk_size : RTMP_CHUNK_SIZE; + this.pingTime = config.rtmp.ping ? config.rtmp.ping * 1000 : RTMP_PING_TIME; + this.pingTimeout = config.rtmp.ping_timeout ? config.rtmp.ping_timeout * 1000 : RTMP_PING_TIMEOUT; + this.pingInterval = null; + + this.isIPC = false; + this.isLocal = this.ip === '127.0.0.1' || this.ip === '::1' || this.ip == '::ffff:127.0.0.1'; + this.isStarting = false; + this.isPublishing = false; + this.isPlaying = false; + this.isIdling = false; + this.isPause = false; + this.isReceiveAudio = true; + this.isReceiveVideo = true; + this.metaData = null; + this.aacSequenceHeader = null; + this.avcSequenceHeader = null; + this.audioCodec = 0; + this.audioCodecName = ''; + this.audioProfileName = ''; + this.audioSamplerate = 0; + this.audioChannels = 1; + this.videoCodec = 0; + this.videoCodecName = ''; + this.videoProfileName = ''; + this.videoWidth = 0; + this.videoHeight = 0; + this.videoFps = 0; + this.videoLevel = 0; + + this.gopCacheEnable = config.rtmp.gop_cache; + this.rtmpGopCacheQueue = null; + + this.ackSize = 0; + this.inAckSize = 0; + this.inLastAck = 0; + + this.appname = ''; + this.streams = 0; + + this.playStreamId = 0; + this.playStreamPath = ''; + this.playArgs = {}; + + this.publishStreamId = 0; + this.publishStreamPath = ''; + this.publishArgs = {}; + + this.players = new Set(); + this.writeBufferQueue = []; + context.sessions.set(this.id, this); + } + + // getEventEmitter() { + // return this.eventEmitter + // } + + run() { + this.socket.on('data', this.onSocketData.bind(this)); + this.socket.on('close', this.onSocketClose.bind(this)); + this.socket.on('error', this.onSocketError.bind(this)); + this.socket.on('timeout', this.onSocketTimeout.bind(this)); + this.socket.setTimeout(this.pingTimeout); + this.isStarting = true; + } + + stop() { + if (this.isStarting) { + this.isStarting = false; + + if (this.playStreamId > 0) { + this.onDeleteStream({ streamId: this.playStreamId }); + } + + if (this.publishStreamId > 0) { + this.onDeleteStream({ streamId: this.publishStreamId }); + } + + if (this.pingInterval != null) { + clearInterval(this.pingInterval); + this.pingInterval = null; + } + + if (!this.isIPC) { + // Logger.log(`[rtmp disconnect] id=${this.id}`); + context.nodeEvent.emit('doneConnect', this.id, this.connectCmdObj); + } + + context.sessions.delete(this.id); + this.socket.destroy(); + } + } + + reject() { + // Logger.log(`[rtmp reject] id=${this.id}`); + this.stop(); + } + + writeBuffer(data) { + this.writeBufferQueue.push(data); + if (this.writeBufferQueue.length >= 10) { + this.socket.write(Buffer.concat(this.writeBufferQueue)); + this.writeBufferQueue.length = 0; + } + } + + onSocketClose() { + // Logger.log('onSocketClose'); + this.stop(); + } + + onSocketError(e) { + // Logger.log('onSocketError', e); + this.stop(); + } + + onSocketTimeout() { + // Logger.log('onSocketTimeout'); + this.stop(); + } + + onSocketData(data) { + let bytes = data.length; + let p = 0; + let n = 0; + while (bytes > 0) { + switch (this.handshakeState) { + case RTMP_HANDSHAKE_UNINIT: + // Logger.log('RTMP_HANDSHAKE_UNINIT'); + this.handshakeState = RTMP_HANDSHAKE_0; + this.handshakeBytes = 0; + bytes -= 1; + p += 1; + break; + case RTMP_HANDSHAKE_0: + // Logger.log('RTMP_HANDSHAKE_0'); + n = RTMP_HANDSHAKE_SIZE - this.handshakeBytes; + n = n <= bytes ? n : bytes; + data.copy(this.handshakePayload, this.handshakeBytes, p, p + n); + this.handshakeBytes += n; + bytes -= n; + p += n; + if (this.handshakeBytes === RTMP_HANDSHAKE_SIZE) { + this.handshakeState = RTMP_HANDSHAKE_1; + this.handshakeBytes = 0; + let s0s1s2 = Handshake.generateS0S1S2(this.handshakePayload); + this.socket.write(s0s1s2); + } + break; + case RTMP_HANDSHAKE_1: + // Logger.log('RTMP_HANDSHAKE_1'); + n = RTMP_HANDSHAKE_SIZE - this.handshakeBytes; + n = n <= bytes ? n : bytes; + data.copy(this.handshakePayload, this.handshakeBytes, p, n); + this.handshakeBytes += n; + bytes -= n; + p += n; + if (this.handshakeBytes === RTMP_HANDSHAKE_SIZE) { + this.handshakeState = RTMP_HANDSHAKE_2; + this.handshakeBytes = 0; + this.handshakePayload = null; + } + break; + case RTMP_HANDSHAKE_2: + default: + // Logger.log('RTMP_HANDSHAKE_2'); + return this.rtmpChunkRead(data, p, bytes); + } + } + } + + rtmpChunkBasicHeaderCreate(fmt, cid) { + let out; + if (cid >= 64 + 255) { + out = Buffer.alloc(3); + out[0] = (fmt << 6) | 1; + out[1] = (cid - 64) & 0xFF; + out[2] = ((cid - 64) >> 8) & 0xFF; + } else if (cid >= 64) { + out = Buffer.alloc(2); + out[0] = (fmt << 6) | 0; + out[1] = (cid - 64) & 0xFF; + } else { + out = Buffer.alloc(1); + out[0] = (fmt << 6) | cid; + } + return out; + } + + rtmpChunkMessageHeaderCreate(header) { + let out = Buffer.alloc(rtmpHeaderSize[header.fmt % 4]); + if (header.fmt <= RTMP_CHUNK_TYPE_2) { + out.writeUIntBE(header.timestamp >= 0xffffff ? 0xffffff : header.timestamp, 0, 3); + } + + if (header.fmt <= RTMP_CHUNK_TYPE_1) { + out.writeUIntBE(header.length, 3, 3); + out.writeUInt8(header.type, 6); + } + + if (header.fmt === RTMP_CHUNK_TYPE_0) { + out.writeUInt32LE(header.stream_id, 7); + } + return out; + } + + rtmpChunksCreate(packet) { + let header = packet.header; + let payload = packet.payload; + let payloadSize = header.length; + let chunkSize = this.outChunkSize; + let chunksOffset = 0; + let payloadOffset = 0; + let chunkBasicHeader = this.rtmpChunkBasicHeaderCreate(header.fmt, header.cid); + let chunkBasicHeader3 = this.rtmpChunkBasicHeaderCreate(RTMP_CHUNK_TYPE_3, header.cid); + let chunkMessageHeader = this.rtmpChunkMessageHeaderCreate(header); + let useExtendedTimestamp = header.timestamp >= 0xffffff; + let headerSize = chunkBasicHeader.length + chunkMessageHeader.length + (useExtendedTimestamp ? 4 : 0); + let n = headerSize + payloadSize + Math.floor(payloadSize / chunkSize); + + if (useExtendedTimestamp) { + n += Math.floor(payloadSize / chunkSize) * 4; + } + if (!(payloadSize % chunkSize)) { + n -= 1; + if (useExtendedTimestamp) { //TODO CHECK + n -= 4; + } + } + + let chunks = Buffer.alloc(n); + chunkBasicHeader.copy(chunks, chunksOffset); + chunksOffset += chunkBasicHeader.length; + chunkMessageHeader.copy(chunks, chunksOffset); + chunksOffset += chunkMessageHeader.length; + if (useExtendedTimestamp) { + chunks.writeUInt32BE(header.timestamp, chunksOffset); + chunksOffset += 4; + } + while (payloadSize > 0) { + if (payloadSize > chunkSize) { + payload.copy(chunks, chunksOffset, payloadOffset, payloadOffset + chunkSize); + payloadSize -= chunkSize; + chunksOffset += chunkSize; + payloadOffset += chunkSize; + chunkBasicHeader3.copy(chunks, chunksOffset); + chunksOffset += chunkBasicHeader3.length; + if (useExtendedTimestamp) { + chunks.writeUInt32BE(header.timestamp, chunksOffset); + chunksOffset += 4; + } + } else { + payload.copy(chunks, chunksOffset, payloadOffset, payloadOffset + payloadSize); + payloadSize -= payloadSize; + chunksOffset += payloadSize; + payloadOffset += payloadSize; + } + } + return chunks; + } + + rtmpChunkRead(data, p, bytes) { + // Logger.log('rtmpChunkRead', p, bytes); + + let size = 0; + let offset = 0; + let extended_timestamp = 0; + + while (offset < bytes) { + switch (this.parserState) { + case RTMP_PARSE_INIT: + this.parserBytes = 1; + this.parserBuffer[0] = data[p + offset++]; + if (0 === (this.parserBuffer[0] & 0x3F)) { + this.parserBasicBytes = 2; + } else if (1 === (this.parserBuffer[0] & 0x3F)) { + this.parserBasicBytes = 3; + } else { + this.parserBasicBytes = 1; + } + this.parserState = RTMP_PARSE_BASIC_HEADER; + break; + case RTMP_PARSE_BASIC_HEADER: + while (this.parserBytes < this.parserBasicBytes && offset < bytes) { + this.parserBuffer[this.parserBytes++] = data[p + offset++]; + } + if (this.parserBytes >= this.parserBasicBytes) { + this.parserState = RTMP_PARSE_MESSAGE_HEADER; + } + break; + case RTMP_PARSE_MESSAGE_HEADER: + size = rtmpHeaderSize[this.parserBuffer[0] >> 6] + this.parserBasicBytes; + while (this.parserBytes < size && offset < bytes) { + this.parserBuffer[this.parserBytes++] = data[p + offset++]; + } + if (this.parserBytes >= size) { + this.rtmpPacketParse(); + this.parserState = RTMP_PARSE_EXTENDED_TIMESTAMP; + } + break; + case RTMP_PARSE_EXTENDED_TIMESTAMP: + size = rtmpHeaderSize[this.parserPacket.header.fmt] + this.parserBasicBytes; + if (this.parserPacket.header.timestamp === 0xFFFFFF) size += 4; + while (this.parserBytes < size && offset < bytes) { + this.parserBuffer[this.parserBytes++] = data[p + offset++]; + } + if (this.parserBytes >= size) { + if (this.parserPacket.header.timestamp === 0xFFFFFF) { + extended_timestamp = this.parserBuffer.readUInt32BE(rtmpHeaderSize[this.parserPacket.header.fmt] + this.parserBasicBytes); + } else { + extended_timestamp = this.parserPacket.header.timestamp; + } + + if (this.parserPacket.bytes === 0) { + if (RTMP_CHUNK_TYPE_0 === this.parserPacket.header.fmt) { + this.parserPacket.clock = extended_timestamp; + } else { + this.parserPacket.clock += extended_timestamp; + } + this.rtmpPacketAlloc(); + } + this.parserState = RTMP_PARSE_PAYLOAD; + } + break; + case RTMP_PARSE_PAYLOAD: + size = Math.min(this.inChunkSize - (this.parserPacket.bytes % this.inChunkSize), this.parserPacket.header.length - this.parserPacket.bytes); + size = Math.min(size, bytes - offset); + if (size > 0) { + data.copy(this.parserPacket.payload, this.parserPacket.bytes, p + offset, p + offset + size); + } + this.parserPacket.bytes += size; + offset += size; + + if (this.parserPacket.bytes >= this.parserPacket.header.length) { + this.parserState = RTMP_PARSE_INIT; + this.parserPacket.bytes = 0; + if(this.parserPacket.clock > 0xffffffff){ + //TODO Shit code, rewrite chunkcreate + break; + } + this.rtmpHandler(); + } else if (0 === (this.parserPacket.bytes % this.inChunkSize)) { + this.parserState = RTMP_PARSE_INIT; + } + break; + } + } + + this.inAckSize += data.length; + if (this.inAckSize >= 0xf0000000) { + this.inAckSize = 0; + this.inLastAck = 0; + } + if (this.ackSize > 0 && this.inAckSize - this.inLastAck >= this.ackSize) { + this.inLastAck = this.inAckSize; + this.sendACK(this.inAckSize); + } + + } + + rtmpPacketParse() { + let fmt = this.parserBuffer[0] >> 6; + let cid = 0; + if (this.parserBasicBytes === 2) { + cid = 64 + this.parserBuffer[1]; + } else if (this.parserBasicBytes === 3) { + cid = 64 + this.parserBuffer[1] + this.parserBuffer[2] << 8; + } else { + cid = this.parserBuffer[0] & 0x3F; + } + let hasp = this.inPackets.has(cid); + if (!hasp) { + this.parserPacket = RtmpPacket.create(fmt, cid); + this.inPackets.set(cid, this.parserPacket); + } else { + this.parserPacket = this.inPackets.get(cid); + } + this.parserPacket.header.fmt = fmt; + this.parserPacket.header.cid = cid; + this.rtmpChunkMessageHeaderRead(); + + if (this.parserPacket.header.type > RTMP_TYPE_METADATA) { + Logger.error("rtmp packet parse error.", this.parserPacket); + this.stop(); + } + + } + + rtmpChunkMessageHeaderRead() { + let offset = this.parserBasicBytes; + + // timestamp / delta + if (this.parserPacket.header.fmt <= RTMP_CHUNK_TYPE_2) { + this.parserPacket.header.timestamp = this.parserBuffer.readUIntBE(offset, 3); + offset += 3; + } + + // message length + type + if (this.parserPacket.header.fmt <= RTMP_CHUNK_TYPE_1) { + this.parserPacket.header.length = this.parserBuffer.readUIntBE(offset, 3); + this.parserPacket.header.type = this.parserBuffer[offset + 3]; + offset += 4; + } + + if (this.parserPacket.header.fmt === RTMP_CHUNK_TYPE_0) { + this.parserPacket.header.stream_id = this.parserBuffer.readUInt32LE(offset); + offset += 4; + } + return offset; + } + + rtmpPacketAlloc() { + if (this.parserPacket.capacity < this.parserPacket.header.length) { + this.parserPacket.payload = Buffer.alloc(this.parserPacket.header.length + 1024); + this.parserPacket.capacity = this.parserPacket.header.length + 1024; + } + } + + rtmpHandler() { + switch (this.parserPacket.header.type) { + case RTMP_TYPE_SET_CHUNK_SIZE: + case RTMP_TYPE_ABORT: + case RTMP_TYPE_ACKNOWLEDGEMENT: + case RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE: + case RTMP_TYPE_SET_PEER_BANDWIDTH: + return 0 === this.rtmpControlHandler() ? -1 : 0; + case RTMP_TYPE_EVENT: + return 0 === this.rtmpEventHandler() ? -1 : 0; + case RTMP_TYPE_AUDIO: + return this.rtmpAudioHandler(); + case RTMP_TYPE_VIDEO: + return this.rtmpVideoHandler(); + case RTMP_TYPE_FLEX_MESSAGE: + case RTMP_TYPE_INVOKE: + return this.rtmpInvokeHandler(); + case RTMP_TYPE_FLEX_STREAM:// AMF3 + case RTMP_TYPE_DATA: // AMF0 + return this.rtmpDataHandler(); + } + } + + rtmpControlHandler() { + let payload = this.parserPacket.payload; + switch (this.parserPacket.header.type) { + case RTMP_TYPE_SET_CHUNK_SIZE: + this.inChunkSize = payload.readUInt32BE(); + // Logger.debug('set inChunkSize', this.inChunkSize); + break; + case RTMP_TYPE_ABORT: + break; + case RTMP_TYPE_ACKNOWLEDGEMENT: + break; + case RTMP_TYPE_WINDOW_ACKNOWLEDGEMENT_SIZE: + this.ackSize = payload.readUInt32BE(); + // Logger.debug('set ack Size', this.ackSize); + break; + case RTMP_TYPE_SET_PEER_BANDWIDTH: + break; + } + } + + rtmpEventHandler() { + + } + + rtmpAudioHandler() { + let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length); + let sound_format = (payload[0] >> 4) & 0x0f; + let sound_type = payload[0] & 0x01; + let sound_size = (payload[0] >> 1) & 0x01; + let sound_rate = (payload[0] >> 2) & 0x03; + + if (this.audioCodec == 0) { + this.audioCodec = sound_format; + this.audioCodecName = AUDIO_CODEC_NAME[sound_format]; + this.audioSamplerate = AUDIO_SOUND_RATE[sound_rate]; + this.audioChannels = ++sound_type; + + if (sound_format == 4) { + this.audioSamplerate = 16000; + } else if (sound_format == 5) { + this.audioSamplerate = 8000; + } else if (sound_format == 11) { + this.audioSamplerate = 16000; + } else if (sound_format == 14) { + this.audioSamplerate = 8000; + } + + if (sound_format != 10 && !this.isIPC) { + // Logger.log(`[rtmp publish] Handle audio. id=${this.id} streamPath=${this.publishStreamPath} sound_format=${sound_format} sound_type=${sound_type} sound_size=${sound_size} sound_rate=${sound_rate} codec_name=${this.audioCodecName} ${this.audioSamplerate} ${this.audioChannels}ch`); + } + } + + if (sound_format == 10 && payload[1] == 0) { + //cache aac sequence header + this.isFirstAudioReceived = true; + this.aacSequenceHeader = Buffer.alloc(payload.length); + payload.copy(this.aacSequenceHeader); + let info = AV.readAACSpecificConfig(this.aacSequenceHeader); + this.audioProfileName = AV.getAACProfileName(info); + this.audioSamplerate = info.sample_rate; + this.audioChannels = info.channels; + if (!this.isIPC) { + // Logger.log(`[rtmp publish] Handle audio. id=${this.id} streamPath=${this.publishStreamPath} sound_format=${sound_format} sound_type=${sound_type} sound_size=${sound_size} sound_rate=${sound_rate} codec_name=${this.audioCodecName} ${this.audioSamplerate} ${this.audioChannels}ch`); + } + } + + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_AUDIO; + packet.header.type = RTMP_TYPE_AUDIO; + packet.payload = payload; + packet.header.length = packet.payload.length; + packet.header.timestamp = this.parserPacket.clock; + let rtmpChunks = this.rtmpChunksCreate(packet); + + //cache gop + if (this.rtmpGopCacheQueue != null) { + if (this.aacSequenceHeader != null && payload[1] === 0) { + //skip aac sequence header + } else { + this.rtmpGopCacheQueue.add(rtmpChunks); + } + } + + for (let playerId of this.players) { + let playerSession = context.sessions.get(playerId); + if (playerSession instanceof NodeRtmpSession) { + if (playerSession.isStarting && playerSession.isPlaying && !playerSession.isPause && playerSession.isReceiveAudio) { + rtmpChunks.writeUInt32LE(playerSession.playStreamId, 8); + playerSession.writeBuffer(rtmpChunks); + } + } + } + } + + rtmpVideoHandler() { + let payload = this.parserPacket.payload.slice(0, this.parserPacket.header.length); + let frame_type = (payload[0] >> 4) & 0x0f; + let codec_id = payload[0] & 0x0f; + + if (codec_id == 7 || codec_id == 12) { + //cache avc sequence header + if (frame_type == 1 && payload[1] == 0) { + this.avcSequenceHeader = Buffer.alloc(payload.length); + payload.copy(this.avcSequenceHeader); + let info = AV.readAVCSpecificConfig(this.avcSequenceHeader); + this.videoWidth = info.width; + this.videoHeight = info.height; + this.videoProfileName = AV.getAVCProfileName(info); + this.videoLevel = info.level; + this.rtmpGopCacheQueue = this.gopCacheEnable ? new Set() : null; + //Logger.log(`[rtmp publish] avc sequence header`,this.avcSequenceHeader); + } + } + + if (this.videoCodec == 0) { + this.videoCodec = codec_id; + this.videoCodecName = VIDEO_CODEC_NAME[codec_id]; + if (!this.isIPC) { + // Logger.log(`[rtmp publish] Handle video. id=${this.id} streamPath=${this.publishStreamPath} frame_type=${frame_type} codec_id=${codec_id} codec_name=${this.videoCodecName} ${this.videoWidth}x${this.videoHeight}`); + } + } + + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_VIDEO; + packet.header.type = RTMP_TYPE_VIDEO; + packet.payload = payload; + packet.header.length = packet.payload.length; + packet.header.timestamp = this.parserPacket.clock; + let rtmpChunks = this.rtmpChunksCreate(packet); + + //cache gop + if ((codec_id == 7 || codec_id == 12) && this.rtmpGopCacheQueue != null) { + if (frame_type == 1 && payload[1] == 1) { + this.rtmpGopCacheQueue.clear(); + } + if (frame_type == 1 && payload[1] == 0) { + //skip avc sequence header + } else { + this.rtmpGopCacheQueue.add(rtmpChunks); + } + } + + // Logger.log(rtmpChunks); + for (let playerId of this.players) { + let playerSession = context.sessions.get(playerId); + if (playerSession instanceof NodeRtmpSession) { + if (playerSession.isStarting && playerSession.isPlaying && !playerSession.isPause && playerSession.isReceiveVideo) { + rtmpChunks.writeUInt32LE(playerSession.playStreamId, 8); + playerSession.writeBuffer(rtmpChunks); + } + } + } + } + + rtmpDataHandler() { + let offset = this.parserPacket.header.type === RTMP_TYPE_FLEX_STREAM ? 1 : 0; + let payload = this.parserPacket.payload.slice(offset, this.parserPacket.header.length); + let dataMessage = AMF.decodeAmf0Data(payload); + switch (dataMessage.cmd) { + case '@setDataFrame': + if (dataMessage.dataObj) { + this.audioSamplerate = dataMessage.dataObj.audiosamplerate; + this.audioChannels = dataMessage.dataObj.stereo ? 2 : 1; + this.videoWidth = dataMessage.dataObj.width; + this.videoHeight = dataMessage.dataObj.height; + this.videoFps = dataMessage.dataObj.framerate; + } + + let opt = { + cmd: 'onMetaData', + dataObj: dataMessage.dataObj + }; + this.metaData = AMF.encodeAmf0Data(opt); + + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_DATA; + packet.header.type = RTMP_TYPE_DATA; + packet.payload = this.metaData; + packet.header.length = packet.payload.length; + let rtmpChunks = this.rtmpChunksCreate(packet); + + for (let playerId of this.players) { + let playerSession = context.sessions.get(playerId); + if (playerSession instanceof NodeRtmpSession) { + if (playerSession.isStarting && playerSession.isPlaying && !playerSession.isPause) { + rtmpChunks.writeUInt32LE(playerSession.playStreamId, 8); + playerSession.socket.write(rtmpChunks); + } + } + } + break; + } + } + + rtmpInvokeHandler() { + let offset = this.parserPacket.header.type === RTMP_TYPE_FLEX_MESSAGE ? 1 : 0; + let payload = this.parserPacket.payload.slice(offset, this.parserPacket.header.length); + let invokeMessage = AMF.decodeAmf0Cmd(payload); + // Logger.log(invokeMessage); + switch (invokeMessage.cmd) { + case 'connect': + this.onConnect(invokeMessage); + break; + case 'releaseStream': + break; + case 'FCPublish': + break; + case 'createStream': + this.onCreateStream(invokeMessage); + break; + case 'publish': + this.onPublish(invokeMessage); + break; + case 'play': + this.onPlay(invokeMessage); + break; + case 'pause': + this.onPause(invokeMessage); + break; + case 'FCUnpublish': + break; + case 'deleteStream': + this.onDeleteStream(invokeMessage); + break; + case 'closeStream': + this.onCloseStream(); + break; + case 'receiveAudio': + this.onReceiveAudio(invokeMessage); + break; + case 'receiveVideo': + this.onReceiveVideo(invokeMessage); + break; + } + } + + sendACK(size) { + let rtmpBuffer = Buffer.from('02000000000004030000000000000000', 'hex'); + rtmpBuffer.writeUInt32BE(size, 12); + this.socket.write(rtmpBuffer); + } + + sendWindowACK(size) { + let rtmpBuffer = Buffer.from('02000000000004050000000000000000', 'hex'); + rtmpBuffer.writeUInt32BE(size, 12); + this.socket.write(rtmpBuffer); + }; + + setPeerBandwidth(size, type) { + let rtmpBuffer = Buffer.from('0200000000000506000000000000000000', 'hex'); + rtmpBuffer.writeUInt32BE(size, 12); + rtmpBuffer[16] = type; + this.socket.write(rtmpBuffer); + }; + + setChunkSize(size) { + let rtmpBuffer = Buffer.from('02000000000004010000000000000000', 'hex'); + rtmpBuffer.writeUInt32BE(size, 12); + this.socket.write(rtmpBuffer); + }; + + sendStreamStatus(st, id) { + let rtmpBuffer = Buffer.from('020000000000060400000000000000000000', 'hex'); + rtmpBuffer.writeUInt16BE(st, 12); + rtmpBuffer.writeUInt32BE(id, 14); + this.socket.write(rtmpBuffer); + } + + sendInvokeMessage(sid, opt) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_INVOKE; + packet.header.type = RTMP_TYPE_INVOKE; + packet.header.stream_id = sid; + packet.payload = AMF.encodeAmf0Cmd(opt); + packet.header.length = packet.payload.length; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + sendDataMessage(opt, sid) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_DATA; + packet.header.type = RTMP_TYPE_DATA; + packet.payload = AMF.encodeAmf0Data(opt); + packet.header.length = packet.payload.length; + packet.header.stream_id = sid; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + sendStatusMessage(sid, level, code, description) { + let opt = { + cmd: 'onStatus', + transId: 0, + cmdObj: null, + info: { + level: level, + code: code, + description: description + } + }; + this.sendInvokeMessage(sid, opt); + } + + sendRtmpSampleAccess(sid) { + let opt = { + cmd: '|RtmpSampleAccess', + bool1: false, + bool2: false + }; + this.sendDataMessage(opt, sid); + } + + sendPingRequest() { + let currentTimestamp = Date.now() - this.startTimestamp; + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_PROTOCOL; + packet.header.type = RTMP_TYPE_EVENT; + packet.header.timestamp = currentTimestamp; + packet.payload = Buffer.from([0, 6, (currentTimestamp >> 24) & 0xff, (currentTimestamp >> 16) & 0xff, (currentTimestamp >> 8) & 0xff, currentTimestamp & 0xff]); + packet.header.length = packet.payload.length; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + respondConnect(tid) { + let opt = { + cmd: '_result', + transId: tid, + cmdObj: { + fmsVer: 'FMS/3,0,1,123', + capabilities: 31 + }, + info: { + level: 'status', + code: 'NetConnection.Connect.Success', + description: 'Connection succeeded.', + objectEncoding: this.objectEncoding + } + }; + this.sendInvokeMessage(0, opt); + } + + respondCreateStream(tid) { + this.streams++; + let opt = { + cmd: "_result", + transId: tid, + cmdObj: null, + info: this.streams + }; + this.sendInvokeMessage(0, opt); + } + + respondPlay() { + this.sendStreamStatus(STREAM_BEGIN, this.playStreamId); + this.sendStatusMessage(this.playStreamId, 'status', 'NetStream.Play.Reset', 'Playing and resetting stream.'); + this.sendStatusMessage(this.playStreamId, 'status', 'NetStream.Play.Start', 'Started playing stream.'); + this.sendRtmpSampleAccess(); + } + + onConnect(invokeMessage) { + invokeMessage.cmdObj.app = invokeMessage.cmdObj.app.replace('/', ''); //fix jwplayer + if (!this.isIPC) { + context.nodeEvent.emit('preConnect', this.id, invokeMessage.cmdObj); + } + // this.eventEmitter.emit('authConnection', { + // appname: this.appname, + // _this: this + // }); + if (!this.isStarting) { + return; + } + this.connectCmdObj = invokeMessage.cmdObj; + this.appname = invokeMessage.cmdObj.app; + this.objectEncoding = invokeMessage.cmdObj.objectEncoding != null ? invokeMessage.cmdObj.objectEncoding : 0; + this.connectTime = new Date(); + this.startTimestamp = Date.now(); + this.pingInterval = setInterval(() => { + this.sendPingRequest(); + }, this.pingTime); + this.sendWindowACK(5000000); + this.setPeerBandwidth(5000000, 2); + this.setChunkSize(this.outChunkSize); + this.respondConnect(invokeMessage.transId); + if (!this.isIPC) { + // Logger.log(`[rtmp connect] id=${this.id} ip=${this.ip} app=${this.appname} args=${JSON.stringify(invokeMessage.cmdObj)}`); + context.nodeEvent.emit('postConnect', this.id, invokeMessage.cmdObj); + } + } + + onCreateStream(invokeMessage) { + this.respondCreateStream(invokeMessage.transId); + } + + onPublish(invokeMessage) { + if (typeof invokeMessage.streamName !== 'string') { + return; + } + this.publishStreamPath = '/' + this.appname + '/' + invokeMessage.streamName.split('?')[0]; + this.publishArgs = QueryString.parse(invokeMessage.streamName.split('?')[1]); + this.publishStreamId = this.parserPacket.header.stream_id; + if (!this.isIPC) { + context.nodeEvent.emit('prePublish', this.id, this.publishStreamPath, this.publishArgs); + } + if (!this.isStarting) { + return; + } + + if (this.config.auth && this.config.auth.publish && !this.isLocal && !this.isIPC) { + let results = NodeCoreUtils.verifyAuth(this.publishArgs.sign, this.publishStreamPath, this.config.auth.secret); + if (!results) { + // Logger.log(`[rtmp publish] Unauthorized. id=${this.id} streamPath=${this.publishStreamPath} streamId=${this.publishStreamId} sign=${this.publishArgs.sign} `); + this.sendStatusMessage(this.publishStreamId, 'error', 'NetStream.publish.Unauthorized', 'Authorization required.'); + return; + } + } + + if (context.publishers.has(this.publishStreamPath)) { + // Logger.log(`[rtmp publish] Already has a stream. id=${this.id} streamPath=${this.publishStreamPath} streamId=${this.publishStreamId}`); + this.sendStatusMessage(this.publishStreamId, 'error', 'NetStream.Publish.BadName', 'Stream already publishing'); + } else if (this.isPublishing) { + // Logger.log(`[rtmp publish] NetConnection is publishing. id=${this.id} streamPath=${this.publishStreamPath} streamId=${this.publishStreamId}`); + this.sendStatusMessage(this.publishStreamId, 'error', 'NetStream.Publish.BadConnection', 'Connection already publishing'); + } else { + if (!this.isIPC) { + // Logger.log(`[rtmp publish] New stream. id=${this.id} streamPath=${this.publishStreamPath} streamId=${this.publishStreamId}`); + } + context.publishers.set(this.publishStreamPath, this.id); + this.isPublishing = true; + + this.sendStatusMessage(this.publishStreamId, 'status', 'NetStream.Publish.Start', `${this.publishStreamPath} is now published.`); + for (let idlePlayerId of context.idlePlayers) { + let idlePlayer = context.sessions.get(idlePlayerId); + if (idlePlayer.playStreamPath === this.publishStreamPath) { + idlePlayer.onStartPlay(); + context.idlePlayers.delete(idlePlayerId); + } + } + setTimeout(() => { + this.publishArgs.ac = this.audioCodec; + this.publishArgs.vc = this.videoCodec; + if (!this.isIPC) { + context.nodeEvent.emit('postPublish', this.id, this.publishStreamPath, this.publishArgs); + } + }, 1000);//TODO 只提交事件,不传音视频参数,由转码器自行分析 + + } + } + + onPlay(invokeMessage) { + if (typeof invokeMessage.streamName !== 'string') { + return; + } + this.playStreamPath = '/' + this.appname + '/' + invokeMessage.streamName.split('?')[0]; + this.playArgs = QueryString.parse(invokeMessage.streamName.split('?')[1]); + this.playStreamId = this.parserPacket.header.stream_id; + if (!this.isIPC) { + context.nodeEvent.emit('prePlay', this.id, this.playStreamPath, this.playArgs); + } + if (!this.isStarting) { + return; + } + + if (this.config.auth && this.config.auth.play && !this.isLocal && !this.isIPC) { + let results = NodeCoreUtils.verifyAuth(this.playArgs.sign, this.playStreamPath, this.config.auth.secret); + if (!results) { + // Logger.log(`[rtmp play] Unauthorized. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId} sign=${this.playArgs.sign}`); + this.sendStatusMessage(this.playStreamId, 'error', 'NetStream.play.Unauthorized', 'Authorization required.'); + return; + } + } + + if (this.isPlaying) { + if (!this.isIPC) { + // Logger.log(`[rtmp play] NetConnection is playing. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId} `); + } + this.sendStatusMessage(this.playStreamId, 'error', 'NetStream.Play.BadConnection', 'Connection already playing'); + } else { + this.respondPlay(); + } + + if (context.publishers.has(this.playStreamPath)) { + this.onStartPlay(); + } else { + // Logger.log(`[rtmp play] Stream not found. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId}`); + this.isIdling = true; + context.idlePlayers.add(this.id); + } + + } + + onStartPlay() { + let publisherId = context.publishers.get(this.playStreamPath); + let publisher = context.sessions.get(publisherId); + let players = publisher.players; + players.add(this.id); + + if (publisher.metaData != null) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_DATA; + packet.header.type = RTMP_TYPE_DATA; + packet.payload = publisher.metaData; + packet.header.length = packet.payload.length; + packet.header.stream_id = this.playStreamId; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + if (publisher.audioCodec === 10) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_AUDIO; + packet.header.type = RTMP_TYPE_AUDIO; + packet.payload = publisher.aacSequenceHeader; + packet.header.length = packet.payload.length; + packet.header.stream_id = this.playStreamId; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + if (publisher.videoCodec === 7 || publisher.videoCodec === 12) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_VIDEO; + packet.header.type = RTMP_TYPE_VIDEO; + packet.payload = publisher.avcSequenceHeader; + packet.header.length = packet.payload.length; + packet.header.stream_id = this.playStreamId; + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + + if (publisher.rtmpGopCacheQueue != null) { + for (let chunks of publisher.rtmpGopCacheQueue) { + chunks.writeUInt32LE(this.playStreamId, 8); + this.socket.write(chunks); + } + } + + this.isIdling = false; + this.isPlaying = true; + if (!this.isIPC) { + context.nodeEvent.emit('postPlay', this.id, this.playStreamPath, this.playArgs); + // Logger.log(`[rtmp play] Join stream. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId} `); + } + } + + onPause(invokeMessage) { + this.isPause = invokeMessage.pause; + let c = this.isPause ? 'NetStream.Pause.Notify' : 'NetStream.Unpause.Notify'; + let d = this.isPause ? 'Paused live' : 'Unpaused live'; + // Logger.log(`[rtmp play] ${d} stream. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId} `); + if (!this.isPause) { + this.sendStreamStatus(STREAM_BEGIN, this.playStreamId); + if (context.publishers.has(this.playStreamPath)) { + //fix ckplayer + let publisherId = context.publishers.get(this.playStreamPath); + let publisher = context.sessions.get(publisherId); + let players = publisher.players; + if (publisher.audioCodec === 10) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_AUDIO; + packet.header.type = RTMP_TYPE_AUDIO; + packet.payload = publisher.aacSequenceHeader; + packet.header.length = packet.payload.length; + packet.header.stream_id = this.playStreamId; + packet.header.timestamp = publisher.parserPacket.clock; // ?? 0 or clock + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + if (publisher.videoCodec === 7 || publisher.videoCodec === 12) { + let packet = RtmpPacket.create(); + packet.header.fmt = RTMP_CHUNK_TYPE_0; + packet.header.cid = RTMP_CHANNEL_VIDEO; + packet.header.type = RTMP_TYPE_VIDEO; + packet.payload = publisher.avcSequenceHeader; + packet.header.length = packet.payload.length; + packet.header.stream_id = this.playStreamId; + packet.header.timestamp = publisher.parserPacket.clock;// ?? 0 or clock + let chunks = this.rtmpChunksCreate(packet); + this.socket.write(chunks); + } + } + + } else { + this.sendStreamStatus(STREAM_EOF, this.playStreamId); + } + this.sendStatusMessage(this.playStreamId, c, d); + } + + onReceiveAudio(invokeMessage) { + this.isReceiveAudio = invokeMessage.bool; + // Logger.log(`[rtmp play] receiveAudio=${this.isReceiveAudio} id=${this.id} `); + } + + onReceiveVideo(invokeMessage) { + this.isReceiveVideo = invokeMessage.bool; + // Logger.log(`[rtmp play] receiveVideo=${this.isReceiveVideo} id=${this.id} `); + } + + onCloseStream() { + //red5-publisher + let closeStream = { streamId: this.parserPacket.header.stream_id }; + this.onDeleteStream(closeStream); + } + + onDeleteStream(invokeMessage) { + if (invokeMessage.streamId == this.playStreamId) { + if (this.isIdling) { + context.idlePlayers.delete(this.id); + this.isIdling = false; + } else { + let publisherId = context.publishers.get(this.playStreamPath); + if (publisherId != null) { + context.sessions.get(publisherId).players.delete(this.id); + } + if (!this.isIPC) { + context.nodeEvent.emit('donePlay', this.id, this.playStreamPath, this.playArgs); + } + this.isPlaying = false; + } + if (!this.isIPC) { + // Logger.log(`[rtmp play] Close stream. id=${this.id} streamPath=${this.playStreamPath} streamId=${this.playStreamId}`); + } + if (this.isStarting) { + this.sendStatusMessage(this.playStreamId, 'status', 'NetStream.Play.Stop', 'Stopped playing stream.'); + } + this.playStreamId = 0; + this.playStreamPath = ''; + } + + if (invokeMessage.streamId == this.publishStreamId) { + if (this.isPublishing) { + if (!this.isIPC) { + // Logger.log(`[rtmp publish] Close stream. id=${this.id} streamPath=${this.publishStreamPath} streamId=${this.publishStreamId}`); + context.nodeEvent.emit('donePublish', this.id, this.publishStreamPath, this.publishArgs); + } + if (this.isStarting) { + this.sendStatusMessage(this.publishStreamId, 'status', 'NetStream.Unpublish.Success', `${this.publishStreamPath} is now unpublished.`); + } + + for (let playerId of this.players) { + let player = context.sessions.get(playerId); + if (player instanceof NodeRtmpSession) { + player.sendStatusMessage(player.playStreamId, 'status', 'NetStream.Play.UnpublishNotify', 'stream is now unpublished.'); + } else { + player.stop(); + } + } + + //let the players to idlePlayers + for (let playerId of this.players) { + let player = context.sessions.get(playerId); + context.idlePlayers.add(playerId); + player.isPlaying = false; + player.isIdling = true; + if (player instanceof NodeRtmpSession) { + player.sendStreamStatus(STREAM_EOF, player.playStreamId); + } + } + + context.publishers.delete(this.publishStreamPath); + if (this.rtmpGopCacheQueue) { + this.rtmpGopCacheQueue.clear(); + } + this.players.clear(); + this.isPublishing = false; + } + this.publishStreamId = 0; + this.publishStreamPath = ''; + } + } +} + +module.exports = NodeRtmpSession; diff --git a/libs/scheduler.js b/libs/scheduler.js new file mode 100644 index 0000000..4cf0812 --- /dev/null +++ b/libs/scheduler.js @@ -0,0 +1,263 @@ +module.exports = function(s,config,lang,app,io){ + s.schedules = {} + //Get all Schedules + s.getAllSchedules = function(callback){ + s.schedules = {} + s.sqlQuery('SELECT * FROM Schedules',function(err,rows){ + rows.forEach(function(schedule){ + s.updateSchedule(schedule) + }) + if(callback)callback() + }) + } + //update schedule + s.updateSchedule = function(row){ + var schedule = Object.assign(row,{}) + if(!s.schedules[schedule.ke])s.schedules[schedule.ke] = {} + s.checkDetails(schedule) + if(!s.schedules[schedule.ke][schedule.name]){ + s.schedules[schedule.ke][schedule.name] = schedule + }else{ + s.schedules[schedule.ke][schedule.name] = Object.assign(s.schedules[schedule.ke][schedule.name],schedule) + } + } + //check time in schedule + s.checkTimeAgainstSchedule = function(start,end,callback){ + try{ + if( + start + ){ + var checkStartTime = new Date() + var startSplit = start.split(':') + var startHour = parseInt(startSplit[0]) + var startMin = parseInt(startSplit[1]) + checkStartTime.setHours(startHour) + checkStartTime.setMinutes(startMin) + if(end){ + var checkEndTime = new Date() + var endSplit = end.split(':') + var endHour = parseInt(endSplit[0]) + var endMin = parseInt(endSplit[1]) + checkEndTime.setHours(endHour) + checkEndTime.setMinutes(endMin) + } + var currentDate = new Date() + if( + ( + currentDate >= checkStartTime && + currentDate <= checkEndTime + ) || + currentDate >= checkStartTime && !end + ){ + callback() + }else{ + callback({ + currentDate : currentDate, + startTime : checkStartTime, + endTime : checkEndTime + }) + } + }else{ + callback() + } + }catch(err){ + console.log(err) + callback() + } + } + //check all Schedules + s.checkSchedules = function(v,callback){ + var groupKeys = Object.keys(s.schedules) + groupKeys.forEach(function(key){ + var scheduleNames = Object.keys(s.schedules[key]) + scheduleNames.forEach(function(name){ + var schedule = s.schedules[key][name] + if(!schedule.active && schedule.enabled === 1 && schedule.start && schedule.details.monitorStates){ + s.checkTimeAgainstSchedule(schedule.start,schedule.end,function(err){ + if(!err){ + schedule.active = true + var monitorStates = schedule.details.monitorStates + monitorStates.forEach(function(stateName){ + s.activateMonitorStates(key,stateName,{ + ke: key, + uid: 'System', + details: {}, + permissions: {}, + lang: lang + },function(endData){ + // console.log(endData) + }) + }) + }else{ + schedule.active = false + } + }) + } + }) + }) + } + // + s.findSchedule = function(groupKey,name,callback){ + //presetQueryVals = [ke, type, name] + s.sqlQuery("SELECT * FROM Schedules WHERE ke=? AND name=? LIMIT 1",[groupKey,name],function(err,schedules){ + var schedule + var notFound = false + if(schedules && schedules[0]){ + schedule = schedules[0] + s.checkDetails(schedule) + }else{ + notFound = true + } + callback(notFound,schedule) + }) + } + // + var onProcessReady = function(){ + s.getAllSchedules(function(){ + s.checkSchedules() + }) + setInterval(function(){ + s.checkSchedules() + },1000 * 60 * 5) + } + /** + * WebServerPath : API : Get Schedule + */ + app.all([ + config.webPaths.apiPrefix+':auth/schedule/:ke', + config.webPaths.adminApiPrefix+':auth/schedule/:ke', + config.webPaths.apiPrefix+':auth/schedule/:ke/:name', + config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name', + config.webPaths.apiPrefix+':auth/schedules/:ke', + config.webPaths.adminApiPrefix+':auth/schedules/:ke', + config.webPaths.apiPrefix+':auth/schedules/:ke/:name', + config.webPaths.adminApiPrefix+':auth/schedules/:ke/:name', + ],function (req,res){ + s.auth(req.params,function(user){ + var endData = { + ok : false + } + if(user.details.sub){ + endData.msg = user.lang['Not Permitted'] + s.closeJsonResponse(res,endData) + return + } + var theQuery = "SELECT * FROM Schedules WHERE ke=?" + var theQueryValues = [req.params.ke] + if(req.params.name){ + theQuery += ' AND name=?' + theQueryValues.push(req.params.name) + } + s.sqlQuery(theQuery,theQueryValues,function(err,schedules){ + if(schedules && schedules[0]){ + endData.ok = true + schedules.forEach(function(schedule){ + s.checkDetails(schedule) + }) + endData.schedules = schedules + }else{ + endData.msg = user.lang['Not Found'] + } + s.closeJsonResponse(res,endData) + }) + }) + }) + /** + * WebServerPath : API : Update Schedule + */ + app.all([ + config.webPaths.apiPrefix+':auth/schedule/:ke/:name/:action', + config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name/:action', + config.webPaths.apiPrefix+':auth/schedules/:ke/:name/:action', + config.webPaths.adminApiPrefix+':auth/schedules/:ke/:name/:action' + ],function (req,res){ + s.auth(req.params,function(user){ + var endData = { + ok : false + } + if(user.details.sub){ + endData.msg = user.lang['Not Permitted'] + s.closeJsonResponse(res,endData) + return + } + switch(req.params.action){ + case'insert':case'edit': + var form = s.getPostData(req) + s.checkDetails(form) + if(!form || !form.details){ + endData.msg = user.lang['Form Data Not Found'] + s.closeJsonResponse(res,endData) + return + } + form.enabled = parseInt(form.enabled) || 1; + s.findSchedule(req.params.ke,req.params.name,function(notFound,preset){ + if(notFound === true){ + endData.msg = lang["Inserted Schedule Configuration"] + var insertData = { + ke: req.params.ke, + name: req.params.name, + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled + } + s.sqlQuery('INSERT INTO Schedules ('+Object.keys(insertData).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(insertData)) + s.tx({ + f: 'add_schedule', + insertData: insertData, + ke: req.params.ke, + name: req.params.name + },'GRP_'+req.params.ke) + }else{ + endData.msg = lang["Edited Schedule Configuration"] + var insertData = { + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled, + ke: req.params.ke, + name: req.params.name + } + s.sqlQuery('UPDATE Schedules SET details=?,start=?,end=?,enabled=? WHERE ke=? AND name=?',Object.values(insertData)) + s.tx({ + f: 'edit_schedule', + insertData: insertData, + ke: req.params.ke, + name: req.params.name + },'GRP_'+req.params.ke) + } + s.updateSchedule({ + ke: req.params.ke, + name: req.params.name, + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled + }) + endData.ok = true + s.closeJsonResponse(res,endData) + }) + break; + case'delete': + s.findSchedule(req.params.ke,req.params.name,function(notFound,schedule){ + if(notFound === true){ + endData.msg = user.lang['Schedule Configuration Not Found'] + s.closeJsonResponse(res,endData) + }else{ + s.sqlQuery('DELETE FROM Schedules WHERE ke=? AND name=?',[req.params.ke,req.params.name],function(err){ + if(!err){ + endData.msg = lang["Deleted Schedule Configuration"] + endData.ok = true + if(s.schedules[schedule.ke])delete(s.schedules[schedule.ke][schedule.name]) + } + s.closeJsonResponse(res,endData) + }) + } + }) + break; + } + }) + }) + //bind events + s.onProcessReady(onProcessReady) +} diff --git a/libs/socketio.js b/libs/socketio.js index c690f0e..3e0b139 100644 --- a/libs/socketio.js +++ b/libs/socketio.js @@ -9,12 +9,8 @@ module.exports = function(s,config,lang,io){ s.clientSocketConnection = {} //send data to detector plugin s.ocvTx=function(data){ - if(!s.ocv){return} - if(s.ocv.isClientPlugin===true){ - s.tx(data,s.ocv.id) - }else{ - s.connectedPlugins[s.ocv.plug].tx(data) - } + // chaining coming in future update + s.sendToAllDetectors(data) } //send data to socket client function s.tx = function(z,y,x){if(x){return x.broadcast.to(y).emit('f',z)};io.to(y).emit('f',z);} @@ -446,8 +442,8 @@ module.exports = function(s,config,lang,io){ s.group[d.ke].mon={} if(!s.group[d.ke].mon){s.group[d.ke].mon={}} } - if(s.ocv){ - tx({f:'detector_plugged',plug:s.ocv.plug,notice:s.ocv.notice}) + if(s.isAtleatOneDetectorPluginConnected){ + s.sendDetectorInfoToClient({f:'detector_plugged'},tx) s.ocvTx({f:'readPlugins',ke:d.ke}) } tx({f:'users_online',users:s.group[d.ke].users}) @@ -477,6 +473,9 @@ module.exports = function(s,config,lang,io){ console.log(err) } }) + s.onSocketAuthenticationExtensions.forEach(function(extender){ + extender(r,cn) + }) } s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ @@ -512,12 +511,23 @@ module.exports = function(s,config,lang,io){ s.ocvTx(d.data) break; case'monitorOrder': - if(d.monitorOrder&&d.monitorOrder instanceof Object){ + if(d.monitorOrder && d.monitorOrder instanceof Object){ s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){ - if(r&&r[0]){ - r=JSON.parse(r[0].details); - r.monitorOrder=d.monitorOrder; - s.sqlQuery('UPDATE Users SET details=? WHERE uid=? AND ke=?',[JSON.stringify(r),cn.uid,cn.ke]) + if(r && r[0]){ + details = JSON.parse(r[0].details) + details.monitorOrder = d.monitorOrder + s.sqlQuery('UPDATE Users SET details=? WHERE uid=? AND ke=?',[s.s(details),cn.uid,cn.ke]) + } + }) + } + break; + case'monitorListOrder': + if(d.monitorListOrder && d.monitorListOrder instanceof Object){ + s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){ + if(r && r[0]){ + details = JSON.parse(r[0].details) + details.monitorListOrder = d.monitorListOrder + s.sqlQuery('UPDATE Users SET details=? WHERE uid=? AND ke=?',[s.s(details),cn.uid,cn.ke]) } }) } @@ -1355,22 +1365,29 @@ module.exports = function(s,config,lang,io){ } } if(cn.pluginEngine){ - s.connectedPlugins[cn.pluginEngine].plugged=false + s.connectedPlugins[cn.pluginEngine].plugged = false s.tx({f:'plugin_engine_unplugged',plug:cn.pluginEngine},'CPU') - delete(s.api[cn.pluginEngine]) } if(cn.cron){ delete(s.cron); } - if(cn.ocv){ - s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU') - delete(s.ocv); - delete(s.api[cn.id]) + if(cn.detectorPlugin){ + s.tx({f:'detector_unplugged',plug:cn.detectorPlugin},'CPU') + s.removeDetectorPlugin(cn.detectorPlugin) + s.sendDetectorInfoToClient({f:'detector_plugged'},function(data){ + s.tx(data,'CPU') + }) } if(cn.superSessionKey){ delete(s.superUsersApi[cn.superSessionKey]) } + s.onWebSocketDisconnectionExtensions.forEach(function(extender){ + extender(cn) + }) delete(s.clientSocketConnection[cn.id]) }) + s.onWebSocketConnectionExtensions.forEach(function(extender){ + extender(cn) + }) }); } diff --git a/libs/sql.js b/libs/sql.js index d9d3d7b..27b0639 100644 --- a/libs/sql.js +++ b/libs/sql.js @@ -1,12 +1,24 @@ +moment = require('moment') module.exports = function(s,config){ + s.onBeforeDatabaseLoadExtensions.forEach(function(extender){ + extender(config) + }) //sql/database connection with knex s.databaseOptions = { client: config.databaseType, connection: config.db, } + var isSqlite = false if(s.databaseOptions.client.indexOf('sqlite')>-1){ + isSqlite = true s.databaseOptions.client = 'sqlite3'; s.databaseOptions.useNullAsDefault = true; + try{ + require('sqlite3') + }catch(err){ + console.log('Installing SQlite3 Module...') + require('child_process').execSync('npm install sqlite3 --unsafe-perm') + } } if(s.databaseOptions.client === 'sqlite3' && s.databaseOptions.connection.filename === undefined){ s.databaseOptions.connection.filename = s.mainDirectory+"/shinobi.sqlite" @@ -38,7 +50,7 @@ module.exports = function(s,config){ return newQuery } s.stringToSqlTime = function(value){ - newValue = new Date(value.replace('T',' ')) + newValue = new Date(s.nameToTime(value)).valueOf() return newValue } s.sqlQuery = function(query,values,onMoveOn,hideLog){ @@ -48,6 +60,11 @@ module.exports = function(s,config){ var values = []; } if(!onMoveOn){onMoveOn=function(){}} + // if(s.databaseOptions.client === 'pg'){ + // query = query + // .replace(/ NOT LIKE /g," NOT ILIKE ") + // .replace(/ LIKE /g," ILIKE ") + // } var mergedQuery = s.mergeQueryValues(query,values) s.debugLog('s.sqlQuery QUERY',mergedQuery) if(!s.databaseEngine || !s.databaseEngine.raw){ @@ -73,25 +90,54 @@ module.exports = function(s,config){ } }) } + s.openDatabaseTable = function(tableName){ + return s.databaseEngine(tableName) + } s.connectDatabase = function(){ s.databaseEngine = require('knex')(s.databaseOptions) } s.preQueries = function(){ - //add Cloud Videos table, will remove in future - s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\' COMMENT \'0:Complete,1:Read,2:Archive\',`details` text) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;',[],function(err){ - // if(err)console.log(err) + var knex = s.databaseEngine + var mySQLtail = '' + if(config.databaseType === 'mysql'){ + mySQLtail = ' ENGINE=InnoDB DEFAULT CHARSET=utf8' + } + //add Presets table and modernize + var createPresetsTableQuery = 'CREATE TABLE IF NOT EXISTS `Presets` ( `ke` varchar(50) DEFAULT NULL, `name` text, `details` text, `type` varchar(50) DEFAULT NULL)' + s.sqlQuery( createPresetsTableQuery + mySQLtail + ';',[],function(err){ + if(err)console.error(err) + if(config.databaseType === 'sqlite3'){ + var aQuery = "ALTER TABLE Presets RENAME TO _Presets_old;" + aQuery += createPresetsTableQuery + aQuery += "INSERT INTO Presets (`ke`, `name`, `details`, `type`) SELECT `ke`, `name`, `details`, `type` FROM _Presets_old;COMMIT;DROP TABLE _Presets_old;" + }else{ + s.sqlQuery('ALTER TABLE `Presets` CHANGE COLUMN `type` `type` VARCHAR(50) NULL DEFAULT NULL AFTER `details`;',[],function(err){ + if(err)console.error(err) + },true) + } },true) - //add monitorStates to Preset ENUM - s.sqlQuery('ALTER TABLE `Presets` CHANGE COLUMN `type` `type` VARCHAR(50) NULL DEFAULT NULL AFTER `details`;',[],function(err){ - // if(err)console.log(err) + //add Schedules table, will remove in future + s.sqlQuery("CREATE TABLE IF NOT EXISTS `Schedules` (`ke` varchar(50) DEFAULT NULL,`name` text,`details` text,`start` varchar(10) DEFAULT NULL,`end` varchar(10) DEFAULT NULL,`enabled` int(1) NOT NULL DEFAULT '1')" + mySQLtail + ';',[],function(err){ + if(err)console.error(err) + },true) + //add Cloud Videos table, will remove in future + s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\',`details` text)' + mySQLtail + ';',[],function(err){ + if(err)console.error(err) },true) //create Files table - s.sqlQuery('CREATE TABLE IF NOT EXISTS `Files` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`name` tinytext NOT NULL,`size` float NOT NULL DEFAULT \'0\',`details` text NOT NULL,`status` int(1) NOT NULL DEFAULT \'0\') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;',[],function(err){ - // if(err)console.log(err) + var createFilesTableQuery = "CREATE TABLE IF NOT EXISTS `Files` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`name` tinytext NOT NULL,`size` float NOT NULL DEFAULT '0',`details` text NOT NULL,`status` int(1) NOT NULL DEFAULT '0',`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP)" + s.sqlQuery(createFilesTableQuery + mySQLtail + ';',[],function(err){ + if(err)console.error(err) //add time column to Files table - s.sqlQuery('ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;',[],function(err){ - // if(err)console.log(err) - },true) + if(config.databaseType === 'sqlite3'){ + var aQuery = "ALTER TABLE Files RENAME TO _Files_old;" + aQuery += createPresetsTableQuery + aQuery += "INSERT INTO Files (`ke`, `mid`, `name`, `details`, `size`, `status`, `time`) SELECT `ke`, `mid`, `name`, `details`, `size`, `status`, `time` FROM _Files_old;COMMIT;DROP TABLE _Files_old;" + }else{ + s.sqlQuery('ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;',[],function(err){ + if(err && err.sqlMessage.indexOf('Duplicate') === -1)console.error(err) + },true) + } },true) delete(s.preQueries) } diff --git a/libs/startup.js b/libs/startup.js index 3a7b937..5754931 100644 --- a/libs/startup.js +++ b/libs/startup.js @@ -9,10 +9,34 @@ module.exports = function(s,config,lang,io){ console.log('Node.js version : '+execSync("node -v")) s.processReady = function(){ s.systemLog(lang.startUpText5) + s.onProcessReadyExtensions.forEach(function(extender){ + extender(true) + }) process.send('ready') } + var checkForTerminalCommands = function(callback){ + var next = function(){ + if(callback)callback() + } + if(!s.isWin){ + var etcPath = '/etc/shinobisystems/cctv.txt' + fs.stat(etcPath,function(err,stat){ + if(err || !stat){ + exec('node '+ s.mainDirectory + '/INSTALL/terminalCommands.js',function(err){ + if(err)console.log(err) + }) + } + next() + }) + }else{ + next() + } + } var loadedAccounts = [] var loadMonitors = function(callback){ + s.beforeMonitorsLoadedOnStartupExtensions.forEach(function(extender){ + extender() + }) s.systemLog(lang.startUpText4) //preliminary monitor start s.sqlQuery('SELECT * FROM Monitors', function(err,monitors) { @@ -147,6 +171,9 @@ module.exports = function(s,config,lang,io){ }) }) },10000) + //hourly check to see if sizePurge has failed to unlock + //checks to see if request count is the number of monitors + 10 + s.checkForStalePurgeLocks() //run prerequsite queries, load users and monitors if(config.childNodes.mode !== 'child'){ //sql/database connection with knex @@ -154,11 +181,13 @@ module.exports = function(s,config,lang,io){ //run prerequsite queries s.preQueries() setTimeout(function(){ - //load administrators (groups) - loadAdminUsers(function(){ - //load monitors (for groups) - loadMonitors(function(){ - s.processReady() + checkForTerminalCommands(function(){ + //load administrators (groups) + loadAdminUsers(function(){ + //load monitors (for groups) + loadMonitors(function(){ + s.processReady() + }) }) }) },1500) diff --git a/libs/uploaders.js b/libs/uploaders.js new file mode 100644 index 0000000..7b67957 --- /dev/null +++ b/libs/uploaders.js @@ -0,0 +1,11 @@ +module.exports = function(s,config,lang){ + var loadLib = function(lib){ + return require('./uploaders/' + lib + '.js') + } + loadLib('loader')(s,config,lang) + loadLib('backblazeB2')(s,config,lang) + loadLib('amazonS3')(s,config,lang) + loadLib('webdav')(s,config,lang) + loadLib('wasabi')(s,config,lang) + loadLib('sftp')(s,config,lang) +} diff --git a/libs/uploaders/amazonS3.js b/libs/uploaders/amazonS3.js new file mode 100644 index 0000000..345f458 --- /dev/null +++ b/libs/uploaders/amazonS3.js @@ -0,0 +1,137 @@ +var fs = require('fs'); +module.exports = function(s,config,lang){ + //Amazon S3 + var beforeAccountSaveForAmazonS3 = function(d){ + //d = save event + d.form.details.aws_use_global=d.d.aws_use_global + d.form.details.use_aws_s3=d.d.use_aws_s3 + } + var cloudDiskUseStartupForAmazonS3 = function(group,userDetails){ + group.cloudDiskUse['s3'].name = 'Amazon S3' + group.cloudDiskUse['s3'].sizeLimitCheck = (userDetails.use_aws_s3_size_limit === '1') + if(!userDetails.aws_s3_size_limit || userDetails.aws_s3_size_limit === ''){ + group.cloudDiskUse['s3'].sizeLimit = 10000 + }else{ + group.cloudDiskUse['s3'].sizeLimit = parseFloat(userDetails.aws_s3_size_limit) + } + } + var loadAmazonS3ForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details) + if(userDetails.aws_use_global === '1' && config.cloudUploaders && config.cloudUploaders.AmazonS3){ + // { + // aws_accessKeyId: "", + // aws_secretAccessKey: "", + // aws_region: "", + // aws_s3_bucket: "", + // aws_s3_dir: "", + // } + userDetails = Object.assign(userDetails,config.cloudUploaders.AmazonS3) + } + //Amazon S3 + if(!s.group[e.ke].aws && + !s.group[e.ke].aws_s3 && + userDetails.aws_s3 !== '0' && + userDetails.aws_accessKeyId !== ''&& + userDetails.aws_secretAccessKey && + userDetails.aws_secretAccessKey !== ''&& + userDetails.aws_region && + userDetails.aws_region !== ''&& + userDetails.aws_s3_bucket !== '' + ){ + if(!userDetails.aws_s3_dir || userDetails.aws_s3_dir === '/'){ + userDetails.aws_s3_dir = '' + } + if(userDetails.aws_s3_dir !== ''){ + userDetails.aws_s3_dir = s.checkCorrectPathEnding(userDetails.aws_s3_dir) + } + s.group[e.ke].aws = new require("aws-sdk") + s.group[e.ke].aws.config = new s.group[e.ke].aws.Config({ + accessKeyId: userDetails.aws_accessKeyId, + secretAccessKey: userDetails.aws_secretAccessKey, + region: userDetails.aws_region + }) + s.group[e.ke].aws_s3 = new s.group[e.ke].aws.S3(); + } + } + var unloadAmazonS3ForUser = function(user){ + s.group[user.ke].aws = null + s.group[user.ke].aws_s3 = null + } + var deleteVideoFromAmazonS3 = function(e,video,callback){ + // e = user + try{ + var videoDetails = JSON.parse(video.details) + }catch(err){ + var videoDetails = video.details + } + if(!videoDetails.location){ + videoDetails.location = video.href.split('.amazonaws.com')[1] + } + s.group[e.ke].aws_s3.deleteObject({ + Bucket: s.group[e.ke].init.aws_s3_bucket, + Key: videoDetails.location, + }, function(err, data) { + if (err) console.log(err); + callback() + }); + } + var uploadVideoToAmazonS3 = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - amazon s3 + if(s.group[e.ke].aws_s3 && s.group[e.ke].init.use_aws_s3 !== '0' && s.group[e.ke].init.aws_s3_save === '1'){ + var ext = k.filename.split('.') + ext = ext[ext.length - 1] + var fileStream = fs.createReadStream(k.dir+k.filename); + fileStream.on('error', function (err) { + console.error(err) + }) + var saveLocation = s.group[e.ke].init.aws_s3_dir+e.ke+'/'+e.mid+'/'+k.filename + s.group[e.ke].aws_s3.upload({ + Bucket: s.group[e.ke].init.aws_s3_bucket, + Key: saveLocation, + Body:fileStream, + ACL:'public-read', + ContentType:'video/'+ext + },function(err,data){ + if(err){ + s.userLog(e,{type:lang['Amazon S3 Upload Error'],msg:err}) + } + if(s.group[e.ke].init.aws_s3_log === '1' && data && data.Location){ + var save = [ + e.mid, + e.ke, + k.startTime, + 1, + s.s({ + type : 's3', + location : saveLocation + }), + k.filesize, + k.endTime, + data.Location + ] + s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) + s.setCloudDiskUsedForGroup(e,{ + amount : k.filesizeMB, + storageType : 's3' + }) + s.purgeCloudDiskForGroup(e,'s3') + } + }) + } + } + //amazon s3 + s.addCloudUploader({ + name: 's3', + loadGroupAppExtender: loadAmazonS3ForUser, + unloadGroupAppExtender: unloadAmazonS3ForUser, + insertCompletedVideoExtender: uploadVideoToAmazonS3, + deleteVideoFromCloudExtensions: deleteVideoFromAmazonS3, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForAmazonS3, + beforeAccountSave: beforeAccountSaveForAmazonS3, + onAccountSave: cloudDiskUseStartupForAmazonS3, + }) +} diff --git a/libs/uploaders/backblazeB2.js b/libs/uploaders/backblazeB2.js new file mode 100644 index 0000000..c8f6f1d --- /dev/null +++ b/libs/uploaders/backblazeB2.js @@ -0,0 +1,170 @@ +var fs = require('fs'); +module.exports = function(s,config,lang){ + //Backblaze B2 + var beforeAccountSaveForBackblazeB2 = function(d){ + //d = save event + d.form.details.b2_use_global=d.d.b2_use_global + d.form.details.use_bb_b2=d.d.use_bb_b2 + } + var cloudDiskUseStartupForBackblazeB2 = function(group,userDetails){ + group.cloudDiskUse['b2'].name = 'Backblaze B2' + group.cloudDiskUse['b2'].sizeLimitCheck = (userDetails.use_bb_b2_size_limit === '1') + if(!userDetails.bb_b2_size_limit || userDetails.bb_b2_size_limit === ''){ + group.cloudDiskUse['b2'].sizeLimit = 10000 + }else{ + group.cloudDiskUse['b2'].sizeLimit = parseFloat(userDetails.bb_b2_size_limit) + } + } + var loadBackblazeB2ForUser = function(e){ + var userDetails = JSON.parse(e.details); + try{ + if(userDetails.b2_use_global === '1' && config.cloudUploaders && config.cloudUploaders.BackblazeB2){ + // { + // bb_b2_accountId: "", + // bb_b2_applicationKey: "", + // bb_b2_bucket: "", + // bb_b2_dir: "", + // } + userDetails = Object.assign(userDetails,config.cloudUploaders.BackblazeB2) + } + if(!s.group[e.ke].bb_b2 && + userDetails.bb_b2_accountId && + userDetails.bb_b2_accountId !=='' && + userDetails.bb_b2_applicationKey && + userDetails.bb_b2_applicationKey !=='' && + userDetails.bb_b2_bucket && + userDetails.bb_b2_bucket !== '' + ){ + var B2 = require('backblaze-b2') + if(!userDetails.bb_b2_dir || userDetails.bb_b2_dir === '/'){ + userDetails.bb_b2_dir = '' + } + if(userDetails.bb_b2_dir !== ''){ + userDetails.bb_b2_dir = s.checkCorrectPathEnding(userDetails.bb_b2_dir) + } + var backblazeErr = function(err){ + // console.log(err) + s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.data || err}) + } + var createB2Connection = function(){ + var b2 = new B2({ + accountId: userDetails.bb_b2_accountId, + applicationKey: userDetails.bb_b2_applicationKey + }) + b2.authorize().then(function(resp){ + s.group[e.ke].bb_b2_downloadUrl = resp.data.downloadUrl + b2.listBuckets().then(function(resp){ + var buckets = resp.data.buckets + var bucketN = -2 + buckets.forEach(function(item,n){ + if(item.bucketName === userDetails.bb_b2_bucket){ + bucketN = n + } + }) + if(bucketN > -1){ + s.group[e.ke].bb_b2_bucketId = buckets[bucketN].bucketId + }else{ + b2.createBucket( + userDetails.bb_b2_bucket, + 'allPublic' + ).then(function(resp){ + s.group[e.ke].bb_b2_bucketId = resp.data.bucketId + }).catch(backblazeErr) + } + }).catch(backblazeErr) + }).catch(backblazeErr) + s.group[e.ke].bb_b2 = b2 + } + createB2Connection() + s.group[e.ke].bb_b2_refreshTimer = setInterval(createB2Connection,1000 * 60 * 60) + } + }catch(err){ + s.debugLog(err) + } + } + var unloadBackblazeB2ForUser = function(user){ + s.group[user.ke].bb_b2 = null + clearInterval(s.group[user.ke].bb_b2_refreshTimer) + } + var deleteVideoFromBackblazeB2 = function(e,video,callback){ + // e = user + try{ + var videoDetails = JSON.parse(video.details) + }catch(err){ + var videoDetails = video.details + } + s.group[e.ke].bb_b2.deleteFileVersion({ + fileId: videoDetails.fileId, + fileName: videoDetails.fileName + }).then(function(resp){ + // console.log('deleteFileVersion',resp.data) + }).catch(function(err){ + console.log('deleteFileVersion',err) + }) + } + var uploadVideoToBackblazeB2 = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - Backblaze B2 + if(s.group[e.ke].bb_b2 && s.group[e.ke].init.use_bb_b2 !== '0' && s.group[e.ke].init.bb_b2_save === '1'){ + var backblazeErr = function(err){ + // console.log(err) + s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.data}) + } + fs.readFile(k.dir+k.filename,function(err,data){ + var backblazeSavePath = s.group[e.ke].init.bb_b2_dir+e.ke+'/'+e.mid+'/'+k.filename + var getUploadUrl = function(bucketId,callback){ + s.group[e.ke].bb_b2.getUploadUrl(bucketId).then(function(resp){ + callback(resp.data) + }).catch(backblazeErr) + } + getUploadUrl(s.group[e.ke].bb_b2_bucketId,function(req){ + s.group[e.ke].bb_b2.uploadFile({ + uploadUrl: req.uploadUrl, + uploadAuthToken: req.authorizationToken, + filename: backblazeSavePath, + data: data, + onUploadProgress: null + }).then(function(resp){ + if(s.group[e.ke].init.bb_b2_log === '1' && resp.data.fileId){ + var backblazeDownloadUrl = s.group[e.ke].bb_b2_downloadUrl + '/file/' + s.group[e.ke].init.bb_b2_bucket + '/' + backblazeSavePath + var save = [ + e.mid, + e.ke, + k.startTime, + 1, + s.s({ + type : 'b2', + bucketId : resp.data.bucketId, + fileId : resp.data.fileId, + fileName : resp.data.fileName + }), + k.filesize, + k.endTime, + backblazeDownloadUrl + ] + s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) + s.setCloudDiskUsedForGroup(e,{ + amount : k.filesizeMB, + storageType : 'b2' + }) + s.purgeCloudDiskForGroup(e,'b2') + } + }).catch(backblazeErr) + }) + }) + } + } + //backblaze b2 + s.addCloudUploader({ + name: 'b2', + loadGroupAppExtender: loadBackblazeB2ForUser, + unloadGroupAppExtender: unloadBackblazeB2ForUser, + insertCompletedVideoExtender: uploadVideoToBackblazeB2, + deleteVideoFromCloudExtensions: deleteVideoFromBackblazeB2, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForBackblazeB2, + beforeAccountSave: beforeAccountSaveForBackblazeB2, + onAccountSave: cloudDiskUseStartupForBackblazeB2, + }) +} diff --git a/libs/uploaders/loader.js b/libs/uploaders/loader.js new file mode 100644 index 0000000..8634701 --- /dev/null +++ b/libs/uploaders/loader.js @@ -0,0 +1,20 @@ +module.exports = function(s){ + s.addCloudUploader = function(opt){ + s.loadGroupAppExtender(opt.loadGroupAppExtender) + s.unloadGroupAppExtender(opt.unloadGroupAppExtender) + s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender) + s.deleteVideoFromCloudExtensions[opt.name] = opt.deleteVideoFromCloudExtensions + s.cloudDiskUseStartupExtensions[opt.name] = opt.cloudDiskUseStartupExtensions + s.beforeAccountSave(opt.beforeAccountSave) + s.onAccountSave(opt.onAccountSave) + s.cloudDisksLoader(opt.name) + } + s.addSimpleUploader = function(opt){ + s.loadGroupAppExtender(opt.loadGroupAppExtender) + s.unloadGroupAppExtender(opt.unloadGroupAppExtender) + s.insertCompletedVideoExtender(opt.insertCompletedVideoExtender) + s.beforeAccountSave(opt.beforeAccountSave) + s.onAccountSave(opt.onAccountSave) + s.onMonitorSave(opt.onMonitorSave) + } +} diff --git a/libs/uploaders/sftp.js b/libs/uploaders/sftp.js new file mode 100644 index 0000000..46edbf4 --- /dev/null +++ b/libs/uploaders/sftp.js @@ -0,0 +1,90 @@ +var fs = require('fs'); +var ssh2SftpClient = require('node-ssh') +module.exports = function(s,config,lang){ + //SFTP + var sftpErr = function(err){ + // console.log(err) + s.userLog({mid:'$USER',ke:e.ke},{type:lang['SFTP Error'],msg:err.data || err}) + } + var beforeAccountSaveForSftp = function(d){ + //d = save event + d.form.details.use_sftp = d.d.use_sftp + } + var loadSftpForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details); + //SFTP + if(!s.group[e.ke].sftp && + !s.group[e.ke].sftp && + userDetails.sftp !== '0' && + userDetails.sftp_host && + userDetails.sftp_host !== ''&& + userDetails.sftp_port && + userDetails.sftp_port !== '' + ){ + if(!userDetails.sftp_dir || userDetails.sftp_dir === '/'){ + userDetails.sftp_dir = '' + } + if(userDetails.sftp_dir !== ''){ + userDetails.sftp_dir = s.checkCorrectPathEnding(userDetails.sftp_dir) + } + var sftp = new ssh2SftpClient() + var connectionDetails = { + host: userDetails.sftp_host, + port: userDetails.sftp_port + } + if(!userDetails.sftp_port)connectionDetails.port = 22 + if(userDetails.sftp_username && userDetails.sftp_username !== '')connectionDetails.username = userDetails.sftp_username + if(userDetails.sftp_password && userDetails.sftp_password !== '')connectionDetails.password = userDetails.sftp_password + if(userDetails.sftp_privateKey && userDetails.sftp_privateKey !== '')connectionDetails.privateKey = userDetails.sftp_privateKey + sftp.connect(connectionDetails).catch(sftpErr) + s.group[e.ke].sftp = sftp + } + } + var unloadSftpForUser = function(user){ + if(s.group[user.ke].sftp && s.group[user.ke].sftp.end)s.group[user.ke].sftp.end().then(function(){ + s.group[user.ke].sftp = null + }) + } + var uploadVideoToSftp = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - SFTP + if(s.group[e.ke].sftp && s.group[e.ke].init.use_sftp !== '0' && s.group[e.ke].init.sftp_save === '1'){ + var localPath = k.dir + k.filename + var saveLocation = s.group[e.ke].init.sftp_dir + e.ke + '/' + e.mid + '/' + k.filename + s.group[e.ke].sftp.putFile(localPath, saveLocation).catch(sftpErr) + } + } + var createSftpDirectory = function(monitorConfig){ + var monitorSaveDirectory = s.group[monitorConfig.ke].init.sftp_dir + monitorConfig.ke + '/' + monitorConfig.mid + s.group[monitorConfig.ke].sftp.mkdir(monitorSaveDirectory, true).catch(function(err){ + if(err.code !== 'ERR_ASSERTION'){ + sftpErr(err) + } + }) + } + var onMonitorSaveForSftp = function(monitorConfig){ + if(s.group[monitorConfig.ke].sftp && s.group[monitorConfig.ke].init.use_sftp !== '0' && s.group[monitorConfig.ke].init.sftp_save === '1'){ + createSftpDirectory(monitorConfig) + } + } + var onAccountSaveForSftp = function(group,userDetails,user){ + if(s.group[user.ke] && s.group[user.ke].sftp && s.group[user.ke].init.use_sftp !== '0' && s.group[user.ke].init.sftp_save === '1'){ + Object.keys(s.group[user.ke].mon_conf).forEach(function(monitorId){ + createSftpDirectory(s.group[user.ke].mon_conf[monitorId]) + }) + } + } + //SFTP (Simple Uploader) + s.addSimpleUploader({ + name: 'sftp', + loadGroupAppExtender: loadSftpForUser, + unloadGroupAppExtender: unloadSftpForUser, + insertCompletedVideoExtender: uploadVideoToSftp, + beforeAccountSave: beforeAccountSaveForSftp, + onAccountSave: onAccountSaveForSftp, + onMonitorSave: onMonitorSaveForSftp, + }) +} diff --git a/libs/uploaders/wasabi.js b/libs/uploaders/wasabi.js new file mode 100644 index 0000000..56b416c --- /dev/null +++ b/libs/uploaders/wasabi.js @@ -0,0 +1,138 @@ +var fs = require('fs'); +module.exports = function(s,config,lang){ + //Wasabi Hot Cloud Storage + var beforeAccountSaveForWasabiHotCloudStorage = function(d){ + //d = save event + d.form.details.whcs_use_global=d.d.whcs_use_global + d.form.details.use_whcs=d.d.use_whcs + } + var cloudDiskUseStartupForWasabiHotCloudStorage = function(group,userDetails){ + group.cloudDiskUse['whcs'].name = 'Wasabi Hot Cloud Storage' + group.cloudDiskUse['whcs'].sizeLimitCheck = (userDetails.use_whcs_size_limit === '1') + if(!userDetails.whcs_size_limit || userDetails.whcs_size_limit === ''){ + group.cloudDiskUse['whcs'].sizeLimit = 10000 + }else{ + group.cloudDiskUse['whcs'].sizeLimit = parseFloat(userDetails.whcs_size_limit) + } + } + var loadWasabiHotCloudStorageForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details) + if(userDetails.whcs_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WasabiHotCloudStorage){ + // { + // whcs_accessKeyId: "", + // whcs_secretAccessKey: "", + // whcs_region: "", + // whcs_bucket: "", + // whcs_dir: "", + // } + userDetails = Object.assign(userDetails,config.cloudUploaders.WasabiHotCloudStorage) + } + //Wasabi Hot Cloud Storage + if(!s.group[e.ke].whcs && + userDetails.whcs !== '0' && + userDetails.whcs_accessKeyId !== ''&& + userDetails.whcs_secretAccessKey && + userDetails.whcs_secretAccessKey !== ''&& + userDetails.whcs_region && + userDetails.whcs_region !== ''&& + userDetails.whcs_bucket !== '' + ){ + if(!userDetails.whcs_dir || userDetails.whcs_dir === '/'){ + userDetails.whcs_dir = '' + } + if(userDetails.whcs_dir !== ''){ + userDetails.whcs_dir = s.checkCorrectPathEnding(userDetails.whcs_dir) + } + var AWS = new require("aws-sdk") + s.group[e.ke].whcs = AWS + var wasabiEndpoint = new AWS.Endpoint('s3.wasabisys.com') + s.group[e.ke].whcs.config = new s.group[e.ke].whcs.Config({ + endpoint: wasabiEndpoint, + accessKeyId: userDetails.whcs_accessKeyId, + secretAccessKey: userDetails.whcs_secretAccessKey, + region: userDetails.whcs_region + }) + s.group[e.ke].whcs = new s.group[e.ke].whcs.S3(); + } + } + var unloadWasabiHotCloudStorageForUser = function(user){ + s.group[user.ke].whcs = null + } + var deleteVideoFromWasabiHotCloudStorage = function(e,video,callback){ + // e = user + try{ + var videoDetails = JSON.parse(video.details) + }catch(err){ + var videoDetails = video.details + } + if(!videoDetails.location){ + videoDetails.location = video.href.split('wasabisys.com')[1] + } + s.group[e.ke].whcs.deleteObject({ + Bucket: s.group[e.ke].init.whcs_bucket, + Key: videoDetails.location, + }, function(err, data) { + if (err) console.log(err); + callback() + }); + } + var uploadVideoToWasabiHotCloudStorage = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - Wasabi Hot Cloud Storage + if(s.group[e.ke].whcs && s.group[e.ke].init.use_whcs !== '0' && s.group[e.ke].init.whcs_save === '1'){ + var ext = k.filename.split('.') + ext = ext[ext.length - 1] + var fileStream = fs.createReadStream(k.dir+k.filename); + fileStream.on('error', function (err) { + console.error(err) + }) + var saveLocation = s.group[e.ke].init.whcs_dir+e.ke+'/'+e.mid+'/'+k.filename + s.group[e.ke].whcs.upload({ + Bucket: s.group[e.ke].init.whcs_bucket, + Key: saveLocation, + Body:fileStream, + ACL:'public-read', + ContentType:'video/'+ext + },function(err,data){ + if(err){ + s.userLog(e,{type:lang['Wasabi Hot Cloud Storage Upload Error'],msg:err}) + } + if(s.group[e.ke].init.whcs_log === '1' && data && data.Location){ + var save = [ + e.mid, + e.ke, + k.startTime, + 1, + s.s({ + type : 'whcs', + location : saveLocation + }), + k.filesize, + k.endTime, + data.Location + ] + s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) + s.setCloudDiskUsedForGroup(e,{ + amount : k.filesizeMB, + storageType : 'whcs' + }) + s.purgeCloudDiskForGroup(e,'whcs') + } + }) + } + } + //wasabi + s.addCloudUploader({ + name: 'whcs', + loadGroupAppExtender: loadWasabiHotCloudStorageForUser, + unloadGroupAppExtender: unloadWasabiHotCloudStorageForUser, + insertCompletedVideoExtender: uploadVideoToWasabiHotCloudStorage, + deleteVideoFromCloudExtensions: deleteVideoFromWasabiHotCloudStorage, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForWasabiHotCloudStorage, + beforeAccountSave: beforeAccountSaveForWasabiHotCloudStorage, + onAccountSave: cloudDiskUseStartupForWasabiHotCloudStorage, + }) +} diff --git a/libs/uploaders/webdav.js b/libs/uploaders/webdav.js new file mode 100644 index 0000000..572924c --- /dev/null +++ b/libs/uploaders/webdav.js @@ -0,0 +1,169 @@ +var fs = require('fs'); +var webdav = require("webdav-fs"); +module.exports = function(s,config,lang){ + // WebDAV + var beforeAccountSaveForWebDav = function(d){ + //d = save event + d.form.details.webdav_use_global=d.d.webdav_use_global + d.form.details.use_webdav=d.d.use_webdav + } + var cloudDiskUseStartupForWebDav = function(group,userDetails){ + group.cloudDiskUse['webdav'].name = 'WebDAV' + group.cloudDiskUse['webdav'].sizeLimitCheck = (userDetails.use_webdav_size_limit === '1') + if(!userDetails.webdav_size_limit || userDetails.webdav_size_limit === ''){ + group.cloudDiskUse['webdav'].sizeLimit = 10000 + }else{ + group.cloudDiskUse['webdav'].sizeLimit = parseFloat(userDetails.webdav_size_limit) + } + } + var loadWebDavForUser = function(e){ + // e = user + var userDetails = JSON.parse(e.details); + if(userDetails.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){ + // { + // webdav_user: "", + // webdav_pass: "", + // webdav_url: "", + // webdav_dir: "", + // } + userDetails = Object.assign(userDetails,config.cloudUploaders.WebDAV) + } + //owncloud/webdav + if(!s.group[e.ke].webdav && + userDetails.webdav_user&& + userDetails.webdav_user!==''&& + userDetails.webdav_pass&& + userDetails.webdav_pass!==''&& + userDetails.webdav_url&& + userDetails.webdav_url!=='' + ){ + if(!userDetails.webdav_dir||userDetails.webdav_dir===''){ + userDetails.webdav_dir='/' + } + userDetails.webdav_dir = s.checkCorrectPathEnding(userDetails.webdav_dir) + s.group[e.ke].webdav = webdav( + userDetails.webdav_url, + userDetails.webdav_user, + userDetails.webdav_pass + ) + } + } + var unloadWebDavForUser = function(user){ + s.group[user.ke].webdav = null + } + var deleteVideoFromWebDav = function(e,video,callback){ + // e = user + try{ + var videoDetails = JSON.parse(video.details) + }catch(err){ + var videoDetails = video.details + } + if(!videoDetails.location){ + var prefix = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[e.ke].init.webdav_url),s.group[e.ke].init.webdav_user,s.group[e.ke].init.webdav_pass) + videoDetails.location = video.href.replace(prefix,'') + } + s.group[e.ke].webdav.unlink(videoDetails.location, function(err) { + if (err) console.log(videoDetails.location,err) + callback() + }) + } + var uploadVideoToWebDav = function(e,k){ + //e = video object + //k = temporary values + if(!k)k={}; + //cloud saver - webdav + var wfs = s.group[e.ke].webdav + if(wfs && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === "1"){ + var webdavUploadDir = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/' + var startWebDavUpload = function(){ + s.group[e.ke].mon[e.id].webdavDirExist = true + var wfsWriteStream = + fs.createReadStream(k.dir + k.filename).pipe(wfs.createWriteStream(webdavUploadDir + k.filename)) + if(s.group[e.ke].init.webdav_log === '1'){ + var webdavRemoteUrl = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[e.ke].init.webdav_url),s.group[e.ke].init.webdav_user,s.group[e.ke].init.webdav_pass) + s.group[e.ke].init.webdav_dir + e.ke + '/'+e.mid+'/'+k.filename + var save = [ + e.mid, + e.ke, + k.startTime, + 1, + s.s({ + type : 'webdav', + location : webdavUploadDir + k.filename + }), + k.filesize, + k.endTime, + webdavRemoteUrl + ] + s.sqlQuery('INSERT INTO `Cloud Videos` (mid,ke,time,status,details,size,end,href) VALUES (?,?,?,?,?,?,?,?)',save) + s.setCloudDiskUsedForGroup(e,{ + amount : k.filesizeMB, + storageType : 'webdav' + }) + s.purgeCloudDiskForGroup(e,'webdav') + } + } + if(s.group[e.ke].mon[e.id].webdavDirExist !== true){ + //check if webdav dir exist + var parentPoint = 0 + var webDavParentz = webdavUploadDir.split('/') + var webDavParents = [] + webDavParentz.forEach(function(v){ + if(v && v !== '')webDavParents.push(v) + }) + var stitchPieces = './' + var lastParentCheck = function(){ + ++parentPoint + if(parentPoint === webDavParents.length){ + startWebDavUpload() + } + checkPathPiece(webDavParents[parentPoint]) + } + var checkPathPiece = function(pathPiece){ + if(pathPiece && pathPiece !== ''){ + stitchPieces += pathPiece + '/' + wfs.stat(stitchPieces, function(error, stats) { + if(error){ + reply = { + status : error.status, + msg : lang.WebdavErrorTextTryCreatingDir, + dir : stitchPieces, + } + s.userLog(e,{type:lang['Webdav Error'],msg:reply}) + wfs.mkdir(stitchPieces, function(error) { + if(error){ + reply = { + status : error.status, + msg : lang.WebdavErrorTextCreatingDir, + dir : stitchPieces, + } + s.userLog(e,{type:lang['Webdav Error'],msg:reply}) + }else{ + lastParentCheck() + } + }) + }else{ + lastParentCheck() + } + }) + }else{ + ++parentPoint + } + } + checkPathPiece(webDavParents[0]) + }else{ + startWebDavUpload() + } + } + } + //webdav + s.addCloudUploader({ + name: 'webdav', + loadGroupAppExtender: loadWebDavForUser, + unloadGroupAppExtender: unloadWebDavForUser, + insertCompletedVideoExtender: uploadVideoToWebDav, + deleteVideoFromCloudExtensions: deleteVideoFromWebDav, + cloudDiskUseStartupExtensions: cloudDiskUseStartupForWebDav, + beforeAccountSave: beforeAccountSaveForWebDav, + onAccountSave: cloudDiskUseStartupForWebDav, + }) +} diff --git a/libs/user.js b/libs/user.js index 6c595ac..5b62758 100644 --- a/libs/user.js +++ b/libs/user.js @@ -15,7 +15,7 @@ module.exports = function(s,config){ if(s.group[e.ke].sizePurgeQueue.length > 0){ checkQueue() }else{ - s.group[e.ke].sizePurging=false + s.group[e.ke].sizePurging = false s.sendDiskUsedAmountToClients(e) } } @@ -116,6 +116,9 @@ module.exports = function(s,config){ s.tx({f:'log',ke:e.ke,mid:e.mid,log:x,time:s.timeObject()},'GRPLOG_'+e.ke); } s.loadGroup = function(e){ + s.loadGroupExtensions.forEach(function(extender){ + extender(e) + }) if(!s.group[e.ke]){ s.group[e.ke]={} } @@ -145,7 +148,7 @@ module.exports = function(s,config){ ar=JSON.parse(r.details); //load extenders s.loadGroupAppExtensions.forEach(function(extender){ - extender(r) + extender(r,ar) }) //disk Used Emitter if(!s.group[e.ke].diskUsedEmitter){ @@ -257,11 +260,11 @@ module.exports = function(s,config){ d.form.details.use_admin=d.d.use_admin d.form.details.use_ldap=d.d.use_ldap //check - if(d.d.edit_days=="0"){ - d.form.details.days=d.d.days; + if(d.d.edit_days == "0"){ + d.form.details.days = d.d.days; } - if(d.d.edit_size=="0"){ - d.form.details.size=d.d.size; + if(d.d.edit_size == "0"){ + d.form.details.size = d.d.size; } if(d.d.sub){ d.form.details.sub=d.d.sub; @@ -292,7 +295,7 @@ module.exports = function(s,config){ var userDetails = JSON.parse(d.form.details) s.group[d.ke].sizeLimit = parseFloat(newSize) s.onAccountSaveExtensions.forEach(function(extender){ - extender(s.group[d.ke],userDetails) + extender(s.group[d.ke],userDetails,user) }) s.unloadGroupAppExtensions.forEach(function(extender){ extender(user) @@ -305,4 +308,45 @@ module.exports = function(s,config){ } }) } + s.findPreset = function(presetQueryVals,callback){ + //presetQueryVals = [ke, type, name] + s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",presetQueryVals,function(err,presets){ + var preset + var notFound = false + if(presets && presets[0]){ + preset = presets[0] + s.checkDetails(preset) + }else{ + notFound = true + } + callback(notFound,preset) + }) + } + s.checkUserPurgeLock = function(groupKey){ + var userGroup = s.group[groupKey] + if(s.group[groupKey].usedSpace > s.group[groupKey].sizeLimit){ + s.group[groupKey].sizePurgeQueue = [] + s.group[groupKey].sizePurging = false + s.systemLog(lang.sizePurgeLockedText + ' : ' + groupKey) + s.onStalePurgeLockExtensions.forEach(function(extender){ + extender(groupKey,s.group[groupKey].usedSpace,s.group[groupKey].sizeLimit) + }) + } + } + if(config.cron.deleteOverMax === true){ + s.checkForStalePurgeLocks = function(){ + var doCheck = function(){ + Object.keys(s.group).forEach(function(groupKey){ + s.checkUserPurgeLock(groupKey) + }) + } + clearTimeout(s.checkForStalePurgeLocksInterval) + s.checkForStalePurgeLocksInterval = setInterval(function(){ + doCheck() + },1000 * 60 * 60) + doCheck() + } + }else{ + s.checkForStalePurgeLocks = function(){} + } } diff --git a/libs/videos.js b/libs/videos.js index 4b54c34..dfbb5c0 100644 --- a/libs/videos.js +++ b/libs/videos.js @@ -230,11 +230,6 @@ module.exports = function(s,config,lang){ }) }) }) - fs.chmod(videoSnap,0o777,function(err){ - if(!err){ - fs.unlink(videoSnap,function(err){}) - } - }) }else{ console.log(new Error()) console.log(lang['Database row does not exist'],queryValues) @@ -242,55 +237,60 @@ module.exports = function(s,config,lang){ }) } s.deleteListOfVideos = function(videos){ - var query = 'DELETE FROM Videos WHERE ' - var videoQuery = [] - var queryValues = [] - videos.forEach(function(video){ - s.checkDetails(video) - //e = video object - video.dir = s.getVideoDirectory(video) - if(!video.filename && video.time){ - video.filename = s.formattedTime(video.time) - } - var filename, - time - if(video.filename.indexOf('.')>-1){ - filename = video.filename - }else{ - filename = video.filename+'.'+video.ext - } - if(video.filename && !video.time){ - time = s.nameToTime(filename) - }else{ - time = video.time - } - time = new Date(time) - fs.chmod(video.dir+filename,0o777,function(err){ - s.tx({ - f: 'video_delete', - filename: filename, - mid: video.id, - ke: video.ke, - time: s.nameToTime(filename), - end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss') - },'GRP_'+video.ke); - s.setDiskUsedForGroup(video,-(video.size / 1000000)) - fs.unlink(video.dir+filename,function(err){ - fs.stat(video.dir+filename,function(err){ - if(!err){ - s.file('delete',video.dir+filename) - } + var deleteSetOfVideos = function(videos){ + var query = 'DELETE FROM Videos WHERE ' + var videoQuery = [] + var queryValues = [] + videos.forEach(function(video){ + s.checkDetails(video) + //e = video object + video.dir = s.getVideoDirectory(video) + if(!video.filename && video.time){ + video.filename = s.formattedTime(video.time) + } + var filename, + time + if(video.filename.indexOf('.')>-1){ + filename = video.filename + }else{ + filename = video.filename+'.'+video.ext + } + if(video.filename && !video.time){ + time = s.nameToTime(filename) + }else{ + time = video.time + } + time = new Date(time) + fs.chmod(video.dir+filename,0o777,function(err){ + s.tx({ + f: 'video_delete', + filename: filename, + mid: video.id, + ke: video.ke, + time: s.nameToTime(filename), + end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss') + },'GRP_'+video.ke); + s.setDiskUsedForGroup(video,-(video.size / 1000000)) + fs.unlink(video.dir+filename,function(err){ + fs.stat(video.dir+filename,function(err){ + if(!err){ + s.file('delete',video.dir+filename) + } + }) }) }) + videoQuery.push('(`mid`=? AND `ke`=? AND `time`=?)') + queryValues = queryValues.concat([video.id,video.ke,time]) }) - videoQuery.push('(`mid`=? AND `ke`=? AND `time`=?)') - queryValues = queryValues.concat([video.id,video.ke,time]) - }) - query += videoQuery.join(' OR ') - s.sqlQuery(query,queryValues,function(err){ - if(err){ - s.systemLog(lang['List of Videos Delete Error'],err) - } + query += videoQuery.join(' OR ') + s.sqlQuery(query,queryValues,function(err){ + if(err){ + s.systemLog(lang['List of Videos Delete Error'],err) + } + }) + } + videos.chunk(100).forEach(function(videosChunk){ + deleteSetOfVideos(videosChunk) }) } s.deleteVideoFromCloudExtensions = {} @@ -378,4 +378,40 @@ module.exports = function(s,config,lang){ finish() } } + s.streamMp4FileOverHttp = function(filePath,req,res){ + var ext = filePath.split('.') + ext = filePath[filePath.length - 1] + var total = fs.statSync(filePath).size; + if (req.headers['range']) { + try{ + var range = req.headers.range; + var parts = range.replace(/bytes=/, "").split("-"); + var partialstart = parts[0]; + var partialend = parts[1]; + var start = parseInt(partialstart, 10); + var end = partialend ? parseInt(partialend, 10) : total-1; + var chunksize = (end-start)+1; + var file = fs.createReadStream(filePath, {start: start, end: end}); + req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext } + req.writeCode=206 + }catch(err){ + req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext}; + var file = fs.createReadStream(filePath) + req.writeCode=200 + } + } else { + req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext}; + var file = fs.createReadStream(filePath) + req.writeCode=200 + } + if(req.query.downloadName){ + req.headerWrite['content-disposition']='attachment; filename="'+req.query.downloadName+'"'; + } + res.writeHead(req.writeCode,req.headerWrite); + file.on('close',function(){ + res.end() + }) + file.pipe(res) + return file + } } diff --git a/libs/webServer.js b/libs/webServer.js index 9cc5363..03dc198 100644 --- a/libs/webServer.js +++ b/libs/webServer.js @@ -50,6 +50,8 @@ module.exports = function(s,config,lang,io){ if(config.renderPaths.grid === undefined){config.renderPaths.grid='pages/grid'} //slick.js (cycle) page if(config.renderPaths.cycle === undefined){config.renderPaths.cycle='pages/cycle'} + // Use uws/cws + if(config.useUWebsocketJs === undefined){config.useUWebsocketJs=true} //SSL options if(config.ssl&&config.ssl.key&&config.ssl.cert){ config.ssl.key=fs.readFileSync(s.checkRelativePath(config.ssl.key),'utf8') @@ -111,5 +113,11 @@ module.exports = function(s,config,lang,io){ path:s.checkCorrectPathEnding(config.webPaths.super)+'socket.io', transports: ['websocket'] }) + if(config.useUWebsocketJs === true){ + io.engine.ws = new (require('cws').Server)({ + noServer: true, + perMessageDeflate: false + }) + } return app } diff --git a/libs/webServerAdminPaths.js b/libs/webServerAdminPaths.js index 7d4809b..b28cfba 100644 --- a/libs/webServerAdminPaths.js +++ b/libs/webServerAdminPaths.js @@ -21,10 +21,10 @@ module.exports = function(s,config,lang,app){ return } var form = s.getPostData(req) - var uid = s.getPostData(req,'uid',false) - var mail = s.getPostData(req,'mail',false) + var uid = form.uid || s.getPostData(req,'uid',false) + var mail = form.mail || s.getPostData(req,'mail',false) if(form){ - var keys = Object.keys(form) + var keys = ['details'] var condition = [] var value = [] keys.forEach(function(v){ @@ -68,8 +68,9 @@ module.exports = function(s,config,lang,app){ s.closeJsonResponse(res,endData) return } - var uid = s.getPostData(req,'uid',false) - var mail = s.getPostData(req,'mail',false) + var form = s.getPostData(req) + var uid = form.uid || s.getPostData(req,'uid',false) + var mail = form.mail || s.getPostData(req,'mail',false) s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[uid,req.params.ke,mail]) s.sqlQuery("SELECT * FROM API WHERE ke=? AND uid=?",[req.params.ke,uid],function(err,rows){ if(rows && rows[0]){ @@ -132,6 +133,12 @@ module.exports = function(s,config,lang,app){ uid: newId, mail: form.mail },'ADM_'+req.params.ke) + endData.user = { + details: s.parseJSON(details), + ke: req.params.ke, + uid: newId, + mail: form.mail + } } res.end(s.prettyPrint(endData)) }) @@ -159,7 +166,6 @@ module.exports = function(s,config,lang,app){ ok: false } res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var hasRestrictions = user.details.sub && user.details.allmonitors !== '1' if(req.params.f !== 'delete'){ @@ -238,7 +244,6 @@ module.exports = function(s,config,lang,app){ ],function (req,res){ var endData = {ok:false} res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var endData = { ok : false @@ -266,6 +271,7 @@ module.exports = function(s,config,lang,app){ },'GRP_' + req.params.ke) endData.ok = true } + endData.api = insert s.closeJsonResponse(res,endData) }) }else{ @@ -283,7 +289,6 @@ module.exports = function(s,config,lang,app){ ],function (req,res){ var endData = {ok:false} res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var endData = { ok : false @@ -336,7 +341,6 @@ module.exports = function(s,config,lang,app){ ],function (req,res){ var endData = {ok:false} res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var endData = { ok : false @@ -366,7 +370,7 @@ module.exports = function(s,config,lang,app){ /** * API : Administrator : Get Monitor State Presets List */ - app.get([ + app.all([ config.webPaths.apiPrefix+':auth/monitorStates/:ke', config.webPaths.adminApiPrefix+':auth/monitorStates/:ke' ],function (req,res){ @@ -411,19 +415,7 @@ module.exports = function(s,config,lang,app){ s.closeJsonResponse(res,endData) return } - var findPreset = function(callback){ - s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",[req.params.ke,'monitorStates',req.params.stateName],function(err,presets){ - var preset - var notFound = false - if(presets && presets[0]){ - preset = presets[0] - s.checkDetails(preset) - }else{ - notFound = true - } - callback(notFound,preset) - }) - } + var presetQueryVals = [req.params.ke,'monitorStates',req.params.stateName] switch(req.params.action){ case'insert':case'edit': var form = s.getPostData(req) @@ -433,7 +425,7 @@ module.exports = function(s,config,lang,app){ s.closeJsonResponse(res,endData) return } - findPreset(function(notFound,preset){ + s.findPreset(presetQueryVals,function(notFound,preset){ if(notFound === true){ endData.msg = lang["Inserted State Configuration"] var details = { @@ -470,7 +462,7 @@ module.exports = function(s,config,lang,app){ }) break; case'delete': - findPreset(function(notFound,preset){ + s.findPreset(presetQueryVals,function(notFound,preset){ if(notFound === true){ endData.msg = user.lang['State Configuration Not Found'] s.closeJsonResponse(res,endData) @@ -486,43 +478,8 @@ module.exports = function(s,config,lang,app){ }) break; default://change monitors according to state - findPreset(function(notFound,preset){ - if(notFound === false){ - var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND ' - var monitorQuery = [] - var sqlQueryValues = [req.params.ke] - var monitorPresets = {} - preset.details.monitors.forEach(function(monitor){ - monitorQuery.push('mid=?') - sqlQueryValues.push(monitor.mid) - monitorPresets[monitor.mid] = monitor - }) - sqlQuery += '('+monitorQuery.join(' OR ')+')' - s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){ - if(monitors && monitors[0]){ - monitors.forEach(function(monitor){ - s.checkDetails(monitor) - s.checkDetails(monitorPresets[monitor.mid]) - var monitorPreset = monitorPresets[monitor.mid] - monitorPreset.details = Object.assign(monitor.details,monitorPreset.details) - monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset)) - monitor.details = JSON.stringify(monitor.details) - s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){ - - },user) - }) - endData.ok = true - s.tx({f:'change_group_state',ke:req.params.ke,name:req.params.stateName},'GRP_'+req.params.ke) - s.closeJsonResponse(res,endData) - }else{ - endData.msg = user.lang['State Configuration has no monitors associated'] - s.closeJsonResponse(res,endData) - } - }) - }else{ - endData.msg = user.lang['State Configuration Not Found'] - s.closeJsonResponse(res,endData) - } + s.activateMonitorStates(req.params.ke,req.params.stateName,user,function(endData){ + s.closeJsonResponse(res,endData) }) break; } diff --git a/libs/webServerPaths.js b/libs/webServerPaths.js index 77b0300..9609638 100644 --- a/libs/webServerPaths.js +++ b/libs/webServerPaths.js @@ -8,6 +8,7 @@ var execSync = require('child_process').execSync; var exec = require('child_process').exec; var spawn = require('child_process').spawn; var httpProxy = require('http-proxy'); +var onvif = require('node-onvif'); var proxy = httpProxy.createProxyServer({}) var ejs = require('ejs'); var CircularJSON = require('circular-json'); @@ -67,6 +68,10 @@ module.exports = function(s,config,lang,app,io){ app.use(s.checkCorrectPathEnding(config.webPaths.super)+'libs',express.static(s.mainDirectory + '/web/libs')) app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: true})); + app.use(function (req,res,next){ + res.header("Access-Control-Allow-Origin",req.headers.origin); + next() + }) app.set('views', s.mainDirectory + '/web'); app.set('view engine','ejs'); //add template handler @@ -125,7 +130,6 @@ module.exports = function(s,config,lang,app,io){ app.get(config.webPaths.apiPrefix+':auth/userInfo/:ke',function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ req.ret.ok=true req.ret.user=user @@ -151,9 +155,6 @@ module.exports = function(s,config,lang,app,io){ s.checkCorrectPathEnding(config.webPaths.super)+':screen', ],function (req,res){ req.ip = s.getClientIp(req) - if(req.query.json === 'true'){ - res.header("Access-Control-Allow-Origin",req.headers.origin); - } var screenChooser = function(screen){ var search = function(screen){ if(req.url.indexOf(screen) > -1){ @@ -181,7 +182,7 @@ module.exports = function(s,config,lang,app,io){ s.renderPage(req,res,config.renderPaths.index,{ failedLogin: true, message: lang.failedLoginText1, - lang: lang, + lang: s.copySystemDefaultLanguage(), config: config, screen: screenChooser(req.params.screen) },function(err,html){ @@ -239,7 +240,7 @@ module.exports = function(s,config,lang,app,io){ s.renderPage(req,res,config.renderPaths.index,{ failedLogin: true, message: lang.failedLoginText2, - lang: lang, + lang: s.copySystemDefaultLanguage(), config: config, screen: screenChooser(req.params.screen) },function(err,html){ @@ -286,7 +287,8 @@ module.exports = function(s,config,lang,app,io){ // config: config, $user: req.resp, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) break; @@ -297,7 +299,8 @@ module.exports = function(s,config,lang,app,io){ // config: config, $user: req.resp, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) break; @@ -311,17 +314,36 @@ module.exports = function(s,config,lang,app,io){ $subs: rr, $mons: rrr, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) }) }else{ //not admin user - renderPage(config.renderPaths.home,{$user:req.resp,config:config,lang:r.lang,define:s.getDefinitonFile(r.details.lang),addStorage:s.dir.addStorage,fs:fs,__dirname:s.mainDirectory}); + renderPage(config.renderPaths.home,{ + $user:req.resp, + config:config, + lang:r.lang, + define:s.getDefinitonFile(r.details.lang), + addStorage:s.dir.addStorage, + fs:fs, + __dirname:s.mainDirectory, + customAutoLoad: s.customAutoLoadTree + }); } break; default: - renderPage(config.renderPaths.home,{$user:req.resp,config:config,lang:r.lang,define:s.getDefinitonFile(r.details.lang),addStorage:s.dir.addStorage,fs:fs,__dirname:s.mainDirectory}); + renderPage(config.renderPaths.home,{ + $user:req.resp, + config:config, + lang:r.lang, + define:s.getDefinitonFile(r.details.lang), + addStorage:s.dir.addStorage, + fs:fs, + __dirname:s.mainDirectory, + customAutoLoad: s.customAutoLoadTree + }); break; } s.userLog({ke:r.ke,mid:'$USER'},{type:r.lang['New Authentication Token'],msg:{for:req.body.function,mail:r.mail,id:r.uid,ip:req.ip}}) @@ -511,6 +533,7 @@ module.exports = function(s,config,lang,app,io){ r=[] } data.Logs = r + data.customAutoLoad = s.customAutoLoadTree fs.readFile(s.location.config,'utf8',function(err,file){ data.plainConfig = JSON.parse(file) renderPage(config.renderPaths.super,data) @@ -558,7 +581,6 @@ module.exports = function(s,config,lang,app,io){ * API : Brute Protection Lock Reset by API */ app.get([config.webPaths.apiPrefix+':auth/resetBruteProtection/:ke'], function (req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(s.failedLoginAttempts[user.mail]){ clearTimeout(s.failedLoginAttempts[user.mail].timeout) @@ -576,7 +598,6 @@ module.exports = function(s,config,lang,app,io){ config.webPaths.apiPrefix+':auth/cycle/:ke', config.webPaths.apiPrefix+':auth/cycle/:ke/:group' ], function(req,res) { - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(user.permissions.get_monitors==="0"){ res.end(user.lang['Not Permitted']) @@ -705,7 +726,6 @@ module.exports = function(s,config,lang,app,io){ }else{ res.setHeader('Content-Type', 'application/json'); } - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ if(user.permissions.get_monitors==="0"){ res.end(s.prettyPrint([])) @@ -821,7 +841,6 @@ module.exports = function(s,config,lang,app,io){ app.get([config.webPaths.apiPrefix+':auth/monitor/:ke',config.webPaths.apiPrefix+':auth/monitor/:ke/:id'], function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ if(user.permissions.get_monitors==="0"){ res.end(s.prettyPrint([])) @@ -903,6 +922,54 @@ module.exports = function(s,config,lang,app,io){ s.auth(req.params,req.fn,res,req); }); /** + * API : Merge Recorded Videos into one file + */ + app.get(config.webPaths.apiPrefix+':auth/videosMerge/:ke', function (req,res){ + var failed = function(resp){ + res.setHeader('Content-Type', 'application/json'); + res.end(s.prettyPrint(resp)) + } + if(req.query.videos && req.query.videos !== ''){ + s.auth(req.params,function(user){ + var videosSelected = JSON.parse(req.query.videos) + var where = [] + var values = [] + videosSelected.forEach(function(video){ + where.push("(ke=? AND mid=? AND `time`=?)") + if(!video.ke)video.ke = req.params.ke + values.push(video.ke) + values.push(video.mid) + var time = s.nameToTime(video.filename) + if(req.query.isUTC === 'true'){ + time = s.utcToLocal(time) + } + time = new Date(time) + values.push(time) + }) + s.sqlQuery('SELECT * FROM Videos WHERE '+where.join(' OR '),values,function(err,r){ + var resp = {ok: false} + if(r && r[0]){ + s.mergeRecordedVideos(r,req.params.ke,function(fullPath,filename){ + res.setHeader('Content-Disposition', 'attachment; filename="'+filename+'"') + var file = fs.createReadStream(fullPath) + file.on('close',function(){ + setTimeout(function(){ + s.file('delete',fullPath) + },1000 * 60 * 3) + res.end() + }) + file.pipe(res) + }) + }else{ + failed({ok:false,msg:'No Videos Found'}) + } + }) + },res,req); + }else{ + failed({ok:false,msg:'"videos" query variable is missing from request.'}) + } + }) + /** * API : Get Videos */ app.get([ @@ -912,7 +979,6 @@ module.exports = function(s,config,lang,app,io){ config.webPaths.apiPrefix+':auth/cloudVideos/:ke/:id' ], function (req,res){ res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var hasRestrictions = user.details.sub && user.details.allmonitors !== '1' if( @@ -1033,7 +1099,6 @@ module.exports = function(s,config,lang,app,io){ app.get([config.webPaths.apiPrefix+':auth/events/:ke',config.webPaths.apiPrefix+':auth/events/:ke/:id',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start/:end'], function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(user.permissions.watch_videos==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.video_view.indexOf(req.params.id)===-1){ res.end(s.prettyPrint([])) @@ -1091,7 +1156,6 @@ module.exports = function(s,config,lang,app,io){ app.get([config.webPaths.apiPrefix+':auth/logs/:ke',config.webPaths.apiPrefix+':auth/logs/:ke/:id'], function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(user.permissions.get_logs==="0" || user.details.sub && user.details.view_logs !== '1'){ res.end(s.prettyPrint([])) @@ -1156,7 +1220,6 @@ module.exports = function(s,config,lang,app,io){ app.get(config.webPaths.apiPrefix+':auth/smonitor/:ke', function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ if(user.permissions.get_monitors==="0"){ res.end(s.prettyPrint([])) @@ -1193,7 +1256,6 @@ module.exports = function(s,config,lang,app,io){ app.get([config.webPaths.apiPrefix+':auth/monitor/:ke/:id/:f',config.webPaths.apiPrefix+':auth/monitor/:ke/:id/:f/:ff',config.webPaths.apiPrefix+':auth/monitor/:ke/:id/:f/:ff/:fff'], function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(user.permissions.control_monitors==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitor_edit.indexOf(req.params.id)===-1){ res.end(user.lang['Not Permitted']) @@ -1288,7 +1350,6 @@ module.exports = function(s,config,lang,app,io){ */ app.get([config.webPaths.apiPrefix+':auth/fileBin/:ke',config.webPaths.apiPrefix+':auth/fileBin/:ke/:id'],function (req,res){ res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ req.sql='SELECT * FROM Files WHERE ke=?';req.ar=[req.params.ke]; if(user.details.sub&&user.details.monitors&&user.details.allmonitors!=='1'){ @@ -1321,7 +1382,6 @@ module.exports = function(s,config,lang,app,io){ * API : Get fileBin file */ app.get(config.webPaths.apiPrefix+':auth/fileBin/:ke/:id/:year/:month/:day/:file', function (req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ req.failed=function(){ res.end(user.lang['File Not Found']) @@ -1352,7 +1412,6 @@ module.exports = function(s,config,lang,app,io){ * API : Zip Videos and Get Link from fileBin */ app.get(config.webPaths.apiPrefix+':auth/zipVideos/:ke', function (req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); var failed = function(resp){ res.setHeader('Content-Type', 'application/json'); res.end(s.prettyPrint(resp)) @@ -1375,7 +1434,7 @@ module.exports = function(s,config,lang,app,io){ values.push(time) }) s.sqlQuery('SELECT * FROM Videos WHERE '+where.join(' OR '),values,function(err,r){ - var resp = {ok:false} + var resp = {ok: false} if(r && r[0]){ resp.ok = true var zipDownload = null @@ -1396,7 +1455,7 @@ module.exports = function(s,config,lang,app,io){ fs.mkdirSync(fileBinDir); } r.forEach(function(video){ - timeFormatted = s.formattedTime(video.time) + var timeFormatted = s.formattedTime(video.time) video.filename = timeFormatted+'.'+video.ext var dir = s.getVideoDirectory(video)+video.filename var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext @@ -1418,16 +1477,27 @@ module.exports = function(s,config,lang,app,io){ var zipDownload = fs.createReadStream(zippedFile) zipDownload.pipe(res) zipDownload.on('error', function (error) { - s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Download Error',msg:error.toString()}) + var errorString = error.toString() + s.userLog({ + ke: req.params.ke, + mid: '$USER' + },{ + title: 'Zip Download Error', + msg: errorString + }) if(zipDownload && zipDownload.destroy){ zipDownload.destroy() } - }); + res.end(s.prettyPrint({ + ok: false, + msg: errorString + })) + }) zipDownload.on('close', function () { res.end() - zipDownload.destroy(); - fs.unlinkSync(zippedFile); - }); + zipDownload.destroy() + fs.unlinkSync(zippedFile) + }) }) }else{ failed({ok:false,msg:'No Videos Found'}) @@ -1437,7 +1507,120 @@ module.exports = function(s,config,lang,app,io){ }else{ failed({ok:false,msg:'"videos" query variable is missing from request.'}) } - }); + }) + /** + * API : Zip Cloud Videos and Get Link from fileBin + */ + app.get(config.webPaths.apiPrefix+':auth/zipCloudVideos/:ke', function (req,res){ + var failed = function(resp){ + res.setHeader('Content-Type', 'application/json'); + res.end(s.prettyPrint(resp)) + } + if(req.query.videos && req.query.videos !== ''){ + s.auth(req.params,function(user){ + var videosSelected = JSON.parse(req.query.videos) + var where = [] + var values = [] + videosSelected.forEach(function(video){ + where.push("(ke=? AND mid=? AND `time`=?)") + if(!video.ke)video.ke = req.params.ke + values.push(video.ke) + values.push(video.mid) + var time = s.nameToTime(video.filename) + if(req.query.isUTC === 'true'){ + time = s.utcToLocal(time) + } + time = new Date(time) + values.push(time) + }) + s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE '+where.join(' OR '),values,function(err,r){ + var resp = {ok: false} + if(r && r[0]){ + resp.ok = true + var zipDownload = null + var tempFiles = [] + var fileId = s.gid() + var fileBinDir = s.dir.fileBin+req.params.ke+'/' + var tempScript = s.dir.streams+req.params.ke+'/'+fileId+'.sh' + var zippedFilename = s.formattedTime()+'-'+fileId+'-Shinobi_Cloud_Backed_Recordings.zip' + var zippedFile = fileBinDir+zippedFilename + var script = 'cd '+fileBinDir+' && zip -9 -r '+zippedFile + res.on('close', () => { + if(zipDownload && zipDownload.destroy){ + zipDownload.destroy() + } + fs.unlink(zippedFile); + }) + if(!fs.existsSync(fileBinDir)){ + fs.mkdirSync(fileBinDir); + } + var cloudDownloadCount = 0 + var getFile = function(video,completed){ + if(!video)completed(); + s.checkDetails(video) + var filename = video.href.split('/') + filename = filename[filename.length - 1] + var timeFormatted = s.formattedTime(video.time) + var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename + var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile) + tempFileWriteStream.on('finish', function() { + ++cloudDownloadCount + getFile(r[cloudDownloadCount],completed) + }) + var cloudVideoDownload = request(video.href) + cloudVideoDownload.on('response', function (res) { + res.pipe(tempFileWriteStream) + }) + tempFiles.push(fileBinDir+tempVideoFile) + script += ' "'+tempVideoFile+'"' + } + getFile(r[cloudDownloadCount],function(){ + fs.writeFileSync(tempScript,script,'utf8') + var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true}) + zipCreate.stderr.on('data',function(data){ + s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()}) + }) + zipCreate.on('exit',function(data){ + fs.unlinkSync(tempScript) + tempFiles.forEach(function(file){ + fs.unlink(file,function(){}) + }) + res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"') + var zipDownload = fs.createReadStream(zippedFile) + zipDownload.pipe(res) + zipDownload.on('error', function (error) { + var errorString = error.toString() + s.userLog({ + ke: req.params.ke, + mid: '$USER' + },{ + title: 'Zip Download Error', + msg: errorString + }) + if(zipDownload && zipDownload.destroy){ + zipDownload.destroy() + } + res.end(s.prettyPrint({ + ok: false, + msg: errorString + })) + }) + zipDownload.on('close', function () { + res.end() + zipDownload.destroy() + fs.unlinkSync(zippedFile) + }) + }) + }) + }else{ + failed({ok:false,msg:'No Videos Found'}) + } + }) + },res,req); + }else{ + failed({ok:false,msg:'"videos" query variable is missing from request.'}) + } + }) /** * API : Get Cloud Video File (proxy) */ @@ -1480,38 +1663,7 @@ module.exports = function(s,config,lang,app,io){ if(r&&r[0]){ req.dir=s.getVideoDirectory(r[0])+req.params.file if (fs.existsSync(req.dir)){ - req.ext=req.params.file.split('.')[1]; - var total = fs.statSync(req.dir).size; - if (req.headers['range']) { - try{ - var range = req.headers.range; - var parts = range.replace(/bytes=/, "").split("-"); - var partialstart = parts[0]; - var partialend = parts[1]; - var start = parseInt(partialstart, 10); - var end = partialend ? parseInt(partialend, 10) : total-1; - var chunksize = (end-start)+1; - var file = fs.createReadStream(req.dir, {start: start, end: end}); - req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext } - req.writeCode=206 - }catch(err){ - req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext}; - var file = fs.createReadStream(req.dir) - req.writeCode=200 - } - } else { - req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext}; - var file=fs.createReadStream(req.dir) - req.writeCode=200 - } - if(req.query.downloadName){ - req.headerWrite['content-disposition']='attachment; filename="'+req.query.downloadName+'"'; - } - res.writeHead(req.writeCode,req.headerWrite); - file.on('close',function(){ - res.end(); - }) - file.pipe(res); + s.streamMp4FileOverHttp(req.dir,req,res) }else{ res.end(user.lang['File Not Found in Filesystem']) } @@ -1524,27 +1676,34 @@ module.exports = function(s,config,lang,app,io){ /** * API : Motion Trigger via GET request */ - app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){ - s.auth(req.params,function(user){ - if(req.query.data){ - try{ - var d={id:req.params.id,ke:req.params.ke,details:JSON.parse(req.query.data)}; - }catch(err){ - res.end('Data Broken',err); - return; - } - }else{ - res.end('No Data'); - return; - } - if(!d.ke||!d.id||!s.group[d.ke]){ - res.end(user.lang['No Group with this key exists']); - return; - } - s.triggerEvent(d) - res.end(user.lang['Trigger Successful']) - },res,req); - }) + app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){ + s.auth(req.params,function(user){ + var endData = { + + } + if(req.query.data){ + try{ + var d = { + id: req.params.id, + ke: req.params.ke, + details: JSON.parse(req.query.data) + } + }catch(err){ + res.end('Data Broken',err) + return + } + }else{ + res.end('No Data') + return + } + if(!d.ke||!d.id||!s.group[d.ke]){ + res.end(user.lang['No Group with this key exists']) + return + } + s.triggerEvent(d) + res.end(user.lang['Trigger Successful']) + },res,req) + }) /** * API : WebHook Tester */ @@ -1560,7 +1719,6 @@ module.exports = function(s,config,lang,app,io){ */ app.get(config.webPaths.apiPrefix+':auth/control/:ke/:id/:direction', function (req,res){ res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ s.cameraControl(req.params,function(resp){ res.end(s.prettyPrint(resp)) @@ -1578,7 +1736,6 @@ module.exports = function(s,config,lang,app,io){ ], function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ if(user.permissions.watch_videos==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.video_delete.indexOf(req.params.id)===-1){ res.end(user.lang['Not Permitted']) @@ -1673,7 +1830,6 @@ module.exports = function(s,config,lang,app,io){ app.get(config.webPaths.apiPrefix+':auth/probe/:ke',function (req,res){ req.ret={ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ switch(req.query.action){ // case'stop': @@ -1720,7 +1876,6 @@ module.exports = function(s,config,lang,app,io){ app.all([config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'],function (req,res){ var response = {ok:false}; res.setHeader('Content-Type', 'application/json'); - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ var errorMessage = function(msg,error){ response.ok = false @@ -1865,4 +2020,13 @@ module.exports = function(s,config,lang,app,io){ s.closeJsonResponse(res,endData) },res,req) }) + /** + * Robots.txt + */ + app.get('/robots.txt', function (req,res){ + res.on('finish',function(){ + res.end() + }) + fs.createReadStream(s.mainDirectory + '/web/pages/robots.txt').pipe(res) + }) } diff --git a/libs/webServerStreamPaths.js b/libs/webServerStreamPaths.js index ad5e490..fea6779 100644 --- a/libs/webServerStreamPaths.js +++ b/libs/webServerStreamPaths.js @@ -16,7 +16,6 @@ module.exports = function(s,config,lang,app){ * Page : Get Embed Stream */ app.get([config.webPaths.apiPrefix+':auth/embed/:ke/:id',config.webPaths.apiPrefix+':auth/embed/:ke/:id/:addon'], function (req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); req.params.protocol=req.protocol; s.auth(req.params,function(user){ if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){ @@ -100,7 +99,6 @@ module.exports = function(s,config,lang,app){ * @param {string} full - if `true` page will load the MJPEG iframe page */ app.get([config.webPaths.apiPrefix+':auth/mjpeg/:ke/:id',config.webPaths.apiPrefix+':auth/mjpeg/:ke/:id/:channel'], function(req,res) { - res.header("Access-Control-Allow-Origin",req.headers.origin); if(req.query.full=='true'){ s.renderPage(req,res,config.renderPaths.mjpeg,{url:config.webPaths.apiPrefix + req.params.auth+'/mjpeg/'+req.params.ke+'/'+req.params.id,originalURL:s.getOriginalUrl(req)}); res.end() @@ -163,7 +161,6 @@ module.exports = function(s,config,lang,app){ * API : Get HLS Stream */ app.get([config.webPaths.apiPrefix+':auth/hls/:ke/:id/:file',config.webPaths.apiPrefix+':auth/hls/:ke/:id/:channel/:file'], function (req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); req.fn=function(user){ s.checkChildProxy(req.params,function(){ req.dir=s.dir.streams+req.params.ke+'/'+req.params.id+'/' @@ -186,7 +183,6 @@ module.exports = function(s,config,lang,app){ * API : Get JPEG Snapshot */ app.get(config.webPaths.apiPrefix+':auth/jpeg/:ke/:id/s.jpg', function(req,res){ - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ s.checkChildProxy(req.params,function(){ if(user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors&&user.details.monitors.indexOf(req.params.id)===-1){ @@ -212,7 +208,6 @@ module.exports = function(s,config,lang,app){ * API : Get FLV Stream */ app.get([config.webPaths.apiPrefix+':auth/flv/:ke/:id/s.flv',config.webPaths.apiPrefix+':auth/flv/:ke/:id/:channel/s.flv'], function(req,res) { - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ s.checkChildProxy(req.params,function(){ var Emitter,chunkChannel @@ -263,7 +258,6 @@ module.exports = function(s,config,lang,app){ * API : Get H.265/h265 HEVC stream */ app.get([config.webPaths.apiPrefix+':auth/h265/:ke/:id/s.hevc',config.webPaths.apiPrefix+':auth/h265/:ke/:id/:channel/s.hevc'], function(req,res) { - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ s.checkChildProxy(req.params,function(){ var Emitter,chunkChannel @@ -313,7 +307,6 @@ module.exports = function(s,config,lang,app){ config.webPaths.apiPrefix+':auth/h264/:ke/:id/:feed', config.webPaths.apiPrefix+':auth/h264/:ke/:id' ], function (req, res) { - res.header("Access-Control-Allow-Origin",req.headers.origin); s.auth(req.params,function(user){ s.checkChildProxy(req.params,function(){ if(!req.query.feed){req.query.feed='1'} diff --git a/libs/webServerSuperPaths.js b/libs/webServerSuperPaths.js index 9c4543a..7a25a6b 100644 --- a/libs/webServerSuperPaths.js +++ b/libs/webServerSuperPaths.js @@ -288,6 +288,7 @@ module.exports = function(s,config,lang,app){ ] ) s.tx({f:'add_account',details:form.details,ke:form.ke,uid:form.uid,mail:form.mail},'$') + endData.user = Object.assign(form,{}) //init user s.loadGroup(form) } @@ -324,7 +325,7 @@ module.exports = function(s,config,lang,app){ r = r[0] var details = JSON.parse(r.details) if(form.pass && form.pass !== ''){ - if(form.pass === form.password_again){ + if(form.pass === form.password_again || form.pass_again){ form.pass = s.createHash(form.pass); }else{ endData.msg = lang["Passwords Don't Match"] @@ -335,11 +336,18 @@ module.exports = function(s,config,lang,app){ delete(form.pass); } delete(form.password_again); + delete(form.pass_again); var keys = Object.keys(form) var set = [] var values = [] keys.forEach(function(v,n){ - if(set==='ke'||set==='password_again'||!form[v]){return} + if( + set === 'ke' || + !form[v] + ){ + //skip + return + } set.push(v+'=?') if(v === 'details'){ form[v] = s.stringJSON(Object.assign(details,s.parseJSON(form[v]))) @@ -360,6 +368,9 @@ module.exports = function(s,config,lang,app){ } close() }) + }else{ + endData.msg = lang['User Not Found'] + close() } }) }else{ @@ -409,53 +420,218 @@ module.exports = function(s,config,lang,app){ },res,req) }) /** - * API : Superuser : Export Entire System + * API : Superuser : Get Entire System */ app.all(config.webPaths.superApiPrefix+':auth/export/system', function (req,res){ + s.superAuth(req.params,function(resp){ + s.systemLog('Copy of the Database Exported',{ + by: resp.$user.mail, + ip: resp.ip + }) + var endData = { + ok : true + } + // var database = s.getPostData(req,'database') + endData.database = {} + var tableNames = [ + 'Users', + 'Monitors', + 'API', + 'Videos', + 'Cloud Videos', + 'Logs', + 'Files', + 'Presets', + ] + var completedTables = 0 + var tableExportLoop = function(callback){ + var tableName = tableNames[completedTables] + if(tableName){ + var tableIsSelected = s.getPostData(req,tableName) == 1 + if(tableIsSelected){ + s.sqlQuery('SELECT * FROM `' + tableName +'`',[],function(err,dataRows){ + endData.database[tableName] = dataRows + ++completedTables + tableExportLoop(callback) + }) + }else{ + ++completedTables + tableExportLoop(callback) + } + }else{ + callback() + } + } + tableExportLoop(function(){ + s.closeJsonResponse(res,endData) + }) + },res,req) + }) + /** + * API : Superuser : Import Entire System + */ + app.all(config.webPaths.superApiPrefix+':auth/import/system', function (req,res){ + s.superAuth(req.params,function(resp){ + var endData = { + ok : false + } + console.log(req.files) + // insert data + var data = s.getPostData(req) + var database = s.getPostData(req,'database') + if(data && data.database)database = data.database + if(database){ + var rowsExistingAlready = {} + var countOfRowsInserted = {} + var countOfRowsExistingAlready = {} + var insertRow = function(tableName,row,callback){ + if(!rowsExistingAlready[tableName])rowsExistingAlready[tableName] = [] + if(!countOfRowsExistingAlready[tableName])countOfRowsExistingAlready[tableName] = 0 + if(!countOfRowsInserted[tableName])countOfRowsInserted[tableName] = 0 + var fieldsToCheck = ['ke'] + switch(tableName){ + case'API': + fieldsToCheck = fieldsToCheck.concat([ + 'code', + 'uid' + ]) + break; + case'Cloud Videos': + fieldsToCheck = fieldsToCheck.concat([ + 'href', + 'mid' + ]) + break; + case'Videos': + fieldsToCheck = fieldsToCheck.concat([ + 'time', + 'mid' + ]) + break; + case'Users': + fieldsToCheck = fieldsToCheck.concat([ + 'uid', + 'mail' + ]) + break; + case'Presets': + fieldsToCheck = fieldsToCheck.concat([ + 'name', + 'type' + ]) + break; + case'Logs': + fieldsToCheck = fieldsToCheck.concat([ + 'time', + 'info', + 'mid' + ]) + break; + case'Events': + fieldsToCheck = fieldsToCheck.concat([ + 'time', + 'details', + 'mid' + ]) + break; + case'Files': + fieldsToCheck = fieldsToCheck.concat([ + 'details', + 'name', + 'mid' + ]) + break; + case'Monitors': + fieldsToCheck = fieldsToCheck.concat([ + 'host', + 'protocol', + 'port', + 'path', + 'mid' + ]) + break; + } + var keysToCheck = [] + var valuesToCheck = [] + fieldsToCheck.forEach(function(key){ + keysToCheck.push(key + '= ?') + valuesToCheck.push(row[key]) + }) + s.sqlQuery('SELECT * FROM ' + tableName + ' WHERE ' + keysToCheck.join(' AND '),valuesToCheck,function(err,selected){ + if(selected && selected[0]){ + selected = selected[0] + rowsExistingAlready[tableName].push(selected) + callback() + }else{ + var rowKeys = Object.keys(row) + var insertEscapes = [] + var insertValues = [] + rowKeys.forEach(function(key){ + insertEscapes.push('?') + insertValues.push(row[key]) + }) + s.sqlQuery('INSERT INTO ' + tableName + ' (' + rowKeys.join(',') +') VALUES (' + insertEscapes.join(',') + ')',insertValues,function(){ + if(!err){ + ++countOfRowsInserted[tableName] + } + callback() + }) + } + }) + } + var actionCount = {} + var insertTableRows = function(tableName,rows,callback){ + if(!actionCount[tableName])actionCount[tableName] = 0 + var insertLoop = function(){ + var row = rows[actionCount[tableName]] + if(row){ + insertRow(tableName,row,function(){ + ++actionCount[tableName] + insertLoop() + }) + }else{ + callback() + } + } + insertLoop() + } + var databaseTableKeys = Object.keys(database) + var completedTables = 0 + var tableInsertLoop = function(callback){ + var tableName = databaseTableKeys[completedTables] + var rows = database[databaseTableKeys[completedTables]] + if(tableName){ + insertTableRows(tableName,rows,function(){ + ++completedTables + tableInsertLoop(callback) + }) + }else{ + callback() + } + } + tableInsertLoop(function(){ + endData.ok = true + endData.tablesInsertedTo = databaseTableKeys + endData.countOfRowsInserted = countOfRowsInserted + endData.rowsExistingAlready = rowsExistingAlready + s.closeJsonResponse(res,endData) + }) + }else{ + endData.msg = lang['Database Not Found'] + s.closeJsonResponse(res,endData) + } + },res,req) + }) + /** + * API : Superuser : Force Check for Stale Purge Locks + */ + app.all(config.webPaths.superApiPrefix+':auth/system/checkForStalePurgeLocks', function (req,res){ s.superAuth(req.params,function(resp){ var endData = { ok : true } - var close = function(){ - res.end(s.prettyPrint(endData)) - } - var account = s.getPostData(req,'account') - s.sqlQuery('SELECT FROM Users',[],function(err,users){ - s.sqlQuery('SELECT FROM Monitors',[],function(err,monitors){ - s.sqlQuery('SELECT FROM API',[],function(err,monitors){ - s.sqlQuery('SELECT FROM Videos',[],function(err,monitors){ - s.sqlQuery('SELECT FROM Logs',[],function(err,monitors){ - - }) - }) - }) - }) - }) - s.sqlQuery('DELETE FROM API WHERE uid=? AND ke=?',[account.uid,account.ke]) - if(s.getPostData(req,'deleteSubAccounts',false) === '1'){ - s.sqlQuery('DELETE FROM Users WHERE ke=?',[account.ke]) - } - if(s.getPostData(req,'deleteMonitors',false) == '1'){ - s.sqlQuery('SELECT * FROM Monitors WHERE ke=?',[account.ke],function(err,monitors){ - if(monitors && monitors[0]){ - monitors.forEach(function(monitor){ - s.camera('stop',monitor) - }) - s.sqlQuery('DELETE FROM Monitors WHERE ke=?',[account.ke]) - } - }) - } - if(s.getPostData(req,'deleteVideos',false) == '1'){ - s.sqlQuery('DELETE FROM Videos WHERE ke=?',[account.ke]) - fs.chmod(s.dir.videos+account.ke,0o777,function(err){ - fs.unlink(s.dir.videos+account.ke,function(err){}) - }) - } - if(s.getPostData(req,'deleteEvents',false) == '1'){ - s.sqlQuery('DELETE FROM Events WHERE ke=?',[account.ke]) - } - s.tx({f:'delete_account',ke:account.ke,uid:account.uid,mail:account.mail},'$') - close() + s.checkForStalePurgeLocks() + res.end(s.prettyPrint(endData)) },res,req) }) } diff --git a/plugins/python-contour/.gitignore b/plugins/python-contour/.gitignore new file mode 100644 index 0000000..543f830 --- /dev/null +++ b/plugins/python-contour/.gitignore @@ -0,0 +1,3 @@ +conf.json +faces +data \ No newline at end of file diff --git a/plugins/python-contour/INSTALL.sh b/plugins/python-contour/INSTALL.sh new file mode 100644 index 0000000..da271df --- /dev/null +++ b/plugins/python-contour/INSTALL.sh @@ -0,0 +1,59 @@ +#!/bin/bash +echo "-----------------------------------------------" +echo "-- Installing Python Dlib Plugin for Shinobi --" +echo "-----------------------------------------------" +echo "-----------------------------------" +if [ ! -e "./conf.json" ]; then + echo "Creating conf.json" + sudo cp conf.sample.json conf.json +else + echo "conf.json already exists..." +fi +echo "-----------------------------------" +sudo apt update -y +echo "Installing python3" +sudo apt install python3 python3-dev python3-pip -y +echo "-----------------------------------" +sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y +sudo apt update +sudo apt-get install gcc-6 g++-6 -y && sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 +echo "-----------------------------------" +if ! [ -x "$(command -v nvidia-smi)" ]; then + echo "You need to install NVIDIA Drivers to use this." + echo "inside the Shinobi directory run the following :" + echo "sh INSTALL/cuda9-part1.sh" + exit 1 +else + echo "NVIDIA Drivers found..." + echo "$(nvidia-smi |grep 'Driver Version')" +fi +echo "-----------------------------------" +if [ ! -d "/usr/local/cuda" ]; then + echo "You need to install CUDA Toolkit to use this." + echo "inside the Shinobi directory run the following :" + echo "sh INSTALL/cuda9-part2-after-reboot.sh" + exit 1 +else + echo "CUDA Toolkit found..." +fi +echo "-----------------------------------" +if ! [ -x "$(command -v opencv_version)" ]; then + echo "You need to install OpenCV with CUDA first." + echo "inside the Shinobi directory run the following :" + echo "sh INSTALL/opencv-cuda.sh" + exit 1 +else + echo "OpenCV found... : $(opencv_version)" +fi +echo "-----------------------------------" +echo "Getting new pip..." +pip3 install --upgrade pip +pip install --user --upgrade pip +export PATH=/usr/local/cuda/bin:$PATH +echo "Smoking pips..." +pip3 install flask_socketio +pip3 install flask +pip3 install numpy +pip3 install gevent gevent-websocket +echo "Start the plugin with pm2 like so :" +echo "pm2 start shinobi-python-dlib.js" diff --git a/plugins/python-contour/README.md b/plugins/python-contour/README.md new file mode 100644 index 0000000..26c20f5 --- /dev/null +++ b/plugins/python-contour/README.md @@ -0,0 +1,72 @@ +# Python Contour Detection with OpenCV + +> This plugin requires the use of port `7990` by default. You can specify a different port by adding `pythonPort` to your plugin's conf.json. + +**Ubuntu and Debian only** + +Go to the Shinobi directory. **/home/Shinobi** is the default directory. + +``` +cd /home/Shinobi/plugins/python-contour +``` + +Copy the config file. + +``` +sh INSTALL.sh +``` + +Start the plugin. + +``` +pm2 start shinobi-python-contour.js +``` + +Doing this will reveal options in the monitor configuration. Shinobi does not need to be restarted when a plugin is initiated or stopped. + +## Run the plugin as a Host +> The main app (Shinobi) will be the client and the plugin will be the host. The purpose of allowing this method is so that you can use one plugin for multiple Shinobi instances. Allowing you to easily manage connections without starting multiple processes. + +Edit your plugins configuration file. Set the `hostPort` **to be different** than the `listening port for camera.js`. + +``` +nano conf.json +``` + +Here is a sample of a Host configuration for the plugin. + - `plug` is the name of the plugin corresponding in the main configuration file. + - `https` choose if you want to use SSL or not. Default is `false`. + - `hostPort` can be any available port number. **Don't make this the same port number as Shinobi.** Default is `8082`. + - `type` tells the main application (Shinobi) what kind of plugin it is. In this case it is a detector. + +``` +{ + "plug":"PythonContour", + "hostPort":8082, + "key":"SomeOpenALPRkeySoPeopleDontMessWithYourShinobi", + "mode":"host", + "type":"detector" +} +``` + +Now modify the **main configuration file** located in the main directory of Shinobi. *Where you currently should be.* + +``` +nano conf.json +``` + +Add the `plugins` array if you don't already have it. Add the following *object inside the array*. + +``` + "plugins":[ + { + "id" : "PythonContour", + "https" : false, + "host" : "localhost", + "port" : 8082, + "key" : "SomeOpenALPRkeySoPeopleDontMessWithYourShinobi", + "mode" : "host", + "type" : "detector" + } + ], +``` diff --git a/plugins/python-contour/bootPy.sh b/plugins/python-contour/bootPy.sh new file mode 100644 index 0000000..68be579 --- /dev/null +++ b/plugins/python-contour/bootPy.sh @@ -0,0 +1 @@ +python3 -u $@ \ No newline at end of file diff --git a/plugins/python-contour/conf.sample.json b/plugins/python-contour/conf.sample.json new file mode 100644 index 0000000..2687164 --- /dev/null +++ b/plugins/python-contour/conf.sample.json @@ -0,0 +1,10 @@ +{ + "plug":"PythonContour", + "host":"localhost", + "port":8080, + "pythonPort":7990, + "hostPort":8082, + "key":"YOUR_CONTOUR_PLUGIN_KEY", + "mode":"client", + "type":"detector" +} diff --git a/plugins/python-contour/package.json b/plugins/python-contour/package.json new file mode 100644 index 0000000..c4c96d9 --- /dev/null +++ b/plugins/python-contour/package.json @@ -0,0 +1,18 @@ +{ + "name": "shinobi-python-contour", + "version": "1.0.0", + "description": "Contour plugin for Shinobi that uses Python functions for detection.", + "main": "shinobi-python-contour.js", + "dependencies": { + "socket.io-client": "^1.7.4", + "express": "^4.16.2", + "moment": "^2.19.2", + "socket.io": "^2.0.4" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Moe Alam", + "license": "ISC" +} diff --git a/plugins/python-contour/pumpkin.py b/plugins/python-contour/pumpkin.py new file mode 100644 index 0000000..728c037 --- /dev/null +++ b/plugins/python-contour/pumpkin.py @@ -0,0 +1,116 @@ +from flask import Flask, request, jsonify, render_template +from flask_socketio import SocketIO, emit +import cv2 +import os +import json +import numpy as np +import sys + +dirname = sys.argv[1] +try: + with open("{}/conf.json".format(dirname)) as json_file: + config = json.load(json_file) + httpPort = config['pythonPort'] + try: + httpPort + except NameError: + httpPort = 7990 +except Exception as e: + print("conf.json not found.") + httpPort = 7990 + +# Load Flask +app = Flask("Contour Detection for Shinobi (Pumpkin Pie)") +socketio = SocketIO(app) +# Silence Flask +# import logging +# log = logging.getLogger('werkzeug') +# log.setLevel(logging.ERROR) + +#load car detector +oldFrames = {} + +fgbg = cv2.createBackgroundSubtractorMOG2() + +# detection function +def spark(filepath,trackerId): + try: + filepath + except NameError: + return "File path not found." + frame = cv2.imread(filepath) + returnData = [] + # resize the frame, convert it to grayscale, and blur it + # frame = imutils.resize(frame, width=500) + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + gray = cv2.GaussianBlur(gray, (21, 21), 0) + + # if the first frame is None, initialize it + global oldFrames + try: + oldFrames[trackerId] + except KeyError: + oldFrames[trackerId] = None + + if oldFrames[trackerId] is None: + oldFrames[trackerId] = gray + + # compute the absolute difference between the current frame and + # first frame + frameDelta = cv2.absdiff(oldFrames[trackerId], gray) + thresh = cv2.threshold(frameDelta, 55, 255, cv2.THRESH_BINARY)[1] + + # dilate the thresholded image to fill in holes, then find contours + # on thresholded image + thresh = cv2.dilate(thresh, None, iterations=2) + image = thresh.copy() + image,cnts,hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) + + # loop over the contours + for c in cnts: + # if the contour is too small, ignore it + #if cv2.contourArea(c) > args["max_area"] or cv2.contourArea < args["min_area"]: + # continue + d = max(cnts, key = cv2.contourArea) + # compute the bounding box for the contour, draw it on the frame, + # and update the text + (x, y, w, h) = cv2.boundingRect(d) + matrix = {} + matrix["tag"] = "Contour" + matrix["x"] = int(x) + matrix["y"] = int(y) + matrix["w"] = int(w) + matrix["h"] = int(h) + returnData.append(matrix) + return returnData + +# bake the image data by a file path +# POST body contains the "img" variable. The value should be to a local image path. +# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg +@app.route('/', methods=['GET']) +def index(): + return "Pumpkin.py is running. This web interface should NEVER be accessible remotely. The Node.js plugin that runs this script should only be allowed accessible remotely." + +# bake the image data by a file path +# POST body contains the "img" variable. The value should be to a local image path. +# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg +@app.route('/post', methods=['POST']) +def post(): + filepath = request.form['img'] + return jsonify(spark(filepath)) + +# bake the image data by a file path +# GET string contains the "img" variable. The value should be to a local image path. +# Example : /dev/shm/streams/[GROUP_KEY]/[MONITOR_ID]/s.jpg +@app.route('/get', methods=['GET']) +def get(): + filepath = request.args.get('img') + return jsonify(spark(filepath)) + +@socketio.on('f') +def receiveMessage(message): + emit('f',{'id':message.get("id"),'data':spark(message.get("path"),message.get("trackerId"))}) + +# quick-and-dirty start +if __name__ == '__main__': + socketio.run(app, port=httpPort) diff --git a/plugins/python-contour/shinobi-python-contour.js b/plugins/python-contour/shinobi-python-contour.js new file mode 100644 index 0000000..711eb47 --- /dev/null +++ b/plugins/python-contour/shinobi-python-contour.js @@ -0,0 +1,298 @@ +// +// Shinobi - Python DLIB Plugin +// Copyright (C) 2016-2025 Moe Alam, moeiscool +// +// # Donate +// +// If you like what I am doing here and want me to continue please consider donating :) +// PayPal : paypal@m03.ca +// +process.on('uncaughtException', function (err) { + console.error('uncaughtException',err); +}); +//main vars +var fs=require('fs'); +var exec = require('child_process').exec; +var spawn = require('child_process').spawn; +var moment = require('moment'); +var http = require('http'); +var express = require('express'); +var socketIoClient = require('socket.io-client'); +var config = require('./conf.json'); +var http = require('http'), + app = express(), + server = http.createServer(app); + +exec("kill $(ps aux | grep '[p]ython3 pumpkin.py' | awk '{print $2}')") + +s={ + group:{}, + dir:{}, + isWin:(process.platform==='win32'), + s:function(json){return JSON.stringify(json,null,3)} +} +s.checkCorrectPathEnding=function(x){ + var length=x.length + if(x.charAt(length-1)!=='/'){ + x=x+'/' + } + return x.replace('__DIR__',__dirname) +} +s.debugLog = function(){ + if(config.debugLog === true){ + console.log(new Date(),arguments) + if(config.debugLogVerbose === true){ + console.log(new Error()) + } + } +} +if(!config.port){config.port=8080} +if(!config.pythonScript){config.pythonScript=__dirname+'/pumpkin.py'} +if(!config.pythonPort){config.pythonPort=7990} +if(!config.hostPort){config.hostPort=8082} +if(config.systemLog===undefined){config.systemLog=true} +//default stream folder check +if(!config.streamDir){ + if(s.isWin===false){ + config.streamDir='/dev/shm' + }else{ + config.streamDir=config.windowsTempDir + } + if(!fs.existsSync(config.streamDir)){ + config.streamDir=__dirname+'/streams/' + }else{ + config.streamDir+='/streams/' + } +} +s.dir.streams=config.streamDir; +//streams dir +if(!fs.existsSync(s.dir.streams)){ + fs.mkdirSync(s.dir.streams); +} +s.gid=function(x){ + if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for( var i=0; i < x; i++ ) + t += p.charAt(Math.floor(Math.random() * p.length)); + return t; +}; +s.getRequest = function(url,callback){ + return http.get(url, function(res){ + var body = ''; + res.on('data', function(chunk){ + body += chunk; + }); + res.on('end',function(){ + try{body = JSON.parse(body)}catch(err){} + callback(body) + }); + }).on('error', function(e){ +// s.systemLog("Get Snapshot Error", e); + }); +} +s.multiplerHeight = 1 +s.multiplerWidth = 1 +s.detectObject=function(buffer,d,tx){ + d.tmpFile=s.gid(5)+'.jpg' + if(!fs.existsSync(s.dir.streams)){ + fs.mkdirSync(s.dir.streams); + } + d.dir=s.dir.streams+d.ke+'/' + if(!fs.existsSync(d.dir)){ + fs.mkdirSync(d.dir); + } + d.dir=s.dir.streams+d.ke+'/'+d.id+'/' + if(!fs.existsSync(d.dir)){ + fs.mkdirSync(d.dir); + } + fs.writeFile(d.dir+d.tmpFile,buffer,function(err){ + if(err) return s.systemLog(err); + if(s.isPythonRunning === false){ + return console.log('Python Script is not Running.') + } + var callbackId = s.gid(10) + s.group[d.ke][d.id].sendToPython({path:d.dir+d.tmpFile,id:callbackId,trackerId:d.ke+d.id},function(data){ + if(data.length > 0){ + var mats=[] + data.forEach(function(v){ + mats.push({ + x:v.x, + y:v.y, + width: v.w, + height: v.h, + confidence:v.confidence, + tag:v.tag + }) + }) + tx({ + f:'trigger', + id:d.id, + ke:d.ke, + details:{ + plug:config.plug, + name:'dlib', + reason:'object', + matrices:mats, + imgHeight:parseFloat(d.mon.detector_scale_y), + imgWidth:parseFloat(d.mon.detector_scale_x) + } + }) + } + delete(s.callbacks[callbackId]) + exec('rm -rf '+d.dir+d.tmpFile,{encoding:'utf8'}) + }) + }) +} +s.systemLog=function(q,w,e){ + if(w===undefined){return} + if(!w){w=''} + if(!e){e=''} + if(config.systemLog===true){ + return console.log(moment().format(),q,w,e) + } +} +s.MainEventController=function(d,cn,tx){ + switch(d.f){ + case'init_plugin_as_host': + if(!cn){ + console.log('No CN',d) + return + } + if(d.key!==config.key){ + console.log(new Date(),'Plugin Key Mismatch',cn.request.connection.remoteAddress,d) + cn.emit('init',{ok:false}) + cn.disconnect() + }else{ + console.log(new Date(),'Plugin Connected to Client',cn.request.connection.remoteAddress) + cn.emit('init',{ok:true,plug:config.plug,notice:config.notice,type:config.type}) + } + break; + case'init_monitor': + if(s.group[d.ke]&&s.group[d.ke][d.id]){ + delete(s.group[d.ke][d.id].buffer) + s.group[d.ke][d.id].refreshTracker(d.ke+d.id) + } + break; + case'frame': + try{ + if(!s.group[d.ke]){ + s.group[d.ke]={} + } + if(!s.group[d.ke][d.id]){ + var engine = s.createCameraBridgeToPython(d.ke+d.id) + s.group[d.ke][d.id]={ + sendToPython : engine.sendToPython, + refreshTracker : engine.refreshTracker + } + } + if(!s.group[d.ke][d.id].buffer){ + s.group[d.ke][d.id].buffer=[d.frame]; + }else{ + s.group[d.ke][d.id].buffer.push(d.frame) + } + if(d.frame[d.frame.length-2] === 0xFF && d.frame[d.frame.length-1] === 0xD9){ + s.detectObject(Buffer.concat(s.group[d.ke][d.id].buffer),d,tx) + s.group[d.ke][d.id].buffer=null; + } + }catch(err){ + if(err){ + s.systemLog(err) + delete(s.group[d.ke][d.id].buffer) + } + } + break; + } +} +server.listen(config.hostPort); +//web pages and plugin api +app.get('/', function (req, res) { + res.end(''+config.plug+' for Shinobi is running') +}); +//Conector to Shinobi +if(config.mode==='host'){ + //start plugin as host + var io = require('socket.io')(server); + io.attach(server); + s.connectedClients={}; + io.on('connection', function (cn) { + s.connectedClients[cn.id]={id:cn.id} + s.connectedClients[cn.id].tx = function(data){ + data.pluginKey=config.key;data.plug=config.plug; + return io.to(cn.id).emit('ocv',data); + } + cn.on('f',function(d){ + s.MainEventController(d,cn,s.connectedClients[cn.id].tx) + }); + cn.on('disconnect',function(d){ + delete(s.connectedClients[cn.id]) + }) + }); +}else{ + //start plugin as client + if(!config.host){config.host='localhost'} + var io = socketIoClient('ws://'+config.host+':'+config.port);//connect to master + s.cx=function(x){x.pluginKey=config.key;x.plug=config.plug;return io.emit('ocv',x)} + io.on('connect',function(d){ + s.cx({f:'init',plug:config.plug,notice:config.notice,type:config.type}); + }) + io.on('disconnect',function(d){ + io.connect(); + }) + io.on('f',function(d){ + s.MainEventController(d,null,s.cx) + }) +} + +//Start Python Controller +s.callbacks = {} +s.createCameraBridgeToPython = function(uniqueId){ + var pythonIo = socketIoClient('ws://localhost:'+config.pythonPort,{transports : ['websocket']}); + var sendToPython = function(data,callback){ + s.callbacks[data.id] = callback + pythonIo.emit('f',data) + } + var refreshTracker = function(data){ + pythonIo.emit('refreshTracker',{trackerId : data}) + } + pythonIo.on('connect',function(d){ + s.debugLog(uniqueId+' is Connected from Python') + }) + pythonIo.on('disconnect',function(d){ + s.debugLog(uniqueId+' is Disconnected from Python') + setTimeout(function(){ + pythonIo.connect(); + s.debugLog(uniqueId+' is Attempting to Reconect to Python') + },3000) + }) + pythonIo.on('f',function(d){ + if(s.callbacks[d.id]){ + s.callbacks[d.id](d.data) + delete(s.callbacks[d.id]) + } + }) + return {refreshTracker : refreshTracker, sendToPython : sendToPython} +} + + +//Start Python Daemon +process.env.PYTHONUNBUFFERED = 1; +s.createPythonProcess = function(){ + s.isPythonRunning = false + s.pythonScript = spawn('sh',[__dirname+'/bootPy.sh',config.pythonScript,__dirname]); + var onStdErr = function(data){ + s.debugLog(data.toString()) + } + var onStdOut = function(data){ + s.debugLog(data.toString()) + } + setTimeout(function(){ + s.isPythonRunning = true + },5000) + s.pythonScript.stderr.on('data',onStdErr); + + s.pythonScript.stdout.on('data',onStdOut); + + s.pythonScript.on('close', function () { + s.debugLog('Python CLOSED') + }); +} +s.createPythonProcess() diff --git a/sql/docker/01-framework.sql b/sql/docker/01-framework.sql deleted file mode 100644 index cec2dda..0000000 --- a/sql/docker/01-framework.sql +++ /dev/null @@ -1,113 +0,0 @@ --- -------------------------------------------------------- --- Host: 192.168.88.58 --- Server version: 5.7.17-0ubuntu0.16.04.1 - (Ubuntu) --- Server OS: Linux --- HeidiSQL Version: 9.3.0.4984 --- -------------------------------------------------------- - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET NAMES utf8mb4 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; - --- Dumping structure for table ccio.API -CREATE TABLE IF NOT EXISTS `API` ( - `ke` varchar(50) DEFAULT NULL, - `uid` varchar(50) DEFAULT NULL, - `ip` tinytext, - `code` varchar(100) DEFAULT NULL, - `details` text, - `time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Events -CREATE TABLE IF NOT EXISTS `Events` ( - `ke` varchar(50) DEFAULT NULL, - `mid` varchar(50) DEFAULT NULL, - `details` text, - `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Logs -CREATE TABLE IF NOT EXISTS `Logs` ( - `ke` varchar(50) DEFAULT NULL, - `mid` varchar(50) DEFAULT NULL, - `info` text, - `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Monitors -CREATE TABLE IF NOT EXISTS `Monitors` ( - `mid` varchar(50) DEFAULT NULL, - `ke` varchar(50) DEFAULT NULL, - `name` varchar(50) DEFAULT NULL, - `shto` text, - `shfr` text, - `details` longtext, - `type` varchar(50) DEFAULT 'jpeg', - `ext` varchar(50) DEFAULT 'webm', - `protocol` varchar(50) DEFAULT 'http', - `host` varchar(100) DEFAULT '0.0.0.0', - `path` varchar(100) DEFAULT '/', - `port` int(8) DEFAULT '80', - `fps` int(8) DEFAULT '1', - `mode` varchar(15) DEFAULT NULL, - `width` int(11) DEFAULT '640', - `height` int(11) DEFAULT '360' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Presets -CREATE TABLE IF NOT EXISTS `Presets` ( - `ke` varchar(50) DEFAULT NULL, - `name` text, - `details` text, - `type` enum('monitor','event','user') DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Users -CREATE TABLE IF NOT EXISTS `Users` ( - `ke` varchar(50) DEFAULT NULL, - `uid` varchar(50) DEFAULT NULL, - `auth` varchar(50) DEFAULT NULL, - `mail` varchar(100) DEFAULT NULL, - `pass` varchar(100) DEFAULT NULL, - `details` longtext, - UNIQUE KEY `mail` (`mail`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. - - --- Dumping structure for table ccio.Videos -CREATE TABLE IF NOT EXISTS `Videos` ( - `mid` varchar(50) DEFAULT NULL, - `ke` varchar(50) DEFAULT NULL, - `ext` enum('webm','mp4') DEFAULT NULL, - `time` timestamp NULL DEFAULT NULL, - `duration` float DEFAULT NULL, - `size` float DEFAULT NULL, - `frames` int(11) DEFAULT NULL, - `end` timestamp NULL DEFAULT NULL, - `status` int(1) DEFAULT '0' COMMENT '0:Building,1:Complete,2:Read,3:Archive', - `details` text -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- Data exporting was unselected. -/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; -/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/sql/framework.sql b/sql/framework.sql index 4961e54..7ef15e1 100644 --- a/sql/framework.sql +++ b/sql/framework.sql @@ -1,6 +1,6 @@ -- -------------------------------------------------------- --- Host: 192.168.88.37 --- Server version: 10.1.25-MariaDB- - Ubuntu 17.04 +-- Host: 192.168.1.31 +-- Server version: 10.1.30-MariaDB-0ubuntu0.17.10.1 - Ubuntu 17.10 -- Server OS: debian-linux-gnu -- HeidiSQL Version: 9.4.0.5125 -- -------------------------------------------------------- @@ -26,6 +26,19 @@ CREATE TABLE IF NOT EXISTS `API` ( `time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- Data exporting was unselected. +-- Dumping structure for table ccio.Cloud Videos +CREATE TABLE IF NOT EXISTS `Cloud Videos` ( + `mid` varchar(50) NOT NULL, + `ke` varchar(50) DEFAULT NULL, + `href` text NOT NULL, + `size` float DEFAULT NULL, + `time` timestamp NULL DEFAULT NULL, + `end` timestamp NULL DEFAULT NULL, + `status` int(1) DEFAULT '0' COMMENT '0:Complete,1:Read,2:Archive', + `details` text +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- Data exporting was unselected. -- Dumping structure for table ccio.Events CREATE TABLE IF NOT EXISTS `Events` ( @@ -101,6 +114,29 @@ CREATE TABLE IF NOT EXISTS `Videos` ( `details` text ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- Data exporting was unselected. +-- Dumping structure for table ccio.Files +CREATE TABLE IF NOT EXISTS `Files` ( + `ke` varchar(50) NOT NULL, + `mid` varchar(50) NOT NULL, + `name` tinytext NOT NULL, + `size` float NOT NULL DEFAULT '0', + `details` text NOT NULL, + `status` int(1) NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`; + +-- Data exporting was unselected. +-- Dumping structure for table ccio.Schedules +CREATE TABLE IF NOT EXISTS `Schedules` ( + `ke` varchar(50) DEFAULT NULL, + `name` text, + `details` text, + `start` varchar(10) DEFAULT NULL, + `end` varchar(10) DEFAULT NULL, + `enabled` int(1) NOT NULL DEFAULT '1' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- Data exporting was unselected. /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; diff --git a/test/run.js b/test/run.js index 0ca943f..2e18014 100644 --- a/test/run.js +++ b/test/run.js @@ -1,94 +1,151 @@ -module.exports = function(s,config,lang,app,io){ +var fs = require('fs') +var request = require('request') +var execSync = require('child_process').execSync +module.exports = function(s,config,lang,io){ + var temp = {} + var superUsers = require(s.location.super) + var requestURL = 'http://'+config.bindip + ':' + config.port +'/' + var requestSuperURL = 'http://localhost:' + config.port +'/super/' + superUsers[0].tokens[0] + '/' + var getBaseURL = function(){ + return 'http://localhost:' + config.port +'/' + } + var buildRegularApiRequestURL = function(auth,path,groupKey){ + return getBaseURL() + auth + '/' + path + '/' + groupKey + '/' + } + var buildAdminRequestURL = function(auth,path,groupKey){ + return getBaseURL() + 'admin/' + auth + '/' + path + '/' + groupKey + '/' + } var checkResult = function(functionName,expectedResult,testResult){ if(expectedResult !== testResult){ console.log(expectedResult,testResult) - throw new Error('x ' + functionName + ' : Failed!') + console.log('x ' + functionName + ' : Failed!') + return false }else{ - console.log('- ' + functionName + ' : Success') + console.log('✓ ' + functionName + ' : Success') + return true } } + var administratorAccountData = { + "mail":"test@test1.com", + "pass":"test1", + "pass_again":"test1", + "ke":"GroupKey123456", + "details":{ + "factorAuth": "0", + "size": "10000", + "days": "5", + "event_days": "10", + "log_days": "10", + "max_camera": "", + "permissions": "all", + "edit_size": "1", + "edit_days": "1", + "edit_event_days": "1", + "edit_log_days": "1", + "use_admin": "1", + "use_aws_s3": "1", + "use_webdav": "1", + "use_discordbot": "1", + "use_ldap": "1" + } + } + var getAdministratorAccountData = function(){ + return Object.assign(administratorAccountData,{}) + } var sampleMonitorObject = require('./testMonitor-WatchOnly.json') var test = { "basic.js" : { - checkRelativePath : function(){ + checkRelativePath : function(next){ var expectedResult = s.mainDirectory + '/' var testResult = s.checkRelativePath('') - checkResult('checkRelativePath',expectedResult,testResult) + checkResult('Internal Function : checkRelativePath',expectedResult,testResult) + next() }, - parseJSON : function(){ + parseJSON : function(next){ var expectedResult = {} var testResult = s.parseJSON('{}') - checkResult('parseJSON',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : parseJSON',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() }, - stringJSON : function(){ + stringJSON : function(next){ var expectedResult = '{}' var testResult = s.stringJSON({}) - checkResult('stringJSON',expectedResult,testResult) + checkResult('Internal Function : stringJSON',expectedResult,testResult) + next() }, - addUserPassToUrl : function(){ + addUserPassToUrl : function(next){ var expectedResult = 'http://user:pass@url.com' var testResult = s.addUserPassToUrl('http://url.com','user','pass') - checkResult('addUserPassToUrl',expectedResult,testResult) + checkResult('Internal Function : addUserPassToUrl',expectedResult,testResult) + next() }, - checkCorrectPathEnding : function(){ + checkCorrectPathEnding : function(next){ var expectedResult = '/' var testResult = s.checkCorrectPathEnding('') - checkResult('checkCorrectPathEnding',expectedResult,testResult) + checkResult('Internal Function : checkCorrectPathEnding',expectedResult,testResult) + next() }, - md5 : function(){ + md5 : function(next){ var expectedResult = '5f4dcc3b5aa765d61d8327deb882cf99' var testResult = s.md5('password') - checkResult('md5',expectedResult,testResult) + checkResult('Internal Function : md5',expectedResult,testResult) + next() }, - sha256 : function(){ + sha256 : function(next){ var expectedResult = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' var testResult = require('crypto').createHash('sha256').update('test').digest("hex") - checkResult('createHash/sha256',expectedResult,testResult) + checkResult('Internal Function : createHash/sha256',expectedResult,testResult) + next() }, - nameToTime : function(){ + nameToTime : function(next){ var expectedResult = '2018-10-22 23:00:00' var testResult = s.nameToTime('2018-10-22T23-00-00.mp4') - checkResult('nameToTime',expectedResult,testResult) + checkResult('Internal Function : nameToTime',expectedResult,testResult) + next() }, - ipRange : function(){ + ipRange : function(next){ var expectedResult = [ '192.168.1.1', '192.168.1.2', '192.168.1.3' ] var testResult = s.ipRange('192.168.1.1','192.168.1.3') - checkResult('ipRange',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : ipRange',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() }, - portRange : function(){ + portRange : function(next){ var expectedResult = [ 8000, 8001, 8002, ] var testResult = s.portRange(8000,8002) - checkResult('portRange',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : portRange',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() }, - getFunctionParamNames : function(){ + getFunctionParamNames : function(next){ var testing = function(arg1,arg2){} var expectedResult = [ 'arg1', 'arg2', ] var testResult = s.getFunctionParamNames(testing) - checkResult('getFunctionParamNames',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : getFunctionParamNames',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() } }, "ffmpeg.js" : { - splitForFFPMEG : function(){ + splitForFFPMEG : function(next){ var expectedResult = [ 'flag1', 'flag2', 'fl ag3', ] var testResult = s.splitForFFPMEG('flag1 flag2 "fl ag3"') - checkResult('splitForFFPMEG',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : splitForFFPMEG',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() }, - "ffmpeg" : function(){ + "ffmpeg" : function(next){ //command string builder var x = {tmp : ''} s.checkDetails(sampleMonitorObject) @@ -100,7 +157,7 @@ module.exports = function(s,config,lang,app,io){ s.ffmpegFunctions.buildMainDetector(sampleMonitorObject,x) s.ffmpegFunctions.assembleMainPieces(sampleMonitorObject,x) var testResult = x.ffmpegCommandString - checkResult('ffmpeg',expectedResult,testResult) + checkResult('Internal Function : ffmpeg',expectedResult,testResult) //check pipe builder var expectedResult = [] var times = config.pipeAddition @@ -112,19 +169,490 @@ module.exports = function(s,config,lang,app,io){ } s.ffmpegFunctions.createPipeArray(sampleMonitorObject,x) var testResult = x.stdioPipes - checkResult('ffmpeg.createPipeArray',JSON.stringify(expectedResult),JSON.stringify(testResult)) + checkResult('Internal Function : ffmpeg.createPipeArray',JSON.stringify(expectedResult),JSON.stringify(testResult)) + next() } + }, + "webServer" : { + "super/accounts/saveSettings" : function(next){ + console.log(requestSuperURL) + var userData = { + "mail": "admin@shinobi.video1", + "pass": "password", + "pass_again": "password" + } + var builtURL = requestSuperURL + 'accounts/saveSettings?data=' + encodeURIComponent(s.s(userData)) + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /accounts/saveSettings',true,response.ok) + next() + }) + }, + "super/accounts/registerAdmin" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = requestSuperURL + 'accounts/registerAdmin?data=' + encodeURIComponent(s.s(userData)) + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + administratorAccountData.uid = response.user.uid + checkResult('API : /accounts/registerAdmin',true,response.ok) + next() + }) + }, + "super/accounts/deleteAdmin" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = requestSuperURL + 'accounts/deleteAdmin?account=' + encodeURIComponent(s.s({ + "mail":"test@test1.com", + "ke":"GroupKey123456", + "uid":administratorAccountData.uid + + })) + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /accounts/deleteAdmin',true,response.ok) + next() + }) + }, + "super/accounts/registerAdmin (Recreate)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = requestSuperURL + 'accounts/registerAdmin?data=' + encodeURIComponent(s.s(userData)) + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + administratorAccountData.uid = response.user.uid + checkResult('API : /accounts/registerAdmin',true,response.ok) + next() + }) + }, + "super/accounts/list" : function(next){ + var builtURL = requestSuperURL + 'accounts/list' + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok === true){ + // administratorAccountData = response.users[0] + } + checkResult('API : /accounts/list',1,response.users.length) + next() + }) + }, + "super/accounts/list/admin" : function(next){ + var builtURL = requestSuperURL + 'accounts/list/admin' + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /accounts/list/admin',1,response.users.length) + next() + }) + }, + "super/accounts/list/sub" : function(next){ + var builtURL = requestSuperURL + 'accounts/list/sub' + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /accounts/list/sub',0,response.users.length) + next() + }) + }, + "super/accounts/editAdmin" : function(next){ + var userData = getAdministratorAccountData() + delete(userData.uid) + var builtURL = requestSuperURL + 'accounts/editAdmin?data=' + encodeURIComponent(s.s(userData)) + "&account=" + encodeURIComponent(s.s({ + "mail":"test@test1.com", + "ke":"GroupKey123456" + })) + request(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(response.msg) + checkResult('API : /accounts/editAdmin',true,response.ok) + next() + }) + }, + "/ (Login via API)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = getBaseURL() + '?json=true' + request.post(builtURL,{ + form : {machineID: "testMachineId", mail: "test@test1.com", pass: "test1", function: "dash"} + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(response) + administratorAccountData.auth = response.$user.auth_token + checkResult('API : / (Login via API)',true,response.ok) + next() + }) + }, + "/api/add" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'api',administratorAccountData.ke) + 'add' + request.post(builtURL,{ + form : { + "data": { + "ip": "0.0.0.0", + "details": { + "auth_socket": "1", + "get_monitors": "1", + "control_monitors": "1", + "get_logs": "1", + "watch_stream": "1", + "watch_snapshot": "1", + "watch_videos": "1", + "delete_videos": "1" + } + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + temp.newApiKey = response.api.code + checkResult('API : /api/add',true,response.ok) + next() + }) + }, + "Delete API Key" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'api',administratorAccountData.ke) + 'delete' + request.post(builtURL,{ + form : { + "data": { + "code": temp.newApiKey + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /api/delete',true,response.ok) + next() + }) + }, + "/admin/accounts/register" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildAdminRequestURL(administratorAccountData.auth,'accounts',administratorAccountData.ke) + 'register' + request.post(builtURL,{ + form : { + "data": { + "mail": "test@test2.com", + "pass": "test1", + "password_again": "test1" + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + temp.subAccount = response.user + checkResult('API : /admin/accounts/register',true,response.ok) + next() + }) + }, + "/admin/accounts/edit" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildAdminRequestURL(administratorAccountData.auth,'accounts',administratorAccountData.ke) + 'edit' + request.post(builtURL,{ + form : { + "data": { + "uid": temp.subAccount.uid, + "mail": temp.subAccount.mail, + "details": temp.subAccount.details + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /admin/accounts/edit',true,response.ok) + next() + }) + }, + "/admin/accounts/delete" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildAdminRequestURL(administratorAccountData.auth,'accounts',administratorAccountData.ke) + 'delete' + request.post(builtURL,{ + form : { + "data": { + "uid": temp.subAccount.uid, + "mail": temp.subAccount.mail, + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + temp.subAccount = null + checkResult('API : /admin/accounts/delete',true,response.ok) + next() + }) + }, + "/configureMonitor (Add)" : function(next){ + temp.monitorId = "10998" + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'configureMonitor',administratorAccountData.ke) + temp.monitorId + request.post(builtURL,{ + form : { + "data": {"mode":"start","mid":temp.monitorId,"name":"ReoLinkWireless","type":"mp4","protocol":"https","host":"cdn.shinobi.video","port":"443","path":"/videos/faces.mp4","ext":"mp4","fps":"3","width":"2048","height":"1536","details":"{\"notes\":\"\",\"dir\":\"\",\"auto_host_enable\":\"1\",\"auto_host\":\"rtsp://user:pass@192.168.1.40:554/\",\"rtsp_transport\":\"tcp\",\"muser\":\"user\",\"mpass\":\"pass\",\"port_force\":null,\"fatal_max\":\"0\",\"aduration\":\"1000000\",\"probesize\":\"1000000\",\"stream_loop\":\"1\",\"sfps\":\"\",\"accelerator\":\"0\",\"hwaccel\":\"cuvid\",\"hwaccel_vcodec\":\"h264_cuvid\",\"hwaccel_device\":\"\",\"stream_type\":\"mp4\",\"stream_flv_type\":\"http\",\"stream_flv_maxLatency\":\"\",\"stream_mjpeg_clients\":\"0\",\"stream_vcodec\":\"copy\",\"stream_acodec\":\"no\",\"hls_time\":\"2\",\"hls_list_size\":\"2\",\"preset_stream\":\"\",\"signal_check\":\"\",\"signal_check_log\":\"0\",\"stream_quality\":\"1\",\"stream_fps\":\"10\",\"stream_scale_x\":\"3072\",\"stream_scale_y\":\"1728\",\"rotate_stream\":null,\"svf\":\"\",\"tv_channel\":null,\"tv_channel_id\":\"\",\"tv_channel_group_title\":\"\",\"stream_timestamp\":null,\"stream_timestamp_font\":\"\",\"stream_timestamp_font_size\":\"\",\"stream_timestamp_color\":\"\",\"stream_timestamp_box_color\":\"\",\"stream_timestamp_x\":\"\",\"stream_timestamp_y\":\"\",\"stream_watermark\":\"0\",\"stream_watermark_location\":\"\",\"stream_watermark_position\":null,\"snap\":\"0\",\"snap_fps\":\"1\",\"snap_scale_x\":\"1920\",\"snap_scale_y\":\"1072\",\"snap_vf\":\"\",\"vcodec\":\"copy\",\"crf\":\"1\",\"preset_record\":\"\",\"acodec\":\"no\",\"dqf\":\"0\",\"cutoff\":\"\",\"rotate_record\":null,\"vf\":\"\",\"timestamp\":\"0\",\"timestamp_font\":\"\",\"timestamp_font_size\":\"\",\"timestamp_color\":\"\",\"timestamp_box_color\":\"\",\"timestamp_x\":\"\",\"timestamp_y\":\"\",\"watermark\":null,\"watermark_location\":\"\",\"watermark_position\":null,\"cust_input\":\"\",\"cust_snap\":\"\",\"cust_rtmp\":\"\",\"cust_rawh264\":\"\",\"cust_detect\":\"\",\"cust_stream\":\"\",\"cust_stream_server\":\"\",\"cust_record\":\"\",\"custom_output\":\"\",\"detector\":\"0\",\"detector_pam\":\"0\",\"detector_noise_filter\":null,\"detector_webhook\":\"0\",\"detector_webhook_url\":\"\",\"detector_command_enable\":\"0\",\"detector_command\":\"\",\"detector_command_timeout\":\"\",\"detector_lock_timeout\":\"\",\"detector_save\":\"0\",\"detector_frame_save\":\"0\",\"detector_mail\":\"0\",\"detector_mail_timeout\":\"\",\"detector_record_method\":\"sip\",\"detector_trigger\":\"1\",\"detector_trigger_record_fps\":\"\",\"detector_timeout\":\"10\",\"watchdog_reset\":\"0\",\"detector_delete_motionless_videos\":\"0\",\"detector_send_frames\":\"1\",\"detector_region_of_interest\":\"0\",\"detector_fps\":\"\",\"detector_scale_x\":\"640\",\"detector_scale_y\":\"480\",\"detector_use_motion\":\"1\",\"detector_use_detect_object\":\"0\",\"detector_frame\":\"0\",\"detector_sensitivity\":\"\",\"cords\":\"[]\",\"detector_buffer_vcodec\":\"auto\",\"detector_buffer_fps\":\"\",\"detector_buffer_hls_time\":\"\",\"detector_buffer_hls_list_size\":\"\",\"detector_buffer_start_number\":\"\",\"detector_buffer_live_start_index\":\"\",\"detector_lisence_plate\":\"0\",\"detector_lisence_plate_country\":\"us\",\"detector_notrigger\":\"0\",\"detector_notrigger_mail\":\"0\",\"detector_notrigger_timeout\":\"\",\"control\":\"0\",\"control_base_url\":\"\",\"control_url_method\":null,\"control_stop\":null,\"control_url_stop_timeout\":\"\",\"control_url_center\":\"\",\"control_url_left\":\"\",\"control_url_left_stop\":\"\",\"control_url_right\":\"\",\"control_url_right_stop\":\"\",\"control_url_up\":\"\",\"control_url_up_stop\":\"\",\"control_url_down\":\"\",\"control_url_down_stop\":\"\",\"control_url_enable_nv\":\"\",\"control_url_disable_nv\":\"\",\"control_url_zoom_out\":\"\",\"control_url_zoom_out_stop\":\"\",\"control_url_zoom_in\":\"\",\"control_url_zoom_in_stop\":\"\",\"groups\":\"\",\"loglevel\":\"quiet\",\"sqllog\":\"0\",\"detector_cascades\":\"\",\"stream_channels\":\"\",\"input_maps\":\"\",\"input_map_choices\":\"\"}","shto":"[]","shfr":"[]"} + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /configureMonitor (Add)',true,response.ok) + next() + }) + }, + "/configureMonitor (Add Second)" : function(next){ + temp.monitorId2 = "10999" + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'configureMonitor',administratorAccountData.ke) + temp.monitorId2 + request.post(builtURL,{ + form : { + "data": {"mode":"start","mid":temp.monitorId2,"name":"ReoLinkWireless","type":"mp4","protocol":"https","host":"cdn.shinobi.video","port":"443","path":"/videos/faces.mp4","ext":"mp4","fps":"3","width":"2048","height":"1536","details":"{\"notes\":\"\",\"dir\":\"\",\"auto_host_enable\":\"1\",\"auto_host\":\"rtsp://user:pass@192.168.1.40:554/\",\"rtsp_transport\":\"tcp\",\"muser\":\"user\",\"mpass\":\"pass\",\"port_force\":null,\"fatal_max\":\"0\",\"aduration\":\"1000000\",\"probesize\":\"1000000\",\"stream_loop\":\"1\",\"sfps\":\"\",\"accelerator\":\"0\",\"hwaccel\":\"cuvid\",\"hwaccel_vcodec\":\"h264_cuvid\",\"hwaccel_device\":\"\",\"stream_type\":\"hls\",\"stream_flv_type\":\"http\",\"stream_flv_maxLatency\":\"\",\"stream_mjpeg_clients\":\"0\",\"stream_vcodec\":\"copy\",\"stream_acodec\":\"no\",\"hls_time\":\"2\",\"hls_list_size\":\"2\",\"preset_stream\":\"\",\"signal_check\":\"\",\"signal_check_log\":\"0\",\"stream_quality\":\"1\",\"stream_fps\":\"10\",\"stream_scale_x\":\"3072\",\"stream_scale_y\":\"1728\",\"rotate_stream\":null,\"svf\":\"\",\"tv_channel\":null,\"tv_channel_id\":\"\",\"tv_channel_group_title\":\"\",\"stream_timestamp\":null,\"stream_timestamp_font\":\"\",\"stream_timestamp_font_size\":\"\",\"stream_timestamp_color\":\"\",\"stream_timestamp_box_color\":\"\",\"stream_timestamp_x\":\"\",\"stream_timestamp_y\":\"\",\"stream_watermark\":\"0\",\"stream_watermark_location\":\"\",\"stream_watermark_position\":null,\"snap\":\"0\",\"snap_fps\":\"1\",\"snap_scale_x\":\"1920\",\"snap_scale_y\":\"1072\",\"snap_vf\":\"\",\"vcodec\":\"copy\",\"crf\":\"1\",\"preset_record\":\"\",\"acodec\":\"no\",\"dqf\":\"0\",\"cutoff\":\"\",\"rotate_record\":null,\"vf\":\"\",\"timestamp\":\"0\",\"timestamp_font\":\"\",\"timestamp_font_size\":\"\",\"timestamp_color\":\"\",\"timestamp_box_color\":\"\",\"timestamp_x\":\"\",\"timestamp_y\":\"\",\"watermark\":null,\"watermark_location\":\"\",\"watermark_position\":null,\"cust_input\":\"\",\"cust_snap\":\"\",\"cust_rtmp\":\"\",\"cust_rawh264\":\"\",\"cust_detect\":\"\",\"cust_stream\":\"\",\"cust_stream_server\":\"\",\"cust_record\":\"\",\"custom_output\":\"\",\"detector\":\"0\",\"detector_pam\":\"0\",\"detector_noise_filter\":null,\"detector_webhook\":\"0\",\"detector_webhook_url\":\"\",\"detector_command_enable\":\"0\",\"detector_command\":\"\",\"detector_command_timeout\":\"\",\"detector_lock_timeout\":\"\",\"detector_save\":\"0\",\"detector_frame_save\":\"0\",\"detector_mail\":\"0\",\"detector_mail_timeout\":\"\",\"detector_record_method\":\"sip\",\"detector_trigger\":\"1\",\"detector_trigger_record_fps\":\"\",\"detector_timeout\":\"10\",\"watchdog_reset\":\"0\",\"detector_delete_motionless_videos\":\"0\",\"detector_send_frames\":\"1\",\"detector_region_of_interest\":\"0\",\"detector_fps\":\"\",\"detector_scale_x\":\"640\",\"detector_scale_y\":\"480\",\"detector_use_motion\":\"1\",\"detector_use_detect_object\":\"0\",\"detector_frame\":\"0\",\"detector_sensitivity\":\"\",\"cords\":\"[]\",\"detector_buffer_vcodec\":\"auto\",\"detector_buffer_fps\":\"\",\"detector_buffer_hls_time\":\"\",\"detector_buffer_hls_list_size\":\"\",\"detector_buffer_start_number\":\"\",\"detector_buffer_live_start_index\":\"\",\"detector_lisence_plate\":\"0\",\"detector_lisence_plate_country\":\"us\",\"detector_notrigger\":\"0\",\"detector_notrigger_mail\":\"0\",\"detector_notrigger_timeout\":\"\",\"control\":\"0\",\"control_base_url\":\"\",\"control_url_method\":null,\"control_stop\":null,\"control_url_stop_timeout\":\"\",\"control_url_center\":\"\",\"control_url_left\":\"\",\"control_url_left_stop\":\"\",\"control_url_right\":\"\",\"control_url_right_stop\":\"\",\"control_url_up\":\"\",\"control_url_up_stop\":\"\",\"control_url_down\":\"\",\"control_url_down_stop\":\"\",\"control_url_enable_nv\":\"\",\"control_url_disable_nv\":\"\",\"control_url_zoom_out\":\"\",\"control_url_zoom_out_stop\":\"\",\"control_url_zoom_in\":\"\",\"control_url_zoom_in_stop\":\"\",\"groups\":\"\",\"loglevel\":\"quiet\",\"sqllog\":\"0\",\"detector_cascades\":\"\",\"stream_channels\":\"\",\"input_maps\":\"\",\"input_map_choices\":\"\"}","shto":"[]","shfr":"[]"} + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /configureMonitor (Add Second)',true,response.ok) + next() + }) + }, + "/configureMonitor (Edit)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'configureMonitor',administratorAccountData.ke) + temp.monitorId + request.post(builtURL,{ + form : { + "data": {"mode":"start","mid":temp.monitorId,"name":"ReoLinkWireless","type":"mp4","protocol":"https","host":"cdn.shinobi.video","port":"443","path":"/videos/faces.mp4","ext":"mp4","fps":"3","width":"2048","height":"1536","details":"{\"notes\":\"\",\"dir\":\"\",\"auto_host_enable\":\"1\",\"auto_host\":\"rtsp://user:pass@192.168.1.40:554/\",\"rtsp_transport\":\"tcp\",\"muser\":\"user\",\"mpass\":\"pass\",\"port_force\":null,\"fatal_max\":\"0\",\"aduration\":\"1000000\",\"probesize\":\"1000000\",\"stream_loop\":\"1\",\"sfps\":\"\",\"accelerator\":\"0\",\"hwaccel\":\"cuvid\",\"hwaccel_vcodec\":\"h264_cuvid\",\"hwaccel_device\":\"\",\"stream_type\":\"mp4\",\"stream_flv_type\":\"http\",\"stream_flv_maxLatency\":\"\",\"stream_mjpeg_clients\":\"0\",\"stream_vcodec\":\"copy\",\"stream_acodec\":\"no\",\"hls_time\":\"2\",\"hls_list_size\":\"2\",\"preset_stream\":\"\",\"signal_check\":\"\",\"signal_check_log\":\"0\",\"stream_quality\":\"1\",\"stream_fps\":\"10\",\"stream_scale_x\":\"3072\",\"stream_scale_y\":\"1728\",\"rotate_stream\":null,\"svf\":\"\",\"tv_channel\":null,\"tv_channel_id\":\"\",\"tv_channel_group_title\":\"\",\"stream_timestamp\":null,\"stream_timestamp_font\":\"\",\"stream_timestamp_font_size\":\"\",\"stream_timestamp_color\":\"\",\"stream_timestamp_box_color\":\"\",\"stream_timestamp_x\":\"\",\"stream_timestamp_y\":\"\",\"stream_watermark\":\"0\",\"stream_watermark_location\":\"\",\"stream_watermark_position\":null,\"snap\":\"0\",\"snap_fps\":\"1\",\"snap_scale_x\":\"1920\",\"snap_scale_y\":\"1072\",\"snap_vf\":\"\",\"vcodec\":\"copy\",\"crf\":\"1\",\"preset_record\":\"\",\"acodec\":\"no\",\"dqf\":\"0\",\"cutoff\":\"\",\"rotate_record\":null,\"vf\":\"\",\"timestamp\":\"0\",\"timestamp_font\":\"\",\"timestamp_font_size\":\"\",\"timestamp_color\":\"\",\"timestamp_box_color\":\"\",\"timestamp_x\":\"\",\"timestamp_y\":\"\",\"watermark\":null,\"watermark_location\":\"\",\"watermark_position\":null,\"cust_input\":\"\",\"cust_snap\":\"\",\"cust_rtmp\":\"\",\"cust_rawh264\":\"\",\"cust_detect\":\"\",\"cust_stream\":\"\",\"cust_stream_server\":\"\",\"cust_record\":\"\",\"custom_output\":\"\",\"detector\":\"0\",\"detector_pam\":\"0\",\"detector_noise_filter\":null,\"detector_webhook\":\"0\",\"detector_webhook_url\":\"\",\"detector_command_enable\":\"0\",\"detector_command\":\"\",\"detector_command_timeout\":\"\",\"detector_lock_timeout\":\"\",\"detector_save\":\"0\",\"detector_frame_save\":\"0\",\"detector_mail\":\"0\",\"detector_mail_timeout\":\"\",\"detector_record_method\":\"sip\",\"detector_trigger\":\"1\",\"detector_trigger_record_fps\":\"\",\"detector_timeout\":\"10\",\"watchdog_reset\":\"0\",\"detector_delete_motionless_videos\":\"0\",\"detector_send_frames\":\"1\",\"detector_region_of_interest\":\"0\",\"detector_fps\":\"\",\"detector_scale_x\":\"640\",\"detector_scale_y\":\"480\",\"detector_use_motion\":\"1\",\"detector_use_detect_object\":\"0\",\"detector_frame\":\"0\",\"detector_sensitivity\":\"\",\"cords\":\"[]\",\"detector_buffer_vcodec\":\"auto\",\"detector_buffer_fps\":\"\",\"detector_buffer_hls_time\":\"\",\"detector_buffer_hls_list_size\":\"\",\"detector_buffer_start_number\":\"\",\"detector_buffer_live_start_index\":\"\",\"detector_lisence_plate\":\"0\",\"detector_lisence_plate_country\":\"us\",\"detector_notrigger\":\"0\",\"detector_notrigger_mail\":\"0\",\"detector_notrigger_timeout\":\"\",\"control\":\"0\",\"control_base_url\":\"\",\"control_url_method\":null,\"control_stop\":null,\"control_url_stop_timeout\":\"\",\"control_url_center\":\"\",\"control_url_left\":\"\",\"control_url_left_stop\":\"\",\"control_url_right\":\"\",\"control_url_right_stop\":\"\",\"control_url_up\":\"\",\"control_url_up_stop\":\"\",\"control_url_down\":\"\",\"control_url_down_stop\":\"\",\"control_url_enable_nv\":\"\",\"control_url_disable_nv\":\"\",\"control_url_zoom_out\":\"\",\"control_url_zoom_out_stop\":\"\",\"control_url_zoom_in\":\"\",\"control_url_zoom_in_stop\":\"\",\"groups\":\"\",\"loglevel\":\"quiet\",\"sqllog\":\"0\",\"detector_cascades\":\"\",\"stream_channels\":\"\",\"input_maps\":\"\",\"input_map_choices\":\"\"}","shto":"[]","shfr":"[]"} + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /configureMonitor (Edit)',true,response.ok) + next() + }) + }, + "/monitor/[MONITOR_ID] (Get)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitor',administratorAccountData.ke) + temp.monitorId + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /monitor/[MONITOR_ID] (Get)',temp.monitorId,response.mid) + next() + }) + }, + "/monitor/[MONITOR_ID]/[MODE] (Mode Switch to Disabled)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitor',administratorAccountData.ke) + temp.monitorId + '/stop' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /monitor/[MONITOR_ID] (Mode Switch to Disabled)',true,response.ok) + next() + }) + }, + "/monitor/[MONITOR_ID]/[MODE] (Mode Switch to Watch-Only)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitor',administratorAccountData.ke) + temp.monitorId + '/start' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /monitor/[MONITOR_ID] (Mode Switch to Watch-Only)',true,response.ok) + next() + }) + }, + "/monitor/[MONITOR_ID]/[MODE] (Mode Switch to Record)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitor',administratorAccountData.ke) + temp.monitorId + '/record' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + checkResult('API : /monitor/[MONITOR_ID] (Mode Switch to Record)',true,response.ok) + next() + }) + }, + "/monitor (Get All)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitor',administratorAccountData.ke) + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(!checkResult('API : /monitor (Get All)',2,response.length)){ + console.log(Object.keys(response)) + } + next() + }) + }, + "/configureMonitor (Delete)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'configureMonitor',administratorAccountData.ke) + temp.monitorId2 + '/delete' + request.post(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /configureMonitor (Delete)',true,response.ok) + next() + }) + }, + "/monitorStates Insert (Disable + Detector Off)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'DisableWithDetectorOff/insert' + request.post(builtURL,{ + form: { + data: { + "monitors": [ + { + "mode":"stop", + "mid":temp.monitorId, + "details": { + "detector": "0" + } + } + ] + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Insert (Disable + Detector Off)',true,response.ok) + next() + }) + }, + "/monitorStates Insert (Enable + Detector On)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'EnableWithDetectorOn/insert' + request.post(builtURL,{ + form: { + data: { + "monitors": [ + { + "mode":"start", + "mid":temp.monitorId, + "details": { + "detector": "1" + } + } + ] + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Insert (Enable + Detector On)',true,response.ok) + next() + }) + }, + "/monitorStates Insert (Continuous Recording)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'RecordOnly/insert' + request.post(builtURL,{ + form: { + data: { + "monitors": [ + { + "mode":"record", + "mid":temp.monitorId, + "details": { + "detector": "0" + } + } + ] + } + } + },function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Insert (Continuous Recording)',true,response.ok) + next() + }) + }, + "/monitorStates List" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates List',true,response.ok) + next() + }) + }, + "/monitorStates Run Action (Disable + Detector Off)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'DisableWithDetectorOff' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Run Action (Disable + Detector Off)',true,response.ok) + next() + }) + }, + "/monitorStates Run Action (Enable + Detector On)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'EnableWithDetectorOn' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Run Action (Enable + Detector On)',true,response.ok) + next() + }) + }, + "/monitorStates Run Action (Continuous Recording)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'RecordOnly' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Run Action (Continuous Recording)',true,response.ok) + next() + }) + }, + "/monitorStates Delete (Disable + Detector Off)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'DisableWithDetectorOff/delete' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Delete (Disable + Detector Off)',true,response.ok) + next() + }) + }, + "/monitorStates Delete (Enable + Detector On)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'EnableWithDetectorOn/delete' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Delete (Enable + Detector On)',true,response.ok) + next() + }) + }, + "/monitorStates Delete (Continuous Recording)" : function(next){ + var userData = getAdministratorAccountData() + var builtURL = buildRegularApiRequestURL(administratorAccountData.auth,'monitorStates',administratorAccountData.ke) + 'RecordOnly/delete' + request.get(builtURL,function(err, httpResponse, body){ + var response = s.parseJSON(body) + if(response.ok !== true)console.log(builtURL,response) + checkResult('API : /monitorStates Delete (Continuous Recording)',true,response.ok) + next() + }) + }, } } console.log('----- Function Test Starting') - Object.keys(test).forEach(function(libkey){ - var library = test[libkey] - console.log('--- Testing ' + libkey + '...') - Object.keys(library).forEach(function(key){ - var functionTest = library[key] - functionTest() - }) - console.log('-- Completed ' + libkey + '...') + var completedGroups = 0 + var testGroupKeys = Object.keys(test) + var testGroupRunLoop = function(callback){ + var tableName = testGroupKeys[completedGroups] + var testers = test[testGroupKeys[completedGroups]] + if(tableName){ + console.log('--- Testing ' + tableName + '...') + // test functions > + var completedFunctions = 0 + var testFunctionsKeys = Object.keys(testers) + var testFunctionRunLoop = function(innerCallback){ + var functioName = testFunctionsKeys[completedFunctions] + var theFunction = testers[testFunctionsKeys[completedFunctions]] + if(functioName){ + theFunction(function(){ + ++completedFunctions + testFunctionRunLoop(innerCallback) + }) + }else{ + innerCallback() + } + } + testFunctionRunLoop(function(){ + console.log('-- Completed ' + tableName + '...') + ++completedGroups + testGroupRunLoop(callback) + }) + // test functions /> + }else{ + callback() + } + } + testGroupRunLoop(function(){ + console.log('---- Function Test Ended') }) - console.log('---- Function Test Ended') } diff --git a/test/testAdminUser.json b/test/testAdminUser.json deleted file mode 100644 index b23e9f5..0000000 --- a/test/testAdminUser.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "mail": "user@shinobi.video", - "ke": "", - "pass": "password", - "password_again": "password", - "details": "{\"factorAuth\":\"0\",\"size\":\"\",\"days\":\"\",\"event_days\":\"\",\"log_days\":\"\",\"max_camera\":\"\",\"permissions\":\"all\",\"edit_size\":\"1\",\"edit_days\":\"1\",\"edit_event_days\":\"1\",\"edit_log_days\":\"1\",\"use_admin\":\"1\",\"use_aws_s3\":\"1\",\"use_webdav\":\"1\",\"use_discordbot\":\"1\",\"use_ldap\":\"1\"}" -} diff --git a/test/testApiAdd.json b/test/testApiAdd.json deleted file mode 100644 index 4ca075a..0000000 --- a/test/testApiAdd.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "data": { - "ip": "[IP ADDRESS]", - "details": { - "auth_socket": "1", - "get_monitors": "1", - "control_monitors": "1", - "get_logs": "1", - "watch_stream": "1", - "watch_snapshot": "1", - "watch_videos": "1", - "delete_videos": "1" - } - } -} diff --git a/test/testApiDelete.json b/test/testApiDelete.json deleted file mode 100644 index 977a20b..0000000 --- a/test/testApiDelete.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "data": { - "code": "[API KEY]" - } -} diff --git a/web/libs/css/dash2.basic.css b/web/libs/css/dash2.basic.css new file mode 100644 index 0000000..0e96231 --- /dev/null +++ b/web/libs/css/dash2.basic.css @@ -0,0 +1,89 @@ +/*Cusotm Bootstrap*/ +.col-5ths, +.col-sm-5ths, +.col-md-5ths, +.col-lg-5ths { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; +} + +@media (min-width: 576px) { + .col-sm-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} + +@media (min-width: 768px) { + .col-md-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} +::-webkit-scrollbar-thumb:hover { + background-color:#bd9565; +} +::-webkit-scrollbar-thumb { + background-color:#bd9565; + border: 2px solid transparent; + border-radius: 10px; + background-clip: padding-box; +} +/**/ +.flex{display:flex} +.flex>div{flex:1} +.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} +.flex-unit-3{flex:3} +.flex-inline{display: inline-flex;position:relative} +@import (less) "../less/pie.less"; +ul{list-style:none} +*{transition:0.2s;box-sizing:border-box} +.affix-top{position:fixed} +.no-padding{padding:0!important} +.no-margin{margin:0!important} +.pre-inline{white-space: normal;word-break: normal} +.pre-inline>ul{margin:0;padding:0} +a{cursor:pointer} +nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} +.m-r{margin-right:10px} +.m-b{margin-bottom:10px} +.m-t{margin-top:10px} +.m-l{margin-left:10px} +.overflow-hidden{overflow: hidden!important} +.list-inline{list-style:none} +.list-inline li{display:inline-block;vertical-align: top;margin:0;} +.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +img{max-width:100%} +.display-table{display:table;width:100%} +.display-inline{display:inline-block} +.display-table-cell{display:table-cell} +.small{font-size:80%} +.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} +.permission_monitor_edit{display:none} +.permission_video_delete{display:none} +.nodata .divider{margin:5px 0} +.loading .divider{margin:5px 0} +/* Video Grid */ +.video_grid{overflow: auto;height: 100%;display: block;} +.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} +.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} +.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} +.video_grid .thumb .title-strip{top:0;opacity:0.5} +.video_grid .thumb .button-strip{bottom:0;opacity:0} +.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} diff --git a/web/libs/css/dash2.forms.css b/web/libs/css/dash2.forms.css new file mode 100644 index 0000000..a1d0ffe --- /dev/null +++ b/web/libs/css/dash2.forms.css @@ -0,0 +1,44 @@ +form.modal-body{margin:0} +.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} +.form-group label{display:table} +.form-group label>div{display:table-cell} +.form-group label>div:nth-child(2n-1){width:30%} +.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} +.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} +.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} +.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} +.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} +.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} +.form-group-group blockquote{letter-spacing:normal;font-style:normal} +.form-group-group blockquote p:empty{display:none} +.form-group-group blockquote p{font-size:inherit} +.form-group-group blockquote p:last-child{margin-bottom:0} +.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} +.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} +.form-group-group table{width:100%} +.form-group-group table tr td{padding:10px 5px} +.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} +.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} +.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} +.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} +.dark .form-group-group .mdl-list__item:hover{background:#555;} +.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} +.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} +.form-group-group h4 small{color:#fff;} +.form-group-group.red{border-color:#d9534f} +.form-group-group.red h4{background:#d9534f;color:#fff} +.form-group-group.purple{border-color:#3f51b5} +.form-group-group.purple h4{background:#3f51b5;color:#fff} +.form-group-group.blue{border-color:#337ab7} +.form-group-group.blue h4{background:#337ab7;color:#fff} +.form-group-group.navy{border-color:#31708f} +.form-group-group.navy h4{background:#31708f;color:#fff} +.form-group-group.green{border-color:#449d44} +.form-group-group.green h4{background:#449d44;color:#fff} +.form-group-group.forestgreen{border-color:#1e4046} +.form-group-group.forestgreen h4{background:#1e4046;color:#fff} +.form-group-group.orange{border-color:#c49a68} +.form-group-group.orange h4{background:#c49a68;color:#fff} +.form-group-group.grey{border-color:#777} +.form-group-group.grey h4{background:#777;color:#fff} +.dark .form-group-group{background:#222} diff --git a/web/libs/css/dash2.modal.css b/web/libs/css/dash2.modal.css new file mode 100644 index 0000000..b0a2cc2 --- /dev/null +++ b/web/libs/css/dash2.modal.css @@ -0,0 +1,6 @@ +.dark.modal .modal-header,.dark.modal .modal-footer{background:#333;border-color:#444;} +.dark.modal .modal-header{color:#fff;} +.dark.modal .modal-footer>*:not(.btn){color:#fff;} +.dark.modal .modal-body{background:#333;} +.dark.modal .close{color:#fff;} +.dark.modal{color:#fff;} diff --git a/web/libs/css/dash2.monitors.css b/web/libs/css/dash2.monitors.css new file mode 100644 index 0000000..c69ccb3 --- /dev/null +++ b/web/libs/css/dash2.monitors.css @@ -0,0 +1,86 @@ + +.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} +.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} + +.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:10} +.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} +.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} +.monitor_item .stream-hud:hover{opacity:1} +.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:70%;padding:5px;text-shadow: 0 0 10px #333;} +.monitor_item .stream-hud:hover .bottom-text{top:0;} +.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} +.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} +.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} +.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} +.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} +.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} +/*.data-menu{max-height:700px}*/ +.data-menu:not(:last-child){border-right:1px solid #fff;} +.data-menu.logs{list-style:none;} +.monitor_item .motionVision{display:none} + +.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} +.monitor_item .ui-resizable-se {bottom: 10px!important;} +.monitor_item .stream-block{position: relative;text-align: center} +.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} +.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} + + +.monitor_item.fullscreen img.stream-element{height:100%;width:auto} +.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} +.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} +.monitor_item{position:relative;padding:0;transition:none;background:#000} +.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} +.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;} +.monitor_item.selected .stream-element{height:600px} +.monitor_item.selected .fa-expand:before{content:"\f066"} +.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} +.monitor_item.detector_triggered .detector-fade{opacity:1} +.monitor_item .detector-fade{opacity:0} +.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} +.monitor_item .progress{width:100%;background:#333;box-shadow:0;} +.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} +.hide_indifference .indifference{display:none!important} +.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} +.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} +.monitor_item[mode="Record"] [mode="record"]{display:none} +.monitor_item[mode="Watch Only"] [mode="start"]{display:none} +.monitor_item .stream-hud .controls .btn{opacity:0.7} +.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} + +.data-menu{text-align:left} +.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} +.data-menu li,.side-menu li:not(.mdl-menu__item){ + border-bottom:1px solid #54502d;padding:10px; +} +.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} +.data-menu .progress-circle span:after{content:''} +img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} +.circle-img.sm{height:25px;width:25px} + +@media screen and (max-width:1500px){ + .monitor_item .mdl-card__supporting-text .btn{ + padding: 5px 10px; + font-size: 11px; + line-height: 1.5; + } +} + +#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} +#monitors_live .monitor_item .indifference{top:-5px} +#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} +#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} +#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} +#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;overflow:visible} +#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} +#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} + +.signal.green{background:#5cb85c} +[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} +[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} +.signal.red{background:#c9302c} diff --git a/web/libs/css/dash2.powervideo.css b/web/libs/css/dash2.powervideo.css new file mode 100644 index 0000000..624a3be --- /dev/null +++ b/web/libs/css/dash2.powervideo.css @@ -0,0 +1,8 @@ +#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} +#pvideo_viewer video{max-height:300px;max-width:100%;} +#pvideo_viewer .holder{height:300px;} +#pvideo_viewer h3{margin-top:0} +#pvideo_viewer .progressBar{position:relative;} +#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} +#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} +#pvideo_viewer h3{font-family:monospace} diff --git a/web/libs/css/dash2.ptzcontrols.css b/web/libs/css/dash2.ptzcontrols.css new file mode 100644 index 0000000..0ecee29 --- /dev/null +++ b/web/libs/css/dash2.ptzcontrols.css @@ -0,0 +1,89 @@ +/*Control Pad*/ +.PTZ_controls { + z-index: 111; + position: absolute; + left: 20px; + top: 20px; + margin:0; + display: inline-block; + width: 120px; +} +.PTZ_controls .btn-group{margin-top:10px} +.PTZ_controls .pad { + position: relative; + height: 120px; + width: 120px; + background: #b7b7b7; + border-radius: 50%; + box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); +} +.PTZ_controls .control { + position: absolute; +} +.PTZ_controls .pad .control { + height: 30px; + width: 30px; + background: #636363; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); + border-radius: 2px; +} +.PTZ_controls .zoom_in{ + top: 0; + right: 0; +} +.PTZ_controls .zoom_out{ + bottom: 0; + right: 0; +} +.PTZ_controls .nv_enabled{ + top: 0; + right: 0; +} +.PTZ_controls .nv_disable{ + bottom: 0; + right: 0; +} +.PTZ_controls .pad .top { + top: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +.PTZ_controls .pad .left { + top: 45px; + left: 15px; +} +.PTZ_controls .pad .right { + top: 45px; + right: 15px; +} +.PTZ_controls .pad .control.right:before { + transform: rotate(90deg) translate(-3px, -5px); +} +.PTZ_controls .pad .bottom { + bottom: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +/* Overlap the other controls to hide box-shadow */ +.PTZ_controls .pad .middle { + height: 34px; + width: 34px; + z-index: 5; + top: 43px; + left: 50%; + margin: 0 0 0 -17px; + box-shadow: none; + border-radius: 3px; +} +.PTZ_controls .pad .middle:after { + position: absolute; + top: 50%; + left: 50%; + margin: -35% 0 0 -35%; + content: ''; + background: #636363; + height: 70%; + width: 70%; + border-radius: 100%; + box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); +} diff --git a/web/libs/css/dash2.regioneditor.css b/web/libs/css/dash2.regioneditor.css new file mode 100644 index 0000000..09d3ee6 --- /dev/null +++ b/web/libs/css/dash2.regioneditor.css @@ -0,0 +1,5 @@ +#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} +#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} +#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} +#region_editor .cord_element.selected{z-index:12;} +#region_editor .cord_element .controls{margin-bottom:5px;} diff --git a/web/libs/css/dash2.righttoleft.css b/web/libs/css/dash2.righttoleft.css new file mode 100644 index 0000000..39d59a4 --- /dev/null +++ b/web/libs/css/dash2.righttoleft.css @@ -0,0 +1,11 @@ +.right-to-left {text-align:right} +.right-to-left select{direction: rtl;} +.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} +.right-to-left .form-group label span{padding-right:10px} +.right-to-left .modal-footer{text-align:left} +.right-to-left .mdl-menu__item>div>*{flex:1} +.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} +.right-to-left .mdl-menu__item{text-align:right} +.right-to-left .mdl-menu__item i{float:right} +.right-to-left .pull-right,.right-to-left .close{float:left!important} +.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} diff --git a/web/libs/css/dash2.timelapse.css b/web/libs/css/dash2.timelapse.css new file mode 100644 index 0000000..924bc2f --- /dev/null +++ b/web/libs/css/dash2.timelapse.css @@ -0,0 +1,13 @@ +#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} +#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} +.timelapse_video:not(:last-child){border-bottom:1px solid #444;} +.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} +.timelapse_video>div>div:not(:last-child){padding-right:10px} +.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} +.timelapse_video.list-group-item{padding:10px} +.timelapse_hud{position: relative;background:#000} +.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} +#timelapse .progress-bar{transition:0.5s!important} +.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} +.timelapse_hud .hover-hide{opacity:0} +.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} diff --git a/web/libs/css/main.dash2.css b/web/libs/css/main.dash2.css index a62515c..b04b37b 100644 --- a/web/libs/css/main.dash2.css +++ b/web/libs/css/main.dash2.css @@ -1,87 +1,3 @@ -/*Cusotm Bootstrap*/ -.col-5ths, -.col-sm-5ths, -.col-md-5ths, -.col-lg-5ths { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} - -.col-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; -} - -@media (min-width: 576px) { - .col-sm-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } -} - -@media (min-width: 768px) { - .col-md-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } -} -::-webkit-scrollbar-thumb:hover { - background-color:#bd9565; -} -::-webkit-scrollbar-thumb { - background-color:#bd9565; - border: 2px solid transparent; - border-radius: 10px; - background-clip: padding-box; -} -/**/ -.flex{display:flex} -.flex>div{flex:1} -.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} -.flex-unit-3{flex:3} -.flex-inline{display: inline-flex;position:relative} -@import (less) "../less/pie.less"; -ul{list-style:none} -*{transition:0.2s;box-sizing:border-box} -.affix-top{position:fixed} -.no-padding{padding:0!important} -.no-margin{margin:0!important} -.pre-inline{white-space: normal;word-break: normal} -.pre-inline>ul{margin:0;padding:0} -a{cursor:pointer} -nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} -.m-r{margin-right:10px} -.m-b{margin-bottom:10px} -.m-t{margin-top:10px} -.m-l{margin-left:10px} -.overflow-hidden{overflow: hidden!important} -.list-inline{list-style:none} -.list-inline li{display:inline-block;vertical-align: top;margin:0;} -.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} -img{max-width:100%} -.display-table{display:table;width:100%} -.display-inline{display:inline-block} -.display-table-cell{display:table-cell} -.small{font-size:80%} -.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} -.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} -.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} -.permission_monitor_edit{display:none} -.permission_video_delete{display:none} -.nodata .divider{margin:5px 0} -.loading .divider{margin:5px 0} - #accbtn{ text-overflow: ellipsis; white-space: nowrap; @@ -89,75 +5,6 @@ img{max-width:100%} padding: 0; } -.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2} -.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} -.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} -.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} -.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} -.monitor_item .stream-hud:hover{opacity:1} -.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:100%;padding:5px;text-shadow: 0 0 10px #333;} -.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} -.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} -.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} -.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} -.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} -.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} -/*.data-menu{max-height:700px}*/ -.data-menu:not(:last-child){border-right:1px solid #fff;} -.data-menu.logs{list-style:none;} -.monitor_item .motionVision{display:none} - -.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} -.monitor_item .ui-resizable-se {bottom: 10px!important;} -.monitor_item .stream-block{position: relative;text-align: center} -.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} -.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} - - -.monitor_item.fullscreen img.stream-element{height:100%;width:auto} -.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} -.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} -.monitor_item{position:relative;padding:0;transition:none;background:#000} -.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} -.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;} -.monitor_item.selected .stream-element{height:600px} -.monitor_item.selected .fa-expand:before{content:"\f066"} -.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} -.monitor_item.detector_triggered .detector-fade{opacity:1} -.monitor_item .detector-fade{opacity:0} -.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} -.monitor_item .progress{width:100%;background:#333;box-shadow:0;} -.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} -.hide_indifference .indifference{display:none!important} -.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} -.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} -.monitor_item[mode="Record"] [mode="record"]{display:none} -.monitor_item[mode="Watch Only"] [mode="start"]{display:none} -.monitor_item .stream-hud .controls .btn{opacity:0.7} -.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} - -@media screen and (max-width:1500px){ - .monitor_item .mdl-card__supporting-text .btn{ - padding: 5px 10px; - font-size: 11px; - line-height: 1.5; - } -} - -#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} -#monitors_live .monitor_item .indifference{top:-5px} -#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} -#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} -#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} -#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;} -#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} -#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} -#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} -#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} -#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} - - - #vis_pwrvideo{height:250px} #monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;} #monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none} @@ -169,19 +16,12 @@ img{max-width:100%} .demo-blog .demo-blog__posts.montage{max-width:100%} - +.mdl-layout__drawer{overflow-y: visible!important} +.hide-side .mdl-layout__drawer{overflow-y: hidden} .mdl-layout__header-row{padding-left:10!important} .mdl-layout__header-row .nav>li>a{border-radius:50%;} .mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff} -.data-menu{text-align:left} -.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} -.data-menu li,.side-menu li{ - border-bottom:1px solid #54502d;padding:10px; -} -.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} -.data-menu .progress-circle span:after{content:''} -img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} -.circle-img.sm{height:25px;width:25px} + .video_video{margin:auto;max-width:100%;max-height:600px;} #confirm_window .video_video{margin-top:15px} @@ -200,60 +40,9 @@ img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} .flex-container-modal-body .flex-block>div{flex:1;float:none} .modal{overflow:auto!important} -form.modal-body{margin:0} -#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} -#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} -#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} -#region_editor .cord_element.selected{z-index:12;} -#region_editor .cord_element .controls{margin-bottom:5px;} -.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} -.form-group label{display:table} -.form-group label>div{display:table-cell} -.form-group label>div:nth-child(2n-1){width:30%} -.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} -.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} -.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} -.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} -.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} -.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} -.form-group-group blockquote{letter-spacing:normal;font-style:normal} -.form-group-group blockquote p:empty{display:none} -.form-group-group blockquote p{font-size:inherit} -.form-group-group blockquote p:last-child{margin-bottom:0} -.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} -.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} -.form-group-group table{width:100%} -.form-group-group table tr td{padding:10px 5px} -.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} -.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} -.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} -.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} -.dark .form-group-group .mdl-list__item:hover{background:#555;} -.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} -.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} -.form-group-group h4 small{color:#fff;} -.form-group-group.red{border-color:#d9534f} -.form-group-group.red h4{background:#d9534f;color:#fff} -.form-group-group.purple{border-color:#3f51b5} -.form-group-group.purple h4{background:#3f51b5;color:#fff} -.form-group-group.blue{border-color:#337ab7} -.form-group-group.blue h4{background:#337ab7;color:#fff} -.form-group-group.navy{border-color:#31708f} -.form-group-group.navy h4{background:#31708f;color:#fff} -.form-group-group.green{border-color:#449d44} -.form-group-group.green h4{background:#449d44;color:#fff} -.form-group-group.forestgreen{border-color:#1e4046} -.form-group-group.forestgreen h4{background:#1e4046;color:#fff} -.form-group-group.orange{border-color:#c49a68} -.form-group-group.orange h4{background:#c49a68;color:#fff} -.form-group-group.grey{border-color:#777} -.form-group-group.grey h4{background:#777;color:#fff} -.dark .form-group-group{background:#222} + .videos_list .title{font-size:12pt;padding:0 10px} -[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} -[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} -.signal.red{background:#c9302c} -.signal.green{background:#5cb85c} + .demo-drawer{background:#2b2a2a;color:#fff;} .demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;} .demo-drawer::-webkit-scrollbar{display:none;} @@ -285,12 +74,14 @@ form.modal-body{margin:0} .nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;} .side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block} .nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none} +.side-menu .mdl-menu{z-index: 12} + #monitors_list .monitor_block{transition:none} .dropdown-menu.scrollable{max-height:300px} .upload_file input{display:none} #video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto} .stream-block,.stream-objects{overflow: hidden!important} -.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1} +.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10} .stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;} .stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px} .stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px} @@ -306,39 +97,11 @@ form.modal-body{margin:0} .form-group label{width:100%} -#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} -#pvideo_viewer video{max-height:300px;max-width:100%;} -#pvideo_viewer .holder{height:300px;} -#pvideo_viewer h3{margin-top:0} -#pvideo_viewer .progressBar{position:relative;} -#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} -#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} -#pvideo_viewer h3{font-family:monospace} + #vis_monitors{overflow:auto;max-height:400px} #vis_monitors .btn-group-vertical{width:100%} -/*timeline*/ -#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} -#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} -.timelapse_video:not(:last-child){border-bottom:1px solid #444;} -.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} -.timelapse_video>div>div:not(:last-child){padding-right:10px} -.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} -.timelapse_video.list-group-item{padding:10px} -.timelapse_hud{position: relative;background:#000} -.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} -#timelapse .progress-bar{transition:0.5s!important} -.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} -.timelapse_hud .hover-hide{opacity:0} -.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} -.video_grid{overflow: auto;height: 100%;display: block;} -.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} -.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} -.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} -.video_grid .thumb .title-strip{top:0;opacity:0.5} -.video_grid .thumb .button-strip{bottom:0;opacity:0} -.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} .table-striped>tbody>tr>td{vertical-align:middle} .table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;} @@ -352,13 +115,6 @@ form.modal-body{margin:0} background-color: #c49a68; border-color: #c49a68; } - -.dark.modal .modal-header,.dark.modal .modal-footer{background:#333;border-color:#444;} -.dark.modal .modal-header{color:#fff;} -.dark.modal .modal-footer>*:not(.btn){color:#fff;} -.dark.modal .modal-body{background:#333;} -.dark.modal .close{color:#fff;} -.dark.modal{color:#fff;} .dark .table-striped>tbody>tr:nth-of-type(even){background:#616161} .dark .table-striped>tbody>tr>td{border-color:#222;color:#fff} .dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;} @@ -481,95 +237,10 @@ ul.msg_list li .message { .mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{ margin-left: 0px;width:100%;transition:0.2s } -/*Control Pad*/ -.PTZ_controls { - z-index: 111; - position: absolute; - left: 20px; - top: 20px; - margin:0; - display: inline-block; - width: 120px; -} -.PTZ_controls .btn-group{margin-top:10px} -.PTZ_controls .pad { - position: relative; - height: 120px; - width: 120px; - background: #b7b7b7; - border-radius: 50%; - box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); -} -.PTZ_controls .control { - position: absolute; -} -.PTZ_controls .pad .control { - height: 30px; - width: 30px; - background: #636363; - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); - border-radius: 2px; -} -.PTZ_controls .zoom_in{ - top: 0; - right: 0; -} -.PTZ_controls .zoom_out{ - bottom: 0; - right: 0; -} -.PTZ_controls .nv_enabled{ - top: 0; - right: 0; -} -.PTZ_controls .nv_disable{ - bottom: 0; - right: 0; -} -.PTZ_controls .pad .top { - top: 15px; - left: 50%; - margin: 0 0 0 -15px; -} -.PTZ_controls .pad .left { - top: 45px; - left: 15px; -} -.PTZ_controls .pad .right { - top: 45px; - right: 15px; -} -.PTZ_controls .pad .control.right:before { - transform: rotate(90deg) translate(-3px, -5px); -} -.PTZ_controls .pad .bottom { - bottom: 15px; - left: 50%; - margin: 0 0 0 -15px; -} -/* Overlap the other controls to hide box-shadow */ -.PTZ_controls .pad .middle { - height: 34px; - width: 34px; - z-index: 5; - top: 43px; - left: 50%; - margin: 0 0 0 -17px; - box-shadow: none; - border-radius: 3px; -} -.PTZ_controls .pad .middle:after { - position: absolute; - top: 50%; - left: 50%; - margin: -35% 0 0 -35%; - content: ''; - background: #636363; - height: 70%; - width: 70%; - border-radius: 100%; - box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); -} +.mdl-menu__item>div{display:flex;align-items: center;width:100%} +.mdl-menu__item>div>i{margin-right:5px} + + /*Digital Zoom*/ .stream-block{ position: relative; @@ -663,20 +334,7 @@ ul.msg_list li .message { -moz-animation: blink 1s linear infinite; animation: blink 1s linear infinite; } -.mdl-menu__item>div{display:flex;align-items: center;width:100%} -.mdl-menu__item>div>i{margin-right:5px} -/*For languages that are right to left*/ -.right-to-left {text-align:right} -.right-to-left select{direction: rtl;} -.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} -.right-to-left .form-group label span{padding-right:10px} -.right-to-left .modal-footer{text-align:left} -.right-to-left .mdl-menu__item>div>*{flex:1} -.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} -.right-to-left .mdl-menu__item{text-align:right} -.right-to-left .mdl-menu__item i{float:right} -.right-to-left .pull-right,.right-to-left .close{float:left!important} -.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} + /* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */ .marc-toggle { width: 50px; diff --git a/web/libs/css/main.dash2.old.css b/web/libs/css/main.dash2.old.css new file mode 100644 index 0000000..63e9d69 --- /dev/null +++ b/web/libs/css/main.dash2.old.css @@ -0,0 +1,742 @@ +/*Cusotm Bootstrap*/ +.col-5ths, +.col-sm-5ths, +.col-md-5ths, +.col-lg-5ths { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; +} + +@media (min-width: 576px) { + .col-sm-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} + +@media (min-width: 768px) { + .col-md-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} +::-webkit-scrollbar-thumb:hover { + background-color:#bd9565; +} +::-webkit-scrollbar-thumb { + background-color:#bd9565; + border: 2px solid transparent; + border-radius: 10px; + background-clip: padding-box; +} +/**/ +.flex{display:flex} +.flex>div{flex:1} +.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} +.flex-unit-3{flex:3} +.flex-inline{display: inline-flex;position:relative} +@import (less) "../less/pie.less"; +ul{list-style:none} +*{transition:0.2s;box-sizing:border-box} +.affix-top{position:fixed} +.no-padding{padding:0!important} +.no-margin{margin:0!important} +.pre-inline{white-space: normal;word-break: normal} +.pre-inline>ul{margin:0;padding:0} +a{cursor:pointer} +nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} +.m-r{margin-right:10px} +.m-b{margin-bottom:10px} +.m-t{margin-top:10px} +.m-l{margin-left:10px} +.overflow-hidden{overflow: hidden!important} +.list-inline{list-style:none} +.list-inline li{display:inline-block;vertical-align: top;margin:0;} +.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +img{max-width:100%} +.display-table{display:table;width:100%} +.display-inline{display:inline-block} +.display-table-cell{display:table-cell} +.small{font-size:80%} +.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} +.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} +.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} +.permission_monitor_edit{display:none} +.permission_video_delete{display:none} +.nodata .divider{margin:5px 0} +.loading .divider{margin:5px 0} + +#accbtn{ + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding: 0; +} + +.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:10} +.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} +.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} +.monitor_item .stream-hud:hover{opacity:1} +.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:100%;padding:5px;text-shadow: 0 0 10px #333;} +.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} +.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} +.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} +.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} +.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} +.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} +/*.data-menu{max-height:700px}*/ +.data-menu:not(:last-child){border-right:1px solid #fff;} +.data-menu.logs{list-style:none;} +.monitor_item .motionVision{display:none} + +.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} +.monitor_item .ui-resizable-se {bottom: 10px!important;} +.monitor_item .stream-block{position: relative;text-align: center} +.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} +.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} + + +.monitor_item.fullscreen img.stream-element{height:100%;width:auto} +.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} +.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} +.monitor_item{position:relative;padding:0;transition:none;background:#000} +.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} +.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;height:100%} +.monitor_item.selected .stream-element{height:600px} +.monitor_item.selected .fa-expand:before{content:"\f066"} +.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} +.monitor_item.detector_triggered .detector-fade{opacity:1} +.monitor_item .detector-fade{opacity:0} +.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} +.monitor_item .progress{width:100%;background:#333;box-shadow:0;} +.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} +.hide_indifference .indifference{display:none!important} +.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} +.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} +.monitor_item[mode="Record"] [mode="record"]{display:none} +.monitor_item[mode="Watch Only"] [mode="start"]{display:none} +.monitor_item .stream-hud .controls .btn{opacity:0.7} +.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} + +@media screen and (max-width:1500px){ + .monitor_item .mdl-card__supporting-text .btn{ + padding: 5px 10px; + font-size: 11px; + line-height: 1.5; + } +} + +#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} +#monitors_live .monitor_item .indifference{top:-5px} +#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} +#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} +#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} +#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;overflow:visible;} +#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} +#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} + + + +#vis_pwrvideo{height:250px} +#monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;} +#monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none} +#region_editor_live iframe,.canvas_holder canvas{border:0;position:absolute;left:0;top:0} +.canvas_holder canvas{z-index:11} + +.demo-blog .mdl-card__media ~ .mdl-card__supporting-text{position:relative;overflow:initial;cursor:move} +.demo-blog .mdl-card__media ~ .mdl-card__supporting-text .btn-group{cursor: default} + +.demo-blog .demo-blog__posts.montage{max-width:100%} + + +.mdl-layout__header-row{padding-left:10!important} +.mdl-layout__header-row .nav>li>a{border-radius:50%;} +.mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff} +.data-menu{text-align:left} +.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} +.data-menu li,.side-menu li{ + border-bottom:1px solid #54502d;padding:10px; +} +.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} +.data-menu .progress-circle span:after{content:''} +img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} +.circle-img.sm{height:25px;width:25px} + +.video_video{margin:auto;max-width:100%;max-height:600px;} +#confirm_window .video_video{margin-top:15px} +#confirm_window .info-table{margin-top:15px} +@media (max-width: 768px){ + .full.modal .modal-body,.medium.modal .modal-body{max-height:400px;overflow:auto} +} +@media (min-width: 768px){ + .modal.full,.modal.medium{padding-left:0!important;} + .modal.full .modal-dialog{width:calc(100% - 10px)!important;margin: 30px auto;} + .modal.medium .modal-dialog{width:calc(70% - 10px)!important;margin: 30px auto;} + .full.modal .modal-body,.medium.modal .modal-body{height:calc(100% - 200px);overflow:auto} +} + +.flex-container-modal-body{overflow: auto} +.flex-container-modal-body .flex-block>div{flex:1;float:none} + +.modal{overflow:auto!important} +form.modal-body{margin:0} +#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} +#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} +#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} +#region_editor .cord_element.selected{z-index:12;} +#region_editor .cord_element .controls{margin-bottom:5px;} +.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} +.form-group label{display:table} +.form-group label>div{display:table-cell} +.form-group label>div:nth-child(2n-1){width:30%} +.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} +.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} +.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} +.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} +.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} +.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} +.form-group-group blockquote{letter-spacing:normal;font-style:normal} +.form-group-group blockquote p:empty{display:none} +.form-group-group blockquote p{font-size:inherit} +.form-group-group blockquote p:last-child{margin-bottom:0} +.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} +.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} +.form-group-group table{width:100%} +.form-group-group table tr td{padding:10px 5px} +.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} +.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} +.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} +.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} +.dark .form-group-group .mdl-list__item:hover{background:#555;} +.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} +.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} +.form-group-group h4 small{color:#fff;} +.form-group-group.red{border-color:#d9534f} +.form-group-group.red h4{background:#d9534f;color:#fff} +.form-group-group.purple{border-color:#3f51b5} +.form-group-group.purple h4{background:#3f51b5;color:#fff} +.form-group-group.blue{border-color:#337ab7} +.form-group-group.blue h4{background:#337ab7;color:#fff} +.form-group-group.navy{border-color:#31708f} +.form-group-group.navy h4{background:#31708f;color:#fff} +.form-group-group.green{border-color:#449d44} +.form-group-group.green h4{background:#449d44;color:#fff} +.form-group-group.forestgreen{border-color:#1e4046} +.form-group-group.forestgreen h4{background:#1e4046;color:#fff} +.form-group-group.orange{border-color:#c49a68} +.form-group-group.orange h4{background:#c49a68;color:#fff} +.form-group-group.grey{border-color:#777} +.form-group-group.grey h4{background:#777;color:#fff} +.dark .form-group-group{background:#222} +.videos_list .title{font-size:12pt;padding:0 10px} +[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} +[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} +.signal.red{background:#c9302c} +.signal.green{background:#5cb85c} +.demo-drawer{background:#2b2a2a;color:#fff;} +.demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;} +.demo-drawer::-webkit-scrollbar{display:none;} +.small-square-img{height:40px;width:40px;border-radius:5px} + +.side-menu .monitor_block{padding:0;position:relative} +.side-menu .monitor_block img{width:100%;height:75px;cursor:pointer;border: 0.5px inset #263238;} +@media screen and (max-width:1025px){ +.side-menu .monitor_block img{height:175px;} +} +.side-menu .monitor_block:hover .icons{opacity:1} +.side-menu .monitor_block:hover .title{opacity:1} +.side-menu .monitor_block .icons,.side-menu .monitor_block .title{opacity:0;width:100%;bottom:0;left:0;background:rgba(0,0,0,0.6);position:absolute;padding:2.5px;z-index:11;cursor:move} +.side-menu .monitor_block .title{bottom:auto;top:0;color:#fff} +.nav-xs.side-menu .monitor_block{width:100%} +.side-menu .monitor_block .list-data{display:none} +.output_data:empty{display:none} +.output_data{max-height:500px;font-family:monospace;padding:10px;border-radius:5px;background:#f3f3f3;overflow:auto} +.dark .output_data{background:#222;} +#probe .output_data div>div{margin-left:10px} +.side-menu.list-blocks .monitor_block .icons,.side-menu.list-blocks .monitor_block .title{position:inherit;opacity:1;background:none} +.side-menu.list-blocks .monitor_block .title{padding:5px;border-radius:5px;background:#222;} +.side-menu.list-blocks .monitor_block:not(:last-child){border-bottom: 1px solid #54502d;} +.side-menu.list-blocks .monitor_block:first-child{border-top: 1px solid #54502d;} +.side-menu.list-blocks .monitor_block{float:none;width:100%;padding: 10px} +.side-menu.list-blocks .monitor_block.ui-sortable-helper{background:rgba(0,0,0,0.6);border-radius:5px;padding:5px;border:0} +.side-menu.list-blocks .monitor_block .list-data{display:block} +.side-menu.list-blocks .monitor_block img{width:60px;height:60px;cursor:pointer;display:inline-block;margin-right:10px;border-radius:50%;vertical-align:top;border:0} +.nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;} +.side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block} +.nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none} +#monitors_list .monitor_block{transition:none} +.dropdown-menu.scrollable{max-height:300px} +.upload_file input{display:none} +#video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto} +.stream-block,.stream-objects{overflow: hidden!important} +.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10} +.stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;} +.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px} +.stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px} +.stream-objects .point{position:absolute;top:0;left:0;border:3px solid red;border-radius:50%} + + +#side_menu_right.nav-xs{width:0!important;overflow:hidden} +.side-menu table{color:#fff;} +#main_canvas{background:#333;color:#fff;padding-top:0} +#main_header{background:#222;color:#fff;} +#logs_modal table tr td:first-child{width:10%} +[class_toggle]{cursor:pointer} + +.form-group label{width:100%} + +#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} +#pvideo_viewer video{max-height:300px;max-width:100%;} +#pvideo_viewer .holder{height:300px;} +#pvideo_viewer h3{margin-top:0} +#pvideo_viewer .progressBar{position:relative;} +#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} +#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} +#pvideo_viewer h3{font-family:monospace} + +#vis_monitors{overflow:auto;max-height:400px} +#vis_monitors .btn-group-vertical{width:100%} + +/*timeline*/ +#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} +#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} +.timelapse_video:not(:last-child){border-bottom:1px solid #444;} +.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} +.timelapse_video>div>div:not(:last-child){padding-right:10px} +.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} +.timelapse_video.list-group-item{padding:10px} +.timelapse_hud{position: relative;background:#000} +.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} +#timelapse .progress-bar{transition:0.5s!important} +.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} +.timelapse_hud .hover-hide{opacity:0} +.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} +.video_grid{overflow: auto;height: 100%;display: block;} +.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} +.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} +.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} +.video_grid .thumb .title-strip{top:0;opacity:0.5} +.video_grid .thumb .button-strip{bottom:0;opacity:0} +.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} + +.table-striped>tbody>tr>td{vertical-align:middle} +.table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;} +#motion_list{height:155px;overflow:auto;border-radius:5px;border:1px solid #444;position:relative;background: #222;margin:0} +.dark .list-group-item{border-color: #444;background:#222} +.dark .list-group-item.active{background:#c49a68;border-color:#a7865f} +.novideos{text-transform: uppercase;text-align: center;border-bottom:0!important;padding-top: 55%!important;letter-spacing:2px} + +.btn-warning { + color: #fff; + background-color: #c49a68; + border-color: #c49a68; +} + +.dark.modal .modal-header,.dark.modal .modal-footer{background:#333;border-color:#444;} +.dark.modal .modal-header{color:#fff;} +.dark.modal .modal-footer>*:not(.btn){color:#fff;} +.dark.modal .modal-body{background:#333;} +.dark.modal .close{color:#fff;} +.dark.modal{color:#fff;} +.dark .table-striped>tbody>tr:nth-of-type(even){background:#616161} +.dark .table-striped>tbody>tr>td{border-color:#222;color:#fff} +.dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;} +.dark .table-striped>tbody>tr:nth-of-type(odd){background-color: #4c4747;} +.dark .table>tbody>tr.active>td{background:inherit;border:0} +.dark code{color: #c49a68;background-color: #36333d;} +.dark a:not(.btn){color: #c49a68;} +.follow-list ul{padding:0;margin:0;font-family:"Roboto","Helvetica","Arial",sans-serif;} +.follow-list ul a:not(.btn){color:#fff} +.os_bars{width:600px;display:inline-block;padding:5px 0 0 10px} +@media screen and (max-width: 600px){ + .os_bars{width:200px;} + .os_bars label{padding:2.5px 0;margin:0;font-size:8pt} +} +.os_bars .display-table .display-table-cell{padding:5px;vertical-align:center;width:33%} +.progress{height:5px;margin:0;} +.os_bars label,.os_bars .percent{padding:2.5px 0;margin:0;font-size:7.5pt} +.ui-pnotify-hide .ui-pnotify{display:none!important} +/*cool dropdown thing*/ +ul.msg_list li { + background: #f7f7f7;color:#333; + padding: 5px; + display: list-item; + margin: 6px 6px 0; + width: 96% !important +} + +ul.msg_list li div{display:block} + +ul.msg_list li:last-child { + margin-bottom: 6px; + padding: 10px +} + +ul.msg_list li a { + padding: 3px 5px !important +} +ul.msg_list li .progress { + height:5px;margin:10px 0 0 0; +} + +ul.msg_list li .image img { + border-radius: 2px 2px 2px 2px; + -webkit-border-radius: 2px 2px 2px 2px; + float: left; + margin-right: 10px; + width: 11% +} + +ul.msg_list li .time { + font-size: 11px; + font-style: italic; + font-weight: bold; + position: absolute; +} + +ul.msg_list li .message { + display: block !important; + font-size: 11px +} + +.dropdown-menu.msg_list span { + white-space: normal +} + +.dropdown-menu { + border: medium none; + box-shadow: none; + display: none; + float: left; + font-size: 12px; + left: 0; + list-style: none outside none; + padding: 0; + position: absolute; + text-shadow: none; + top: 100%; + z-index: 9998; + border: 1px solid #D9DEE4; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.dropdown-menu>li>a { + color: #5A738E +} + +.navbar-nav .open .dropdown-menu { + position: absolute; + background: #fff; + margin-top: 0; + border: 1px solid #D9DEE4; + -webkit-box-shadow: none; + right: 0; + left: auto; + width: 220px +} +.is-small-screen .nav>li{display:inline-block} +.navbar-nav .open .dropdown-menu li a{padding:7px 15px} +.navbar-nav .open .dropdown-menu.msg_list { + width: 300px +} +.nav>li>a{color:#fff} +.nav>li>a:focus, .nav>li>a:hover,.nav .open>a, .nav .open>a:focus, .nav .open>a:hover{background:#867560} + +.mdl-js-layout.hide-side:not(.is-small-screen){ + +} +@media screen and (min-width: 1025px){ + .mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__drawer { + width: 0px;transition:0.2s + } +} +.mdl-js-layout.hide-side:not(.is-small-screen) .mdl-layout__header .mdl-layout__drawer-button{ + display:none; +} +.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__content{ + margin-left: 0px;transition:0.2s +} +.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{ + margin-left: 0px;width:100%;transition:0.2s +} +/*Control Pad*/ +.PTZ_controls { + z-index: 111; + position: absolute; + left: 20px; + top: 20px; + margin:0; + display: inline-block; + width: 120px; +} +.PTZ_controls .btn-group{margin-top:10px} +.PTZ_controls .pad { + position: relative; + height: 120px; + width: 120px; + background: #b7b7b7; + border-radius: 50%; + box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); +} +.PTZ_controls .control { + position: absolute; +} +.PTZ_controls .pad .control { + height: 30px; + width: 30px; + background: #636363; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); + border-radius: 2px; +} +.PTZ_controls .zoom_in{ + top: 0; + right: 0; +} +.PTZ_controls .zoom_out{ + bottom: 0; + right: 0; +} +.PTZ_controls .nv_enabled{ + top: 0; + right: 0; +} +.PTZ_controls .nv_disable{ + bottom: 0; + right: 0; +} +.PTZ_controls .pad .top { + top: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +.PTZ_controls .pad .left { + top: 45px; + left: 15px; +} +.PTZ_controls .pad .right { + top: 45px; + right: 15px; +} +.PTZ_controls .pad .control.right:before { + transform: rotate(90deg) translate(-3px, -5px); +} +.PTZ_controls .pad .bottom { + bottom: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +/* Overlap the other controls to hide box-shadow */ +.PTZ_controls .pad .middle { + height: 34px; + width: 34px; + z-index: 5; + top: 43px; + left: 50%; + margin: 0 0 0 -17px; + box-shadow: none; + border-radius: 3px; +} +.PTZ_controls .pad .middle:after { + position: absolute; + top: 50%; + left: 50%; + margin: -35% 0 0 -35%; + content: ''; + background: #636363; + height: 70%; + width: 70%; + border-radius: 100%; + box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); +} +/*Digital Zoom*/ +.stream-block{ + position: relative; + overflow: auto; +} +.zoomGlass { + overflow: hidden; + transition: none; + width: 175px; height: 175px; + position: absolute; + border-radius: 15px; + border: 3px solid #ddd; + z-index:9999; +} +.zoomGlass iframe,.zoomGlass canvas{position:absolute;transition: none;} +.zoomGlass .hoverShade{position:absolute;width:100%;height:100%} + +.dark.form-control,.dark .form-control { + display: block; + width: 100%; + height: 35px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.5; + color: #eee; + background-color: #36333d; + background-image: none; + border: 1px solid #444; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +.dark.form-control:focus,.dark .form-control:focus { + color: #ddd; + background-color: #333; + box-shadow: none; +} + + +/*** custom checkboxes ***/ + +.checkbox input[type=checkbox] { display:none; } /* to hide the checkbox itself */ +.checkbox input[type=checkbox] + label:before { + font-family: FontAwesome; + display: inline-block; +} + +.checkbox input[type=checkbox] + label:before { content: "\f096"; } /* unchecked icon */ +.checkbox input[type=checkbox] + label:before { letter-spacing: 10px; } /* space between checkbox and label */ + +.checkbox input[type=checkbox]:checked + label:before { content: "\f046"; } /* checked icon */ +.checkbox input[type=checkbox]:checked + label:before { letter-spacing: 5px; } /* allow space for check mark */ + +/*Clock*/ +#time-date {font-size:12px; text-align:center;} +@media screen and (min-width:1025px){ + #clock {padding-right:35px} +} +#clock ul { width:150px; margin:0 auto; padding:0px; list-style:none; text-align:center; } +#clock ul li { display:inline; font-size:1.6em; text-align:center;font-family:monospace;} + +#clock .point { position:relative; -moz-animation:mymove 1s ease infinite; -webkit-animation:mymove 1s ease infinite; } + +/*custom vis.js css*/ +.vis-timeline{background:#212121;color:#fff;border-color:#444} +.vis-time-axis .vis-text{color: #dedede} +.vis-item.vis-range .vis-item-content{background:#333;color:#fff} +.vis-time-axis .vis-grid.vis-minor{border-color:#444} + + +@-moz-document url-prefix() { + .full.modal .modal-body, .medium.modal .modal-body { + height:70% + } +} + +/*animations*/ +@keyframes blink { + 0% { opacity:1 } + 50% { opacity:0 } + 100% { opacity:1 } +} +@-webkit-keyframes blink { + 0% { opacity:1 } + 50% { opacity:0 } + 100% { opacity:1 } +} +.blink,[mode="Record"] .lamp { + -webkit-animation: blink 1s linear infinite; + -moz-animation: blink 1s linear infinite; + animation: blink 1s linear infinite; +} +.mdl-menu__item>div{display:flex;align-items: center;width:100%} +.mdl-menu__item>div>i{margin-right:5px} +/*For languages that are right to left*/ +.right-to-left {text-align:right} +.right-to-left select{direction: rtl;} +.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} +.right-to-left .form-group label span{padding-right:10px} +.right-to-left .modal-footer{text-align:left} +.right-to-left .mdl-menu__item>div>*{flex:1} +.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} +.right-to-left .mdl-menu__item{text-align:right} +.right-to-left .mdl-menu__item i{float:right} +.right-to-left .pull-right,.right-to-left .close{float:left!important} +.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} +/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */ +.marc-toggle { + width: 50px; + height: 25px; +} +.marc-toggle.abs-bot-left { + position: absolute; + bottom: 10px; + left: 10px; +} +.marc-toggle.abs-bot-right { + position: absolute; + bottom: 10px; + right: 10px; +} +.marc-toggle input[type=checkbox]{ + height: 0; + width: 0; + visibility: hidden; +} + +.marc-toggle label { + cursor: pointer; + text-indent: -9999px; + width: 100px; + height: 20px; + background: grey; + display: block; + border-radius: 100px; + margin-bottom: 0; + position: relative; +} + +.marc-toggle label:after { + content: ''; + position: absolute; + top: 5px; + left: 5px; + width: 10px; + height: 10px; + background: #fff; + border-radius: 90px; + transition: 0.3s; +} + +.marc-toggle input:checked + label { + background: #00118c; +} + +.marc-toggle input:checked + label:after { + left: calc(100% - 5px); + transform: translateX(-100%); +} + +.marc-toggle label:active:after { + width: 10px; +} + +/*hexagon pattern*/ +.bg-hexagon { + background-color: #054e9f; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23fdfdfd' fill-opacity='0.4' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); +} diff --git a/web/libs/css/material-design-iconic-font.min.css b/web/libs/css/material-design-iconic-font.min.css new file mode 100644 index 0000000..e1a58fe --- /dev/null +++ b/web/libs/css/material-design-iconic-font.min.css @@ -0,0 +1 @@ +@font-face{font-family:Material-Design-Iconic-Font;src:url(../fonts/Material-Design-Iconic-Font.woff2?v=2.2.0) format('woff2'),url(../fonts/Material-Design-Iconic-Font.woff?v=2.2.0) format('woff'),url(../fonts/Material-Design-Iconic-Font.ttf?v=2.2.0) format('truetype')}.zmdi{display:inline-block;font:normal normal normal 14px/1 'Material-Design-Iconic-Font';font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.zmdi-hc-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.zmdi-hc-2x{font-size:2em}.zmdi-hc-3x{font-size:3em}.zmdi-hc-4x{font-size:4em}.zmdi-hc-5x{font-size:5em}.zmdi-hc-fw{width:1.28571429em;text-align:center}.zmdi-hc-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.zmdi-hc-ul>li{position:relative}.zmdi-hc-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.zmdi-hc-li.zmdi-hc-lg{left:-1.85714286em}.zmdi-hc-border{padding:.1em .25em;border:solid .1em #9e9e9e;border-radius:2px}.zmdi-hc-border-circle{padding:.1em .25em;border:solid .1em #9e9e9e;border-radius:50%}.zmdi.pull-left{float:left;margin-right:.15em}.zmdi.pull-right{float:right;margin-left:.15em}.zmdi-hc-spin{-webkit-animation:zmdi-spin 1.5s infinite linear;animation:zmdi-spin 1.5s infinite linear}.zmdi-hc-spin-reverse{-webkit-animation:zmdi-spin-reverse 1.5s infinite linear;animation:zmdi-spin-reverse 1.5s infinite linear}@-webkit-keyframes zmdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes zmdi-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@-webkit-keyframes zmdi-spin-reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(-359deg);transform:rotate(-359deg)}}@keyframes zmdi-spin-reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(-359deg);transform:rotate(-359deg)}}.zmdi-hc-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.zmdi-hc-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.zmdi-hc-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.zmdi-hc-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.zmdi-hc-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}.zmdi-hc-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.zmdi-hc-stack-1x,.zmdi-hc-stack-2x{position:absolute;left:0;width:100%;text-align:center}.zmdi-hc-stack-1x{line-height:inherit}.zmdi-hc-stack-2x{font-size:2em}.zmdi-hc-inverse{color:#fff}.zmdi-3d-rotation:before{content:'\f101'}.zmdi-airplane-off:before{content:'\f102'}.zmdi-airplane:before{content:'\f103'}.zmdi-album:before{content:'\f104'}.zmdi-archive:before{content:'\f105'}.zmdi-assignment-account:before{content:'\f106'}.zmdi-assignment-alert:before{content:'\f107'}.zmdi-assignment-check:before{content:'\f108'}.zmdi-assignment-o:before{content:'\f109'}.zmdi-assignment-return:before{content:'\f10a'}.zmdi-assignment-returned:before{content:'\f10b'}.zmdi-assignment:before{content:'\f10c'}.zmdi-attachment-alt:before{content:'\f10d'}.zmdi-attachment:before{content:'\f10e'}.zmdi-audio:before{content:'\f10f'}.zmdi-badge-check:before{content:'\f110'}.zmdi-balance-wallet:before{content:'\f111'}.zmdi-balance:before{content:'\f112'}.zmdi-battery-alert:before{content:'\f113'}.zmdi-battery-flash:before{content:'\f114'}.zmdi-battery-unknown:before{content:'\f115'}.zmdi-battery:before{content:'\f116'}.zmdi-bike:before{content:'\f117'}.zmdi-block-alt:before{content:'\f118'}.zmdi-block:before{content:'\f119'}.zmdi-boat:before{content:'\f11a'}.zmdi-book-image:before{content:'\f11b'}.zmdi-book:before{content:'\f11c'}.zmdi-bookmark-outline:before{content:'\f11d'}.zmdi-bookmark:before{content:'\f11e'}.zmdi-brush:before{content:'\f11f'}.zmdi-bug:before{content:'\f120'}.zmdi-bus:before{content:'\f121'}.zmdi-cake:before{content:'\f122'}.zmdi-car-taxi:before{content:'\f123'}.zmdi-car-wash:before{content:'\f124'}.zmdi-car:before{content:'\f125'}.zmdi-card-giftcard:before{content:'\f126'}.zmdi-card-membership:before{content:'\f127'}.zmdi-card-travel:before{content:'\f128'}.zmdi-card:before{content:'\f129'}.zmdi-case-check:before{content:'\f12a'}.zmdi-case-download:before{content:'\f12b'}.zmdi-case-play:before{content:'\f12c'}.zmdi-case:before{content:'\f12d'}.zmdi-cast-connected:before{content:'\f12e'}.zmdi-cast:before{content:'\f12f'}.zmdi-chart-donut:before{content:'\f130'}.zmdi-chart:before{content:'\f131'}.zmdi-city-alt:before{content:'\f132'}.zmdi-city:before{content:'\f133'}.zmdi-close-circle-o:before{content:'\f134'}.zmdi-close-circle:before{content:'\f135'}.zmdi-close:before{content:'\f136'}.zmdi-cocktail:before{content:'\f137'}.zmdi-code-setting:before{content:'\f138'}.zmdi-code-smartphone:before{content:'\f139'}.zmdi-code:before{content:'\f13a'}.zmdi-coffee:before{content:'\f13b'}.zmdi-collection-bookmark:before{content:'\f13c'}.zmdi-collection-case-play:before{content:'\f13d'}.zmdi-collection-folder-image:before{content:'\f13e'}.zmdi-collection-image-o:before{content:'\f13f'}.zmdi-collection-image:before{content:'\f140'}.zmdi-collection-item-1:before{content:'\f141'}.zmdi-collection-item-2:before{content:'\f142'}.zmdi-collection-item-3:before{content:'\f143'}.zmdi-collection-item-4:before{content:'\f144'}.zmdi-collection-item-5:before{content:'\f145'}.zmdi-collection-item-6:before{content:'\f146'}.zmdi-collection-item-7:before{content:'\f147'}.zmdi-collection-item-8:before{content:'\f148'}.zmdi-collection-item-9-plus:before{content:'\f149'}.zmdi-collection-item-9:before{content:'\f14a'}.zmdi-collection-item:before{content:'\f14b'}.zmdi-collection-music:before{content:'\f14c'}.zmdi-collection-pdf:before{content:'\f14d'}.zmdi-collection-plus:before{content:'\f14e'}.zmdi-collection-speaker:before{content:'\f14f'}.zmdi-collection-text:before{content:'\f150'}.zmdi-collection-video:before{content:'\f151'}.zmdi-compass:before{content:'\f152'}.zmdi-cutlery:before{content:'\f153'}.zmdi-delete:before{content:'\f154'}.zmdi-dialpad:before{content:'\f155'}.zmdi-dns:before{content:'\f156'}.zmdi-drink:before{content:'\f157'}.zmdi-edit:before{content:'\f158'}.zmdi-email-open:before{content:'\f159'}.zmdi-email:before{content:'\f15a'}.zmdi-eye-off:before{content:'\f15b'}.zmdi-eye:before{content:'\f15c'}.zmdi-eyedropper:before{content:'\f15d'}.zmdi-favorite-outline:before{content:'\f15e'}.zmdi-favorite:before{content:'\f15f'}.zmdi-filter-list:before{content:'\f160'}.zmdi-fire:before{content:'\f161'}.zmdi-flag:before{content:'\f162'}.zmdi-flare:before{content:'\f163'}.zmdi-flash-auto:before{content:'\f164'}.zmdi-flash-off:before{content:'\f165'}.zmdi-flash:before{content:'\f166'}.zmdi-flip:before{content:'\f167'}.zmdi-flower-alt:before{content:'\f168'}.zmdi-flower:before{content:'\f169'}.zmdi-font:before{content:'\f16a'}.zmdi-fullscreen-alt:before{content:'\f16b'}.zmdi-fullscreen-exit:before{content:'\f16c'}.zmdi-fullscreen:before{content:'\f16d'}.zmdi-functions:before{content:'\f16e'}.zmdi-gas-station:before{content:'\f16f'}.zmdi-gesture:before{content:'\f170'}.zmdi-globe-alt:before{content:'\f171'}.zmdi-globe-lock:before{content:'\f172'}.zmdi-globe:before{content:'\f173'}.zmdi-graduation-cap:before{content:'\f174'}.zmdi-home:before{content:'\f175'}.zmdi-hospital-alt:before{content:'\f176'}.zmdi-hospital:before{content:'\f177'}.zmdi-hotel:before{content:'\f178'}.zmdi-hourglass-alt:before{content:'\f179'}.zmdi-hourglass-outline:before{content:'\f17a'}.zmdi-hourglass:before{content:'\f17b'}.zmdi-http:before{content:'\f17c'}.zmdi-image-alt:before{content:'\f17d'}.zmdi-image-o:before{content:'\f17e'}.zmdi-image:before{content:'\f17f'}.zmdi-inbox:before{content:'\f180'}.zmdi-invert-colors-off:before{content:'\f181'}.zmdi-invert-colors:before{content:'\f182'}.zmdi-key:before{content:'\f183'}.zmdi-label-alt-outline:before{content:'\f184'}.zmdi-label-alt:before{content:'\f185'}.zmdi-label-heart:before{content:'\f186'}.zmdi-label:before{content:'\f187'}.zmdi-labels:before{content:'\f188'}.zmdi-lamp:before{content:'\f189'}.zmdi-landscape:before{content:'\f18a'}.zmdi-layers-off:before{content:'\f18b'}.zmdi-layers:before{content:'\f18c'}.zmdi-library:before{content:'\f18d'}.zmdi-link:before{content:'\f18e'}.zmdi-lock-open:before{content:'\f18f'}.zmdi-lock-outline:before{content:'\f190'}.zmdi-lock:before{content:'\f191'}.zmdi-mail-reply-all:before{content:'\f192'}.zmdi-mail-reply:before{content:'\f193'}.zmdi-mail-send:before{content:'\f194'}.zmdi-mall:before{content:'\f195'}.zmdi-map:before{content:'\f196'}.zmdi-menu:before{content:'\f197'}.zmdi-money-box:before{content:'\f198'}.zmdi-money-off:before{content:'\f199'}.zmdi-money:before{content:'\f19a'}.zmdi-more-vert:before{content:'\f19b'}.zmdi-more:before{content:'\f19c'}.zmdi-movie-alt:before{content:'\f19d'}.zmdi-movie:before{content:'\f19e'}.zmdi-nature-people:before{content:'\f19f'}.zmdi-nature:before{content:'\f1a0'}.zmdi-navigation:before{content:'\f1a1'}.zmdi-open-in-browser:before{content:'\f1a2'}.zmdi-open-in-new:before{content:'\f1a3'}.zmdi-palette:before{content:'\f1a4'}.zmdi-parking:before{content:'\f1a5'}.zmdi-pin-account:before{content:'\f1a6'}.zmdi-pin-assistant:before{content:'\f1a7'}.zmdi-pin-drop:before{content:'\f1a8'}.zmdi-pin-help:before{content:'\f1a9'}.zmdi-pin-off:before{content:'\f1aa'}.zmdi-pin:before{content:'\f1ab'}.zmdi-pizza:before{content:'\f1ac'}.zmdi-plaster:before{content:'\f1ad'}.zmdi-power-setting:before{content:'\f1ae'}.zmdi-power:before{content:'\f1af'}.zmdi-print:before{content:'\f1b0'}.zmdi-puzzle-piece:before{content:'\f1b1'}.zmdi-quote:before{content:'\f1b2'}.zmdi-railway:before{content:'\f1b3'}.zmdi-receipt:before{content:'\f1b4'}.zmdi-refresh-alt:before{content:'\f1b5'}.zmdi-refresh-sync-alert:before{content:'\f1b6'}.zmdi-refresh-sync-off:before{content:'\f1b7'}.zmdi-refresh-sync:before{content:'\f1b8'}.zmdi-refresh:before{content:'\f1b9'}.zmdi-roller:before{content:'\f1ba'}.zmdi-ruler:before{content:'\f1bb'}.zmdi-scissors:before{content:'\f1bc'}.zmdi-screen-rotation-lock:before{content:'\f1bd'}.zmdi-screen-rotation:before{content:'\f1be'}.zmdi-search-for:before{content:'\f1bf'}.zmdi-search-in-file:before{content:'\f1c0'}.zmdi-search-in-page:before{content:'\f1c1'}.zmdi-search-replace:before{content:'\f1c2'}.zmdi-search:before{content:'\f1c3'}.zmdi-seat:before{content:'\f1c4'}.zmdi-settings-square:before{content:'\f1c5'}.zmdi-settings:before{content:'\f1c6'}.zmdi-shield-check:before{content:'\f1c7'}.zmdi-shield-security:before{content:'\f1c8'}.zmdi-shopping-basket:before{content:'\f1c9'}.zmdi-shopping-cart-plus:before{content:'\f1ca'}.zmdi-shopping-cart:before{content:'\f1cb'}.zmdi-sign-in:before{content:'\f1cc'}.zmdi-sort-amount-asc:before{content:'\f1cd'}.zmdi-sort-amount-desc:before{content:'\f1ce'}.zmdi-sort-asc:before{content:'\f1cf'}.zmdi-sort-desc:before{content:'\f1d0'}.zmdi-spellcheck:before{content:'\f1d1'}.zmdi-storage:before{content:'\f1d2'}.zmdi-store-24:before{content:'\f1d3'}.zmdi-store:before{content:'\f1d4'}.zmdi-subway:before{content:'\f1d5'}.zmdi-sun:before{content:'\f1d6'}.zmdi-tab-unselected:before{content:'\f1d7'}.zmdi-tab:before{content:'\f1d8'}.zmdi-tag-close:before{content:'\f1d9'}.zmdi-tag-more:before{content:'\f1da'}.zmdi-tag:before{content:'\f1db'}.zmdi-thumb-down:before{content:'\f1dc'}.zmdi-thumb-up-down:before{content:'\f1dd'}.zmdi-thumb-up:before{content:'\f1de'}.zmdi-ticket-star:before{content:'\f1df'}.zmdi-toll:before{content:'\f1e0'}.zmdi-toys:before{content:'\f1e1'}.zmdi-traffic:before{content:'\f1e2'}.zmdi-translate:before{content:'\f1e3'}.zmdi-triangle-down:before{content:'\f1e4'}.zmdi-triangle-up:before{content:'\f1e5'}.zmdi-truck:before{content:'\f1e6'}.zmdi-turning-sign:before{content:'\f1e7'}.zmdi-wallpaper:before{content:'\f1e8'}.zmdi-washing-machine:before{content:'\f1e9'}.zmdi-window-maximize:before{content:'\f1ea'}.zmdi-window-minimize:before{content:'\f1eb'}.zmdi-window-restore:before{content:'\f1ec'}.zmdi-wrench:before{content:'\f1ed'}.zmdi-zoom-in:before{content:'\f1ee'}.zmdi-zoom-out:before{content:'\f1ef'}.zmdi-alert-circle-o:before{content:'\f1f0'}.zmdi-alert-circle:before{content:'\f1f1'}.zmdi-alert-octagon:before{content:'\f1f2'}.zmdi-alert-polygon:before{content:'\f1f3'}.zmdi-alert-triangle:before{content:'\f1f4'}.zmdi-help-outline:before{content:'\f1f5'}.zmdi-help:before{content:'\f1f6'}.zmdi-info-outline:before{content:'\f1f7'}.zmdi-info:before{content:'\f1f8'}.zmdi-notifications-active:before{content:'\f1f9'}.zmdi-notifications-add:before{content:'\f1fa'}.zmdi-notifications-none:before{content:'\f1fb'}.zmdi-notifications-off:before{content:'\f1fc'}.zmdi-notifications-paused:before{content:'\f1fd'}.zmdi-notifications:before{content:'\f1fe'}.zmdi-account-add:before{content:'\f1ff'}.zmdi-account-box-mail:before{content:'\f200'}.zmdi-account-box-o:before{content:'\f201'}.zmdi-account-box-phone:before{content:'\f202'}.zmdi-account-box:before{content:'\f203'}.zmdi-account-calendar:before{content:'\f204'}.zmdi-account-circle:before{content:'\f205'}.zmdi-account-o:before{content:'\f206'}.zmdi-account:before{content:'\f207'}.zmdi-accounts-add:before{content:'\f208'}.zmdi-accounts-alt:before{content:'\f209'}.zmdi-accounts-list-alt:before{content:'\f20a'}.zmdi-accounts-list:before{content:'\f20b'}.zmdi-accounts-outline:before{content:'\f20c'}.zmdi-accounts:before{content:'\f20d'}.zmdi-face:before{content:'\f20e'}.zmdi-female:before{content:'\f20f'}.zmdi-male-alt:before{content:'\f210'}.zmdi-male-female:before{content:'\f211'}.zmdi-male:before{content:'\f212'}.zmdi-mood-bad:before{content:'\f213'}.zmdi-mood:before{content:'\f214'}.zmdi-run:before{content:'\f215'}.zmdi-walk:before{content:'\f216'}.zmdi-cloud-box:before{content:'\f217'}.zmdi-cloud-circle:before{content:'\f218'}.zmdi-cloud-done:before{content:'\f219'}.zmdi-cloud-download:before{content:'\f21a'}.zmdi-cloud-off:before{content:'\f21b'}.zmdi-cloud-outline-alt:before{content:'\f21c'}.zmdi-cloud-outline:before{content:'\f21d'}.zmdi-cloud-upload:before{content:'\f21e'}.zmdi-cloud:before{content:'\f21f'}.zmdi-download:before{content:'\f220'}.zmdi-file-plus:before{content:'\f221'}.zmdi-file-text:before{content:'\f222'}.zmdi-file:before{content:'\f223'}.zmdi-folder-outline:before{content:'\f224'}.zmdi-folder-person:before{content:'\f225'}.zmdi-folder-star-alt:before{content:'\f226'}.zmdi-folder-star:before{content:'\f227'}.zmdi-folder:before{content:'\f228'}.zmdi-gif:before{content:'\f229'}.zmdi-upload:before{content:'\f22a'}.zmdi-border-all:before{content:'\f22b'}.zmdi-border-bottom:before{content:'\f22c'}.zmdi-border-clear:before{content:'\f22d'}.zmdi-border-color:before{content:'\f22e'}.zmdi-border-horizontal:before{content:'\f22f'}.zmdi-border-inner:before{content:'\f230'}.zmdi-border-left:before{content:'\f231'}.zmdi-border-outer:before{content:'\f232'}.zmdi-border-right:before{content:'\f233'}.zmdi-border-style:before{content:'\f234'}.zmdi-border-top:before{content:'\f235'}.zmdi-border-vertical:before{content:'\f236'}.zmdi-copy:before{content:'\f237'}.zmdi-crop:before{content:'\f238'}.zmdi-format-align-center:before{content:'\f239'}.zmdi-format-align-justify:before{content:'\f23a'}.zmdi-format-align-left:before{content:'\f23b'}.zmdi-format-align-right:before{content:'\f23c'}.zmdi-format-bold:before{content:'\f23d'}.zmdi-format-clear-all:before{content:'\f23e'}.zmdi-format-clear:before{content:'\f23f'}.zmdi-format-color-fill:before{content:'\f240'}.zmdi-format-color-reset:before{content:'\f241'}.zmdi-format-color-text:before{content:'\f242'}.zmdi-format-indent-decrease:before{content:'\f243'}.zmdi-format-indent-increase:before{content:'\f244'}.zmdi-format-italic:before{content:'\f245'}.zmdi-format-line-spacing:before{content:'\f246'}.zmdi-format-list-bulleted:before{content:'\f247'}.zmdi-format-list-numbered:before{content:'\f248'}.zmdi-format-ltr:before{content:'\f249'}.zmdi-format-rtl:before{content:'\f24a'}.zmdi-format-size:before{content:'\f24b'}.zmdi-format-strikethrough-s:before{content:'\f24c'}.zmdi-format-strikethrough:before{content:'\f24d'}.zmdi-format-subject:before{content:'\f24e'}.zmdi-format-underlined:before{content:'\f24f'}.zmdi-format-valign-bottom:before{content:'\f250'}.zmdi-format-valign-center:before{content:'\f251'}.zmdi-format-valign-top:before{content:'\f252'}.zmdi-redo:before{content:'\f253'}.zmdi-select-all:before{content:'\f254'}.zmdi-space-bar:before{content:'\f255'}.zmdi-text-format:before{content:'\f256'}.zmdi-transform:before{content:'\f257'}.zmdi-undo:before{content:'\f258'}.zmdi-wrap-text:before{content:'\f259'}.zmdi-comment-alert:before{content:'\f25a'}.zmdi-comment-alt-text:before{content:'\f25b'}.zmdi-comment-alt:before{content:'\f25c'}.zmdi-comment-edit:before{content:'\f25d'}.zmdi-comment-image:before{content:'\f25e'}.zmdi-comment-list:before{content:'\f25f'}.zmdi-comment-more:before{content:'\f260'}.zmdi-comment-outline:before{content:'\f261'}.zmdi-comment-text-alt:before{content:'\f262'}.zmdi-comment-text:before{content:'\f263'}.zmdi-comment-video:before{content:'\f264'}.zmdi-comment:before{content:'\f265'}.zmdi-comments:before{content:'\f266'}.zmdi-check-all:before{content:'\f267'}.zmdi-check-circle-u:before{content:'\f268'}.zmdi-check-circle:before{content:'\f269'}.zmdi-check-square:before{content:'\f26a'}.zmdi-check:before{content:'\f26b'}.zmdi-circle-o:before{content:'\f26c'}.zmdi-circle:before{content:'\f26d'}.zmdi-dot-circle-alt:before{content:'\f26e'}.zmdi-dot-circle:before{content:'\f26f'}.zmdi-minus-circle-outline:before{content:'\f270'}.zmdi-minus-circle:before{content:'\f271'}.zmdi-minus-square:before{content:'\f272'}.zmdi-minus:before{content:'\f273'}.zmdi-plus-circle-o-duplicate:before{content:'\f274'}.zmdi-plus-circle-o:before{content:'\f275'}.zmdi-plus-circle:before{content:'\f276'}.zmdi-plus-square:before{content:'\f277'}.zmdi-plus:before{content:'\f278'}.zmdi-square-o:before{content:'\f279'}.zmdi-star-circle:before{content:'\f27a'}.zmdi-star-half:before{content:'\f27b'}.zmdi-star-outline:before{content:'\f27c'}.zmdi-star:before{content:'\f27d'}.zmdi-bluetooth-connected:before{content:'\f27e'}.zmdi-bluetooth-off:before{content:'\f27f'}.zmdi-bluetooth-search:before{content:'\f280'}.zmdi-bluetooth-setting:before{content:'\f281'}.zmdi-bluetooth:before{content:'\f282'}.zmdi-camera-add:before{content:'\f283'}.zmdi-camera-alt:before{content:'\f284'}.zmdi-camera-bw:before{content:'\f285'}.zmdi-camera-front:before{content:'\f286'}.zmdi-camera-mic:before{content:'\f287'}.zmdi-camera-party-mode:before{content:'\f288'}.zmdi-camera-rear:before{content:'\f289'}.zmdi-camera-roll:before{content:'\f28a'}.zmdi-camera-switch:before{content:'\f28b'}.zmdi-camera:before{content:'\f28c'}.zmdi-card-alert:before{content:'\f28d'}.zmdi-card-off:before{content:'\f28e'}.zmdi-card-sd:before{content:'\f28f'}.zmdi-card-sim:before{content:'\f290'}.zmdi-desktop-mac:before{content:'\f291'}.zmdi-desktop-windows:before{content:'\f292'}.zmdi-device-hub:before{content:'\f293'}.zmdi-devices-off:before{content:'\f294'}.zmdi-devices:before{content:'\f295'}.zmdi-dock:before{content:'\f296'}.zmdi-floppy:before{content:'\f297'}.zmdi-gamepad:before{content:'\f298'}.zmdi-gps-dot:before{content:'\f299'}.zmdi-gps-off:before{content:'\f29a'}.zmdi-gps:before{content:'\f29b'}.zmdi-headset-mic:before{content:'\f29c'}.zmdi-headset:before{content:'\f29d'}.zmdi-input-antenna:before{content:'\f29e'}.zmdi-input-composite:before{content:'\f29f'}.zmdi-input-hdmi:before{content:'\f2a0'}.zmdi-input-power:before{content:'\f2a1'}.zmdi-input-svideo:before{content:'\f2a2'}.zmdi-keyboard-hide:before{content:'\f2a3'}.zmdi-keyboard:before{content:'\f2a4'}.zmdi-laptop-chromebook:before{content:'\f2a5'}.zmdi-laptop-mac:before{content:'\f2a6'}.zmdi-laptop:before{content:'\f2a7'}.zmdi-mic-off:before{content:'\f2a8'}.zmdi-mic-outline:before{content:'\f2a9'}.zmdi-mic-setting:before{content:'\f2aa'}.zmdi-mic:before{content:'\f2ab'}.zmdi-mouse:before{content:'\f2ac'}.zmdi-network-alert:before{content:'\f2ad'}.zmdi-network-locked:before{content:'\f2ae'}.zmdi-network-off:before{content:'\f2af'}.zmdi-network-outline:before{content:'\f2b0'}.zmdi-network-setting:before{content:'\f2b1'}.zmdi-network:before{content:'\f2b2'}.zmdi-phone-bluetooth:before{content:'\f2b3'}.zmdi-phone-end:before{content:'\f2b4'}.zmdi-phone-forwarded:before{content:'\f2b5'}.zmdi-phone-in-talk:before{content:'\f2b6'}.zmdi-phone-locked:before{content:'\f2b7'}.zmdi-phone-missed:before{content:'\f2b8'}.zmdi-phone-msg:before{content:'\f2b9'}.zmdi-phone-paused:before{content:'\f2ba'}.zmdi-phone-ring:before{content:'\f2bb'}.zmdi-phone-setting:before{content:'\f2bc'}.zmdi-phone-sip:before{content:'\f2bd'}.zmdi-phone:before{content:'\f2be'}.zmdi-portable-wifi-changes:before{content:'\f2bf'}.zmdi-portable-wifi-off:before{content:'\f2c0'}.zmdi-portable-wifi:before{content:'\f2c1'}.zmdi-radio:before{content:'\f2c2'}.zmdi-reader:before{content:'\f2c3'}.zmdi-remote-control-alt:before{content:'\f2c4'}.zmdi-remote-control:before{content:'\f2c5'}.zmdi-router:before{content:'\f2c6'}.zmdi-scanner:before{content:'\f2c7'}.zmdi-smartphone-android:before{content:'\f2c8'}.zmdi-smartphone-download:before{content:'\f2c9'}.zmdi-smartphone-erase:before{content:'\f2ca'}.zmdi-smartphone-info:before{content:'\f2cb'}.zmdi-smartphone-iphone:before{content:'\f2cc'}.zmdi-smartphone-landscape-lock:before{content:'\f2cd'}.zmdi-smartphone-landscape:before{content:'\f2ce'}.zmdi-smartphone-lock:before{content:'\f2cf'}.zmdi-smartphone-portrait-lock:before{content:'\f2d0'}.zmdi-smartphone-ring:before{content:'\f2d1'}.zmdi-smartphone-setting:before{content:'\f2d2'}.zmdi-smartphone-setup:before{content:'\f2d3'}.zmdi-smartphone:before{content:'\f2d4'}.zmdi-speaker:before{content:'\f2d5'}.zmdi-tablet-android:before{content:'\f2d6'}.zmdi-tablet-mac:before{content:'\f2d7'}.zmdi-tablet:before{content:'\f2d8'}.zmdi-tv-alt-play:before{content:'\f2d9'}.zmdi-tv-list:before{content:'\f2da'}.zmdi-tv-play:before{content:'\f2db'}.zmdi-tv:before{content:'\f2dc'}.zmdi-usb:before{content:'\f2dd'}.zmdi-videocam-off:before{content:'\f2de'}.zmdi-videocam-switch:before{content:'\f2df'}.zmdi-videocam:before{content:'\f2e0'}.zmdi-watch:before{content:'\f2e1'}.zmdi-wifi-alt-2:before{content:'\f2e2'}.zmdi-wifi-alt:before{content:'\f2e3'}.zmdi-wifi-info:before{content:'\f2e4'}.zmdi-wifi-lock:before{content:'\f2e5'}.zmdi-wifi-off:before{content:'\f2e6'}.zmdi-wifi-outline:before{content:'\f2e7'}.zmdi-wifi:before{content:'\f2e8'}.zmdi-arrow-left-bottom:before{content:'\f2e9'}.zmdi-arrow-left:before{content:'\f2ea'}.zmdi-arrow-merge:before{content:'\f2eb'}.zmdi-arrow-missed:before{content:'\f2ec'}.zmdi-arrow-right-top:before{content:'\f2ed'}.zmdi-arrow-right:before{content:'\f2ee'}.zmdi-arrow-split:before{content:'\f2ef'}.zmdi-arrows:before{content:'\f2f0'}.zmdi-caret-down-circle:before{content:'\f2f1'}.zmdi-caret-down:before{content:'\f2f2'}.zmdi-caret-left-circle:before{content:'\f2f3'}.zmdi-caret-left:before{content:'\f2f4'}.zmdi-caret-right-circle:before{content:'\f2f5'}.zmdi-caret-right:before{content:'\f2f6'}.zmdi-caret-up-circle:before{content:'\f2f7'}.zmdi-caret-up:before{content:'\f2f8'}.zmdi-chevron-down:before{content:'\f2f9'}.zmdi-chevron-left:before{content:'\f2fa'}.zmdi-chevron-right:before{content:'\f2fb'}.zmdi-chevron-up:before{content:'\f2fc'}.zmdi-forward:before{content:'\f2fd'}.zmdi-long-arrow-down:before{content:'\f2fe'}.zmdi-long-arrow-left:before{content:'\f2ff'}.zmdi-long-arrow-return:before{content:'\f300'}.zmdi-long-arrow-right:before{content:'\f301'}.zmdi-long-arrow-tab:before{content:'\f302'}.zmdi-long-arrow-up:before{content:'\f303'}.zmdi-rotate-ccw:before{content:'\f304'}.zmdi-rotate-cw:before{content:'\f305'}.zmdi-rotate-left:before{content:'\f306'}.zmdi-rotate-right:before{content:'\f307'}.zmdi-square-down:before{content:'\f308'}.zmdi-square-right:before{content:'\f309'}.zmdi-swap-alt:before{content:'\f30a'}.zmdi-swap-vertical-circle:before{content:'\f30b'}.zmdi-swap-vertical:before{content:'\f30c'}.zmdi-swap:before{content:'\f30d'}.zmdi-trending-down:before{content:'\f30e'}.zmdi-trending-flat:before{content:'\f30f'}.zmdi-trending-up:before{content:'\f310'}.zmdi-unfold-less:before{content:'\f311'}.zmdi-unfold-more:before{content:'\f312'}.zmdi-apps:before{content:'\f313'}.zmdi-grid-off:before{content:'\f314'}.zmdi-grid:before{content:'\f315'}.zmdi-view-agenda:before{content:'\f316'}.zmdi-view-array:before{content:'\f317'}.zmdi-view-carousel:before{content:'\f318'}.zmdi-view-column:before{content:'\f319'}.zmdi-view-comfy:before{content:'\f31a'}.zmdi-view-compact:before{content:'\f31b'}.zmdi-view-dashboard:before{content:'\f31c'}.zmdi-view-day:before{content:'\f31d'}.zmdi-view-headline:before{content:'\f31e'}.zmdi-view-list-alt:before{content:'\f31f'}.zmdi-view-list:before{content:'\f320'}.zmdi-view-module:before{content:'\f321'}.zmdi-view-quilt:before{content:'\f322'}.zmdi-view-stream:before{content:'\f323'}.zmdi-view-subtitles:before{content:'\f324'}.zmdi-view-toc:before{content:'\f325'}.zmdi-view-web:before{content:'\f326'}.zmdi-view-week:before{content:'\f327'}.zmdi-widgets:before{content:'\f328'}.zmdi-alarm-check:before{content:'\f329'}.zmdi-alarm-off:before{content:'\f32a'}.zmdi-alarm-plus:before{content:'\f32b'}.zmdi-alarm-snooze:before{content:'\f32c'}.zmdi-alarm:before{content:'\f32d'}.zmdi-calendar-alt:before{content:'\f32e'}.zmdi-calendar-check:before{content:'\f32f'}.zmdi-calendar-close:before{content:'\f330'}.zmdi-calendar-note:before{content:'\f331'}.zmdi-calendar:before{content:'\f332'}.zmdi-time-countdown:before{content:'\f333'}.zmdi-time-interval:before{content:'\f334'}.zmdi-time-restore-setting:before{content:'\f335'}.zmdi-time-restore:before{content:'\f336'}.zmdi-time:before{content:'\f337'}.zmdi-timer-off:before{content:'\f338'}.zmdi-timer:before{content:'\f339'}.zmdi-android-alt:before{content:'\f33a'}.zmdi-android:before{content:'\f33b'}.zmdi-apple:before{content:'\f33c'}.zmdi-behance:before{content:'\f33d'}.zmdi-codepen:before{content:'\f33e'}.zmdi-dribbble:before{content:'\f33f'}.zmdi-dropbox:before{content:'\f340'}.zmdi-evernote:before{content:'\f341'}.zmdi-facebook-box:before{content:'\f342'}.zmdi-facebook:before{content:'\f343'}.zmdi-github-box:before{content:'\f344'}.zmdi-github:before{content:'\f345'}.zmdi-google-drive:before{content:'\f346'}.zmdi-google-earth:before{content:'\f347'}.zmdi-google-glass:before{content:'\f348'}.zmdi-google-maps:before{content:'\f349'}.zmdi-google-pages:before{content:'\f34a'}.zmdi-google-play:before{content:'\f34b'}.zmdi-google-plus-box:before{content:'\f34c'}.zmdi-google-plus:before{content:'\f34d'}.zmdi-google:before{content:'\f34e'}.zmdi-instagram:before{content:'\f34f'}.zmdi-language-css3:before{content:'\f350'}.zmdi-language-html5:before{content:'\f351'}.zmdi-language-javascript:before{content:'\f352'}.zmdi-language-python-alt:before{content:'\f353'}.zmdi-language-python:before{content:'\f354'}.zmdi-lastfm:before{content:'\f355'}.zmdi-linkedin-box:before{content:'\f356'}.zmdi-paypal:before{content:'\f357'}.zmdi-pinterest-box:before{content:'\f358'}.zmdi-pocket:before{content:'\f359'}.zmdi-polymer:before{content:'\f35a'}.zmdi-share:before{content:'\f35b'}.zmdi-stackoverflow:before{content:'\f35c'}.zmdi-steam-square:before{content:'\f35d'}.zmdi-steam:before{content:'\f35e'}.zmdi-twitter-box:before{content:'\f35f'}.zmdi-twitter:before{content:'\f360'}.zmdi-vk:before{content:'\f361'}.zmdi-wikipedia:before{content:'\f362'}.zmdi-windows:before{content:'\f363'}.zmdi-aspect-ratio-alt:before{content:'\f364'}.zmdi-aspect-ratio:before{content:'\f365'}.zmdi-blur-circular:before{content:'\f366'}.zmdi-blur-linear:before{content:'\f367'}.zmdi-blur-off:before{content:'\f368'}.zmdi-blur:before{content:'\f369'}.zmdi-brightness-2:before{content:'\f36a'}.zmdi-brightness-3:before{content:'\f36b'}.zmdi-brightness-4:before{content:'\f36c'}.zmdi-brightness-5:before{content:'\f36d'}.zmdi-brightness-6:before{content:'\f36e'}.zmdi-brightness-7:before{content:'\f36f'}.zmdi-brightness-auto:before{content:'\f370'}.zmdi-brightness-setting:before{content:'\f371'}.zmdi-broken-image:before{content:'\f372'}.zmdi-center-focus-strong:before{content:'\f373'}.zmdi-center-focus-weak:before{content:'\f374'}.zmdi-compare:before{content:'\f375'}.zmdi-crop-16-9:before{content:'\f376'}.zmdi-crop-3-2:before{content:'\f377'}.zmdi-crop-5-4:before{content:'\f378'}.zmdi-crop-7-5:before{content:'\f379'}.zmdi-crop-din:before{content:'\f37a'}.zmdi-crop-free:before{content:'\f37b'}.zmdi-crop-landscape:before{content:'\f37c'}.zmdi-crop-portrait:before{content:'\f37d'}.zmdi-crop-square:before{content:'\f37e'}.zmdi-exposure-alt:before{content:'\f37f'}.zmdi-exposure:before{content:'\f380'}.zmdi-filter-b-and-w:before{content:'\f381'}.zmdi-filter-center-focus:before{content:'\f382'}.zmdi-filter-frames:before{content:'\f383'}.zmdi-filter-tilt-shift:before{content:'\f384'}.zmdi-gradient:before{content:'\f385'}.zmdi-grain:before{content:'\f386'}.zmdi-graphic-eq:before{content:'\f387'}.zmdi-hdr-off:before{content:'\f388'}.zmdi-hdr-strong:before{content:'\f389'}.zmdi-hdr-weak:before{content:'\f38a'}.zmdi-hdr:before{content:'\f38b'}.zmdi-iridescent:before{content:'\f38c'}.zmdi-leak-off:before{content:'\f38d'}.zmdi-leak:before{content:'\f38e'}.zmdi-looks:before{content:'\f38f'}.zmdi-loupe:before{content:'\f390'}.zmdi-panorama-horizontal:before{content:'\f391'}.zmdi-panorama-vertical:before{content:'\f392'}.zmdi-panorama-wide-angle:before{content:'\f393'}.zmdi-photo-size-select-large:before{content:'\f394'}.zmdi-photo-size-select-small:before{content:'\f395'}.zmdi-picture-in-picture:before{content:'\f396'}.zmdi-slideshow:before{content:'\f397'}.zmdi-texture:before{content:'\f398'}.zmdi-tonality:before{content:'\f399'}.zmdi-vignette:before{content:'\f39a'}.zmdi-wb-auto:before{content:'\f39b'}.zmdi-eject-alt:before{content:'\f39c'}.zmdi-eject:before{content:'\f39d'}.zmdi-equalizer:before{content:'\f39e'}.zmdi-fast-forward:before{content:'\f39f'}.zmdi-fast-rewind:before{content:'\f3a0'}.zmdi-forward-10:before{content:'\f3a1'}.zmdi-forward-30:before{content:'\f3a2'}.zmdi-forward-5:before{content:'\f3a3'}.zmdi-hearing:before{content:'\f3a4'}.zmdi-pause-circle-outline:before{content:'\f3a5'}.zmdi-pause-circle:before{content:'\f3a6'}.zmdi-pause:before{content:'\f3a7'}.zmdi-play-circle-outline:before{content:'\f3a8'}.zmdi-play-circle:before{content:'\f3a9'}.zmdi-play:before{content:'\f3aa'}.zmdi-playlist-audio:before{content:'\f3ab'}.zmdi-playlist-plus:before{content:'\f3ac'}.zmdi-repeat-one:before{content:'\f3ad'}.zmdi-repeat:before{content:'\f3ae'}.zmdi-replay-10:before{content:'\f3af'}.zmdi-replay-30:before{content:'\f3b0'}.zmdi-replay-5:before{content:'\f3b1'}.zmdi-replay:before{content:'\f3b2'}.zmdi-shuffle:before{content:'\f3b3'}.zmdi-skip-next:before{content:'\f3b4'}.zmdi-skip-previous:before{content:'\f3b5'}.zmdi-stop:before{content:'\f3b6'}.zmdi-surround-sound:before{content:'\f3b7'}.zmdi-tune:before{content:'\f3b8'}.zmdi-volume-down:before{content:'\f3b9'}.zmdi-volume-mute:before{content:'\f3ba'}.zmdi-volume-off:before{content:'\f3bb'}.zmdi-volume-up:before{content:'\f3bc'}.zmdi-n-1-square:before{content:'\f3bd'}.zmdi-n-2-square:before{content:'\f3be'}.zmdi-n-3-square:before{content:'\f3bf'}.zmdi-n-4-square:before{content:'\f3c0'}.zmdi-n-5-square:before{content:'\f3c1'}.zmdi-n-6-square:before{content:'\f3c2'}.zmdi-neg-1:before{content:'\f3c3'}.zmdi-neg-2:before{content:'\f3c4'}.zmdi-plus-1:before{content:'\f3c5'}.zmdi-plus-2:before{content:'\f3c6'}.zmdi-sec-10:before{content:'\f3c7'}.zmdi-sec-3:before{content:'\f3c8'}.zmdi-zero:before{content:'\f3c9'}.zmdi-airline-seat-flat-angled:before{content:'\f3ca'}.zmdi-airline-seat-flat:before{content:'\f3cb'}.zmdi-airline-seat-individual-suite:before{content:'\f3cc'}.zmdi-airline-seat-legroom-extra:before{content:'\f3cd'}.zmdi-airline-seat-legroom-normal:before{content:'\f3ce'}.zmdi-airline-seat-legroom-reduced:before{content:'\f3cf'}.zmdi-airline-seat-recline-extra:before{content:'\f3d0'}.zmdi-airline-seat-recline-normal:before{content:'\f3d1'}.zmdi-airplay:before{content:'\f3d2'}.zmdi-closed-caption:before{content:'\f3d3'}.zmdi-confirmation-number:before{content:'\f3d4'}.zmdi-developer-board:before{content:'\f3d5'}.zmdi-disc-full:before{content:'\f3d6'}.zmdi-explicit:before{content:'\f3d7'}.zmdi-flight-land:before{content:'\f3d8'}.zmdi-flight-takeoff:before{content:'\f3d9'}.zmdi-flip-to-back:before{content:'\f3da'}.zmdi-flip-to-front:before{content:'\f3db'}.zmdi-group-work:before{content:'\f3dc'}.zmdi-hd:before{content:'\f3dd'}.zmdi-hq:before{content:'\f3de'}.zmdi-markunread-mailbox:before{content:'\f3df'}.zmdi-memory:before{content:'\f3e0'}.zmdi-nfc:before{content:'\f3e1'}.zmdi-play-for-work:before{content:'\f3e2'}.zmdi-power-input:before{content:'\f3e3'}.zmdi-present-to-all:before{content:'\f3e4'}.zmdi-satellite:before{content:'\f3e5'}.zmdi-tap-and-play:before{content:'\f3e6'}.zmdi-vibration:before{content:'\f3e7'}.zmdi-voicemail:before{content:'\f3e8'}.zmdi-group:before{content:'\f3e9'}.zmdi-rss:before{content:'\f3ea'}.zmdi-shape:before{content:'\f3eb'}.zmdi-spinner:before{content:'\f3ec'}.zmdi-ungroup:before{content:'\f3ed'}.zmdi-500px:before{content:'\f3ee'}.zmdi-8tracks:before{content:'\f3ef'}.zmdi-amazon:before{content:'\f3f0'}.zmdi-blogger:before{content:'\f3f1'}.zmdi-delicious:before{content:'\f3f2'}.zmdi-disqus:before{content:'\f3f3'}.zmdi-flattr:before{content:'\f3f4'}.zmdi-flickr:before{content:'\f3f5'}.zmdi-github-alt:before{content:'\f3f6'}.zmdi-google-old:before{content:'\f3f7'}.zmdi-linkedin:before{content:'\f3f8'}.zmdi-odnoklassniki:before{content:'\f3f9'}.zmdi-outlook:before{content:'\f3fa'}.zmdi-paypal-alt:before{content:'\f3fb'}.zmdi-pinterest:before{content:'\f3fc'}.zmdi-playstation:before{content:'\f3fd'}.zmdi-reddit:before{content:'\f3fe'}.zmdi-skype:before{content:'\f3ff'}.zmdi-slideshare:before{content:'\f400'}.zmdi-soundcloud:before{content:'\f401'}.zmdi-tumblr:before{content:'\f402'}.zmdi-twitch:before{content:'\f403'}.zmdi-vimeo:before{content:'\f404'}.zmdi-whatsapp:before{content:'\f405'}.zmdi-xbox:before{content:'\f406'}.zmdi-yahoo:before{content:'\f407'}.zmdi-youtube-play:before{content:'\f408'}.zmdi-youtube:before{content:'\f409'}.zmdi-3d-rotation:before{content:'\f101'}.zmdi-airplane-off:before{content:'\f102'}.zmdi-airplane:before{content:'\f103'}.zmdi-album:before{content:'\f104'}.zmdi-archive:before{content:'\f105'}.zmdi-assignment-account:before{content:'\f106'}.zmdi-assignment-alert:before{content:'\f107'}.zmdi-assignment-check:before{content:'\f108'}.zmdi-assignment-o:before{content:'\f109'}.zmdi-assignment-return:before{content:'\f10a'}.zmdi-assignment-returned:before{content:'\f10b'}.zmdi-assignment:before{content:'\f10c'}.zmdi-attachment-alt:before{content:'\f10d'}.zmdi-attachment:before{content:'\f10e'}.zmdi-audio:before{content:'\f10f'}.zmdi-badge-check:before{content:'\f110'}.zmdi-balance-wallet:before{content:'\f111'}.zmdi-balance:before{content:'\f112'}.zmdi-battery-alert:before{content:'\f113'}.zmdi-battery-flash:before{content:'\f114'}.zmdi-battery-unknown:before{content:'\f115'}.zmdi-battery:before{content:'\f116'}.zmdi-bike:before{content:'\f117'}.zmdi-block-alt:before{content:'\f118'}.zmdi-block:before{content:'\f119'}.zmdi-boat:before{content:'\f11a'}.zmdi-book-image:before{content:'\f11b'}.zmdi-book:before{content:'\f11c'}.zmdi-bookmark-outline:before{content:'\f11d'}.zmdi-bookmark:before{content:'\f11e'}.zmdi-brush:before{content:'\f11f'}.zmdi-bug:before{content:'\f120'}.zmdi-bus:before{content:'\f121'}.zmdi-cake:before{content:'\f122'}.zmdi-car-taxi:before{content:'\f123'}.zmdi-car-wash:before{content:'\f124'}.zmdi-car:before{content:'\f125'}.zmdi-card-giftcard:before{content:'\f126'}.zmdi-card-membership:before{content:'\f127'}.zmdi-card-travel:before{content:'\f128'}.zmdi-card:before{content:'\f129'}.zmdi-case-check:before{content:'\f12a'}.zmdi-case-download:before{content:'\f12b'}.zmdi-case-play:before{content:'\f12c'}.zmdi-case:before{content:'\f12d'}.zmdi-cast-connected:before{content:'\f12e'}.zmdi-cast:before{content:'\f12f'}.zmdi-chart-donut:before{content:'\f130'}.zmdi-chart:before{content:'\f131'}.zmdi-city-alt:before{content:'\f132'}.zmdi-city:before{content:'\f133'}.zmdi-close-circle-o:before{content:'\f134'}.zmdi-close-circle:before{content:'\f135'}.zmdi-close:before{content:'\f136'}.zmdi-cocktail:before{content:'\f137'}.zmdi-code-setting:before{content:'\f138'}.zmdi-code-smartphone:before{content:'\f139'}.zmdi-code:before{content:'\f13a'}.zmdi-coffee:before{content:'\f13b'}.zmdi-collection-bookmark:before{content:'\f13c'}.zmdi-collection-case-play:before{content:'\f13d'}.zmdi-collection-folder-image:before{content:'\f13e'}.zmdi-collection-image-o:before{content:'\f13f'}.zmdi-collection-image:before{content:'\f140'}.zmdi-collection-item-1:before{content:'\f141'}.zmdi-collection-item-2:before{content:'\f142'}.zmdi-collection-item-3:before{content:'\f143'}.zmdi-collection-item-4:before{content:'\f144'}.zmdi-collection-item-5:before{content:'\f145'}.zmdi-collection-item-6:before{content:'\f146'}.zmdi-collection-item-7:before{content:'\f147'}.zmdi-collection-item-8:before{content:'\f148'}.zmdi-collection-item-9-plus:before{content:'\f149'}.zmdi-collection-item-9:before{content:'\f14a'}.zmdi-collection-item:before{content:'\f14b'}.zmdi-collection-music:before{content:'\f14c'}.zmdi-collection-pdf:before{content:'\f14d'}.zmdi-collection-plus:before{content:'\f14e'}.zmdi-collection-speaker:before{content:'\f14f'}.zmdi-collection-text:before{content:'\f150'}.zmdi-collection-video:before{content:'\f151'}.zmdi-compass:before{content:'\f152'}.zmdi-cutlery:before{content:'\f153'}.zmdi-delete:before{content:'\f154'}.zmdi-dialpad:before{content:'\f155'}.zmdi-dns:before{content:'\f156'}.zmdi-drink:before{content:'\f157'}.zmdi-edit:before{content:'\f158'}.zmdi-email-open:before{content:'\f159'}.zmdi-email:before{content:'\f15a'}.zmdi-eye-off:before{content:'\f15b'}.zmdi-eye:before{content:'\f15c'}.zmdi-eyedropper:before{content:'\f15d'}.zmdi-favorite-outline:before{content:'\f15e'}.zmdi-favorite:before{content:'\f15f'}.zmdi-filter-list:before{content:'\f160'}.zmdi-fire:before{content:'\f161'}.zmdi-flag:before{content:'\f162'}.zmdi-flare:before{content:'\f163'}.zmdi-flash-auto:before{content:'\f164'}.zmdi-flash-off:before{content:'\f165'}.zmdi-flash:before{content:'\f166'}.zmdi-flip:before{content:'\f167'}.zmdi-flower-alt:before{content:'\f168'}.zmdi-flower:before{content:'\f169'}.zmdi-font:before{content:'\f16a'}.zmdi-fullscreen-alt:before{content:'\f16b'}.zmdi-fullscreen-exit:before{content:'\f16c'}.zmdi-fullscreen:before{content:'\f16d'}.zmdi-functions:before{content:'\f16e'}.zmdi-gas-station:before{content:'\f16f'}.zmdi-gesture:before{content:'\f170'}.zmdi-globe-alt:before{content:'\f171'}.zmdi-globe-lock:before{content:'\f172'}.zmdi-globe:before{content:'\f173'}.zmdi-graduation-cap:before{content:'\f174'}.zmdi-home:before{content:'\f175'}.zmdi-hospital-alt:before{content:'\f176'}.zmdi-hospital:before{content:'\f177'}.zmdi-hotel:before{content:'\f178'}.zmdi-hourglass-alt:before{content:'\f179'}.zmdi-hourglass-outline:before{content:'\f17a'}.zmdi-hourglass:before{content:'\f17b'}.zmdi-http:before{content:'\f17c'}.zmdi-image-alt:before{content:'\f17d'}.zmdi-image-o:before{content:'\f17e'}.zmdi-image:before{content:'\f17f'}.zmdi-inbox:before{content:'\f180'}.zmdi-invert-colors-off:before{content:'\f181'}.zmdi-invert-colors:before{content:'\f182'}.zmdi-key:before{content:'\f183'}.zmdi-label-alt-outline:before{content:'\f184'}.zmdi-label-alt:before{content:'\f185'}.zmdi-label-heart:before{content:'\f186'}.zmdi-label:before{content:'\f187'}.zmdi-labels:before{content:'\f188'}.zmdi-lamp:before{content:'\f189'}.zmdi-landscape:before{content:'\f18a'}.zmdi-layers-off:before{content:'\f18b'}.zmdi-layers:before{content:'\f18c'}.zmdi-library:before{content:'\f18d'}.zmdi-link:before{content:'\f18e'}.zmdi-lock-open:before{content:'\f18f'}.zmdi-lock-outline:before{content:'\f190'}.zmdi-lock:before{content:'\f191'}.zmdi-mail-reply-all:before{content:'\f192'}.zmdi-mail-reply:before{content:'\f193'}.zmdi-mail-send:before{content:'\f194'}.zmdi-mall:before{content:'\f195'}.zmdi-map:before{content:'\f196'}.zmdi-menu:before{content:'\f197'}.zmdi-money-box:before{content:'\f198'}.zmdi-money-off:before{content:'\f199'}.zmdi-money:before{content:'\f19a'}.zmdi-more-vert:before{content:'\f19b'}.zmdi-more:before{content:'\f19c'}.zmdi-movie-alt:before{content:'\f19d'}.zmdi-movie:before{content:'\f19e'}.zmdi-nature-people:before{content:'\f19f'}.zmdi-nature:before{content:'\f1a0'}.zmdi-navigation:before{content:'\f1a1'}.zmdi-open-in-browser:before{content:'\f1a2'}.zmdi-open-in-new:before{content:'\f1a3'}.zmdi-palette:before{content:'\f1a4'}.zmdi-parking:before{content:'\f1a5'}.zmdi-pin-account:before{content:'\f1a6'}.zmdi-pin-assistant:before{content:'\f1a7'}.zmdi-pin-drop:before{content:'\f1a8'}.zmdi-pin-help:before{content:'\f1a9'}.zmdi-pin-off:before{content:'\f1aa'}.zmdi-pin:before{content:'\f1ab'}.zmdi-pizza:before{content:'\f1ac'}.zmdi-plaster:before{content:'\f1ad'}.zmdi-power-setting:before{content:'\f1ae'}.zmdi-power:before{content:'\f1af'}.zmdi-print:before{content:'\f1b0'}.zmdi-puzzle-piece:before{content:'\f1b1'}.zmdi-quote:before{content:'\f1b2'}.zmdi-railway:before{content:'\f1b3'}.zmdi-receipt:before{content:'\f1b4'}.zmdi-refresh-alt:before{content:'\f1b5'}.zmdi-refresh-sync-alert:before{content:'\f1b6'}.zmdi-refresh-sync-off:before{content:'\f1b7'}.zmdi-refresh-sync:before{content:'\f1b8'}.zmdi-refresh:before{content:'\f1b9'}.zmdi-roller:before{content:'\f1ba'}.zmdi-ruler:before{content:'\f1bb'}.zmdi-scissors:before{content:'\f1bc'}.zmdi-screen-rotation-lock:before{content:'\f1bd'}.zmdi-screen-rotation:before{content:'\f1be'}.zmdi-search-for:before{content:'\f1bf'}.zmdi-search-in-file:before{content:'\f1c0'}.zmdi-search-in-page:before{content:'\f1c1'}.zmdi-search-replace:before{content:'\f1c2'}.zmdi-search:before{content:'\f1c3'}.zmdi-seat:before{content:'\f1c4'}.zmdi-settings-square:before{content:'\f1c5'}.zmdi-settings:before{content:'\f1c6'}.zmdi-shield-check:before{content:'\f1c7'}.zmdi-shield-security:before{content:'\f1c8'}.zmdi-shopping-basket:before{content:'\f1c9'}.zmdi-shopping-cart-plus:before{content:'\f1ca'}.zmdi-shopping-cart:before{content:'\f1cb'}.zmdi-sign-in:before{content:'\f1cc'}.zmdi-sort-amount-asc:before{content:'\f1cd'}.zmdi-sort-amount-desc:before{content:'\f1ce'}.zmdi-sort-asc:before{content:'\f1cf'}.zmdi-sort-desc:before{content:'\f1d0'}.zmdi-spellcheck:before{content:'\f1d1'}.zmdi-storage:before{content:'\f1d2'}.zmdi-store-24:before{content:'\f1d3'}.zmdi-store:before{content:'\f1d4'}.zmdi-subway:before{content:'\f1d5'}.zmdi-sun:before{content:'\f1d6'}.zmdi-tab-unselected:before{content:'\f1d7'}.zmdi-tab:before{content:'\f1d8'}.zmdi-tag-close:before{content:'\f1d9'}.zmdi-tag-more:before{content:'\f1da'}.zmdi-tag:before{content:'\f1db'}.zmdi-thumb-down:before{content:'\f1dc'}.zmdi-thumb-up-down:before{content:'\f1dd'}.zmdi-thumb-up:before{content:'\f1de'}.zmdi-ticket-star:before{content:'\f1df'}.zmdi-toll:before{content:'\f1e0'}.zmdi-toys:before{content:'\f1e1'}.zmdi-traffic:before{content:'\f1e2'}.zmdi-translate:before{content:'\f1e3'}.zmdi-triangle-down:before{content:'\f1e4'}.zmdi-triangle-up:before{content:'\f1e5'}.zmdi-truck:before{content:'\f1e6'}.zmdi-turning-sign:before{content:'\f1e7'}.zmdi-wallpaper:before{content:'\f1e8'}.zmdi-washing-machine:before{content:'\f1e9'}.zmdi-window-maximize:before{content:'\f1ea'}.zmdi-window-minimize:before{content:'\f1eb'}.zmdi-window-restore:before{content:'\f1ec'}.zmdi-wrench:before{content:'\f1ed'}.zmdi-zoom-in:before{content:'\f1ee'}.zmdi-zoom-out:before{content:'\f1ef'}.zmdi-alert-circle-o:before{content:'\f1f0'}.zmdi-alert-circle:before{content:'\f1f1'}.zmdi-alert-octagon:before{content:'\f1f2'}.zmdi-alert-polygon:before{content:'\f1f3'}.zmdi-alert-triangle:before{content:'\f1f4'}.zmdi-help-outline:before{content:'\f1f5'}.zmdi-help:before{content:'\f1f6'}.zmdi-info-outline:before{content:'\f1f7'}.zmdi-info:before{content:'\f1f8'}.zmdi-notifications-active:before{content:'\f1f9'}.zmdi-notifications-add:before{content:'\f1fa'}.zmdi-notifications-none:before{content:'\f1fb'}.zmdi-notifications-off:before{content:'\f1fc'}.zmdi-notifications-paused:before{content:'\f1fd'}.zmdi-notifications:before{content:'\f1fe'}.zmdi-account-add:before{content:'\f1ff'}.zmdi-account-box-mail:before{content:'\f200'}.zmdi-account-box-o:before{content:'\f201'}.zmdi-account-box-phone:before{content:'\f202'}.zmdi-account-box:before{content:'\f203'}.zmdi-account-calendar:before{content:'\f204'}.zmdi-account-circle:before{content:'\f205'}.zmdi-account-o:before{content:'\f206'}.zmdi-account:before{content:'\f207'}.zmdi-accounts-add:before{content:'\f208'}.zmdi-accounts-alt:before{content:'\f209'}.zmdi-accounts-list-alt:before{content:'\f20a'}.zmdi-accounts-list:before{content:'\f20b'}.zmdi-accounts-outline:before{content:'\f20c'}.zmdi-accounts:before{content:'\f20d'}.zmdi-face:before{content:'\f20e'}.zmdi-female:before{content:'\f20f'}.zmdi-male-alt:before{content:'\f210'}.zmdi-male-female:before{content:'\f211'}.zmdi-male:before{content:'\f212'}.zmdi-mood-bad:before{content:'\f213'}.zmdi-mood:before{content:'\f214'}.zmdi-run:before{content:'\f215'}.zmdi-walk:before{content:'\f216'}.zmdi-cloud-box:before{content:'\f217'}.zmdi-cloud-circle:before{content:'\f218'}.zmdi-cloud-done:before{content:'\f219'}.zmdi-cloud-download:before{content:'\f21a'}.zmdi-cloud-off:before{content:'\f21b'}.zmdi-cloud-outline-alt:before{content:'\f21c'}.zmdi-cloud-outline:before{content:'\f21d'}.zmdi-cloud-upload:before{content:'\f21e'}.zmdi-cloud:before{content:'\f21f'}.zmdi-download:before{content:'\f220'}.zmdi-file-plus:before{content:'\f221'}.zmdi-file-text:before{content:'\f222'}.zmdi-file:before{content:'\f223'}.zmdi-folder-outline:before{content:'\f224'}.zmdi-folder-person:before{content:'\f225'}.zmdi-folder-star-alt:before{content:'\f226'}.zmdi-folder-star:before{content:'\f227'}.zmdi-folder:before{content:'\f228'}.zmdi-gif:before{content:'\f229'}.zmdi-upload:before{content:'\f22a'}.zmdi-border-all:before{content:'\f22b'}.zmdi-border-bottom:before{content:'\f22c'}.zmdi-border-clear:before{content:'\f22d'}.zmdi-border-color:before{content:'\f22e'}.zmdi-border-horizontal:before{content:'\f22f'}.zmdi-border-inner:before{content:'\f230'}.zmdi-border-left:before{content:'\f231'}.zmdi-border-outer:before{content:'\f232'}.zmdi-border-right:before{content:'\f233'}.zmdi-border-style:before{content:'\f234'}.zmdi-border-top:before{content:'\f235'}.zmdi-border-vertical:before{content:'\f236'}.zmdi-copy:before{content:'\f237'}.zmdi-crop:before{content:'\f238'}.zmdi-format-align-center:before{content:'\f239'}.zmdi-format-align-justify:before{content:'\f23a'}.zmdi-format-align-left:before{content:'\f23b'}.zmdi-format-align-right:before{content:'\f23c'}.zmdi-format-bold:before{content:'\f23d'}.zmdi-format-clear-all:before{content:'\f23e'}.zmdi-format-clear:before{content:'\f23f'}.zmdi-format-color-fill:before{content:'\f240'}.zmdi-format-color-reset:before{content:'\f241'}.zmdi-format-color-text:before{content:'\f242'}.zmdi-format-indent-decrease:before{content:'\f243'}.zmdi-format-indent-increase:before{content:'\f244'}.zmdi-format-italic:before{content:'\f245'}.zmdi-format-line-spacing:before{content:'\f246'}.zmdi-format-list-bulleted:before{content:'\f247'}.zmdi-format-list-numbered:before{content:'\f248'}.zmdi-format-ltr:before{content:'\f249'}.zmdi-format-rtl:before{content:'\f24a'}.zmdi-format-size:before{content:'\f24b'}.zmdi-format-strikethrough-s:before{content:'\f24c'}.zmdi-format-strikethrough:before{content:'\f24d'}.zmdi-format-subject:before{content:'\f24e'}.zmdi-format-underlined:before{content:'\f24f'}.zmdi-format-valign-bottom:before{content:'\f250'}.zmdi-format-valign-center:before{content:'\f251'}.zmdi-format-valign-top:before{content:'\f252'}.zmdi-redo:before{content:'\f253'}.zmdi-select-all:before{content:'\f254'}.zmdi-space-bar:before{content:'\f255'}.zmdi-text-format:before{content:'\f256'}.zmdi-transform:before{content:'\f257'}.zmdi-undo:before{content:'\f258'}.zmdi-wrap-text:before{content:'\f259'}.zmdi-comment-alert:before{content:'\f25a'}.zmdi-comment-alt-text:before{content:'\f25b'}.zmdi-comment-alt:before{content:'\f25c'}.zmdi-comment-edit:before{content:'\f25d'}.zmdi-comment-image:before{content:'\f25e'}.zmdi-comment-list:before{content:'\f25f'}.zmdi-comment-more:before{content:'\f260'}.zmdi-comment-outline:before{content:'\f261'}.zmdi-comment-text-alt:before{content:'\f262'}.zmdi-comment-text:before{content:'\f263'}.zmdi-comment-video:before{content:'\f264'}.zmdi-comment:before{content:'\f265'}.zmdi-comments:before{content:'\f266'}.zmdi-check-all:before{content:'\f267'}.zmdi-check-circle-u:before{content:'\f268'}.zmdi-check-circle:before{content:'\f269'}.zmdi-check-square:before{content:'\f26a'}.zmdi-check:before{content:'\f26b'}.zmdi-circle-o:before{content:'\f26c'}.zmdi-circle:before{content:'\f26d'}.zmdi-dot-circle-alt:before{content:'\f26e'}.zmdi-dot-circle:before{content:'\f26f'}.zmdi-minus-circle-outline:before{content:'\f270'}.zmdi-minus-circle:before{content:'\f271'}.zmdi-minus-square:before{content:'\f272'}.zmdi-minus:before{content:'\f273'}.zmdi-plus-circle-o-duplicate:before{content:'\f274'}.zmdi-plus-circle-o:before{content:'\f275'}.zmdi-plus-circle:before{content:'\f276'}.zmdi-plus-square:before{content:'\f277'}.zmdi-plus:before{content:'\f278'}.zmdi-square-o:before{content:'\f279'}.zmdi-star-circle:before{content:'\f27a'}.zmdi-star-half:before{content:'\f27b'}.zmdi-star-outline:before{content:'\f27c'}.zmdi-star:before{content:'\f27d'}.zmdi-bluetooth-connected:before{content:'\f27e'}.zmdi-bluetooth-off:before{content:'\f27f'}.zmdi-bluetooth-search:before{content:'\f280'}.zmdi-bluetooth-setting:before{content:'\f281'}.zmdi-bluetooth:before{content:'\f282'}.zmdi-camera-add:before{content:'\f283'}.zmdi-camera-alt:before{content:'\f284'}.zmdi-camera-bw:before{content:'\f285'}.zmdi-camera-front:before{content:'\f286'}.zmdi-camera-mic:before{content:'\f287'}.zmdi-camera-party-mode:before{content:'\f288'}.zmdi-camera-rear:before{content:'\f289'}.zmdi-camera-roll:before{content:'\f28a'}.zmdi-camera-switch:before{content:'\f28b'}.zmdi-camera:before{content:'\f28c'}.zmdi-card-alert:before{content:'\f28d'}.zmdi-card-off:before{content:'\f28e'}.zmdi-card-sd:before{content:'\f28f'}.zmdi-card-sim:before{content:'\f290'}.zmdi-desktop-mac:before{content:'\f291'}.zmdi-desktop-windows:before{content:'\f292'}.zmdi-device-hub:before{content:'\f293'}.zmdi-devices-off:before{content:'\f294'}.zmdi-devices:before{content:'\f295'}.zmdi-dock:before{content:'\f296'}.zmdi-floppy:before{content:'\f297'}.zmdi-gamepad:before{content:'\f298'}.zmdi-gps-dot:before{content:'\f299'}.zmdi-gps-off:before{content:'\f29a'}.zmdi-gps:before{content:'\f29b'}.zmdi-headset-mic:before{content:'\f29c'}.zmdi-headset:before{content:'\f29d'}.zmdi-input-antenna:before{content:'\f29e'}.zmdi-input-composite:before{content:'\f29f'}.zmdi-input-hdmi:before{content:'\f2a0'}.zmdi-input-power:before{content:'\f2a1'}.zmdi-input-svideo:before{content:'\f2a2'}.zmdi-keyboard-hide:before{content:'\f2a3'}.zmdi-keyboard:before{content:'\f2a4'}.zmdi-laptop-chromebook:before{content:'\f2a5'}.zmdi-laptop-mac:before{content:'\f2a6'}.zmdi-laptop:before{content:'\f2a7'}.zmdi-mic-off:before{content:'\f2a8'}.zmdi-mic-outline:before{content:'\f2a9'}.zmdi-mic-setting:before{content:'\f2aa'}.zmdi-mic:before{content:'\f2ab'}.zmdi-mouse:before{content:'\f2ac'}.zmdi-network-alert:before{content:'\f2ad'}.zmdi-network-locked:before{content:'\f2ae'}.zmdi-network-off:before{content:'\f2af'}.zmdi-network-outline:before{content:'\f2b0'}.zmdi-network-setting:before{content:'\f2b1'}.zmdi-network:before{content:'\f2b2'}.zmdi-phone-bluetooth:before{content:'\f2b3'}.zmdi-phone-end:before{content:'\f2b4'}.zmdi-phone-forwarded:before{content:'\f2b5'}.zmdi-phone-in-talk:before{content:'\f2b6'}.zmdi-phone-locked:before{content:'\f2b7'}.zmdi-phone-missed:before{content:'\f2b8'}.zmdi-phone-msg:before{content:'\f2b9'}.zmdi-phone-paused:before{content:'\f2ba'}.zmdi-phone-ring:before{content:'\f2bb'}.zmdi-phone-setting:before{content:'\f2bc'}.zmdi-phone-sip:before{content:'\f2bd'}.zmdi-phone:before{content:'\f2be'}.zmdi-portable-wifi-changes:before{content:'\f2bf'}.zmdi-portable-wifi-off:before{content:'\f2c0'}.zmdi-portable-wifi:before{content:'\f2c1'}.zmdi-radio:before{content:'\f2c2'}.zmdi-reader:before{content:'\f2c3'}.zmdi-remote-control-alt:before{content:'\f2c4'}.zmdi-remote-control:before{content:'\f2c5'}.zmdi-router:before{content:'\f2c6'}.zmdi-scanner:before{content:'\f2c7'}.zmdi-smartphone-android:before{content:'\f2c8'}.zmdi-smartphone-download:before{content:'\f2c9'}.zmdi-smartphone-erase:before{content:'\f2ca'}.zmdi-smartphone-info:before{content:'\f2cb'}.zmdi-smartphone-iphone:before{content:'\f2cc'}.zmdi-smartphone-landscape-lock:before{content:'\f2cd'}.zmdi-smartphone-landscape:before{content:'\f2ce'}.zmdi-smartphone-lock:before{content:'\f2cf'}.zmdi-smartphone-portrait-lock:before{content:'\f2d0'}.zmdi-smartphone-ring:before{content:'\f2d1'}.zmdi-smartphone-setting:before{content:'\f2d2'}.zmdi-smartphone-setup:before{content:'\f2d3'}.zmdi-smartphone:before{content:'\f2d4'}.zmdi-speaker:before{content:'\f2d5'}.zmdi-tablet-android:before{content:'\f2d6'}.zmdi-tablet-mac:before{content:'\f2d7'}.zmdi-tablet:before{content:'\f2d8'}.zmdi-tv-alt-play:before{content:'\f2d9'}.zmdi-tv-list:before{content:'\f2da'}.zmdi-tv-play:before{content:'\f2db'}.zmdi-tv:before{content:'\f2dc'}.zmdi-usb:before{content:'\f2dd'}.zmdi-videocam-off:before{content:'\f2de'}.zmdi-videocam-switch:before{content:'\f2df'}.zmdi-videocam:before{content:'\f2e0'}.zmdi-watch:before{content:'\f2e1'}.zmdi-wifi-alt-2:before{content:'\f2e2'}.zmdi-wifi-alt:before{content:'\f2e3'}.zmdi-wifi-info:before{content:'\f2e4'}.zmdi-wifi-lock:before{content:'\f2e5'}.zmdi-wifi-off:before{content:'\f2e6'}.zmdi-wifi-outline:before{content:'\f2e7'}.zmdi-wifi:before{content:'\f2e8'}.zmdi-arrow-left-bottom:before{content:'\f2e9'}.zmdi-arrow-left:before{content:'\f2ea'}.zmdi-arrow-merge:before{content:'\f2eb'}.zmdi-arrow-missed:before{content:'\f2ec'}.zmdi-arrow-right-top:before{content:'\f2ed'}.zmdi-arrow-right:before{content:'\f2ee'}.zmdi-arrow-split:before{content:'\f2ef'}.zmdi-arrows:before{content:'\f2f0'}.zmdi-caret-down-circle:before{content:'\f2f1'}.zmdi-caret-down:before{content:'\f2f2'}.zmdi-caret-left-circle:before{content:'\f2f3'}.zmdi-caret-left:before{content:'\f2f4'}.zmdi-caret-right-circle:before{content:'\f2f5'}.zmdi-caret-right:before{content:'\f2f6'}.zmdi-caret-up-circle:before{content:'\f2f7'}.zmdi-caret-up:before{content:'\f2f8'}.zmdi-chevron-down:before{content:'\f2f9'}.zmdi-chevron-left:before{content:'\f2fa'}.zmdi-chevron-right:before{content:'\f2fb'}.zmdi-chevron-up:before{content:'\f2fc'}.zmdi-forward:before{content:'\f2fd'}.zmdi-long-arrow-down:before{content:'\f2fe'}.zmdi-long-arrow-left:before{content:'\f2ff'}.zmdi-long-arrow-return:before{content:'\f300'}.zmdi-long-arrow-right:before{content:'\f301'}.zmdi-long-arrow-tab:before{content:'\f302'}.zmdi-long-arrow-up:before{content:'\f303'}.zmdi-rotate-ccw:before{content:'\f304'}.zmdi-rotate-cw:before{content:'\f305'}.zmdi-rotate-left:before{content:'\f306'}.zmdi-rotate-right:before{content:'\f307'}.zmdi-square-down:before{content:'\f308'}.zmdi-square-right:before{content:'\f309'}.zmdi-swap-alt:before{content:'\f30a'}.zmdi-swap-vertical-circle:before{content:'\f30b'}.zmdi-swap-vertical:before{content:'\f30c'}.zmdi-swap:before{content:'\f30d'}.zmdi-trending-down:before{content:'\f30e'}.zmdi-trending-flat:before{content:'\f30f'}.zmdi-trending-up:before{content:'\f310'}.zmdi-unfold-less:before{content:'\f311'}.zmdi-unfold-more:before{content:'\f312'}.zmdi-apps:before{content:'\f313'}.zmdi-grid-off:before{content:'\f314'}.zmdi-grid:before{content:'\f315'}.zmdi-view-agenda:before{content:'\f316'}.zmdi-view-array:before{content:'\f317'}.zmdi-view-carousel:before{content:'\f318'}.zmdi-view-column:before{content:'\f319'}.zmdi-view-comfy:before{content:'\f31a'}.zmdi-view-compact:before{content:'\f31b'}.zmdi-view-dashboard:before{content:'\f31c'}.zmdi-view-day:before{content:'\f31d'}.zmdi-view-headline:before{content:'\f31e'}.zmdi-view-list-alt:before{content:'\f31f'}.zmdi-view-list:before{content:'\f320'}.zmdi-view-module:before{content:'\f321'}.zmdi-view-quilt:before{content:'\f322'}.zmdi-view-stream:before{content:'\f323'}.zmdi-view-subtitles:before{content:'\f324'}.zmdi-view-toc:before{content:'\f325'}.zmdi-view-web:before{content:'\f326'}.zmdi-view-week:before{content:'\f327'}.zmdi-widgets:before{content:'\f328'}.zmdi-alarm-check:before{content:'\f329'}.zmdi-alarm-off:before{content:'\f32a'}.zmdi-alarm-plus:before{content:'\f32b'}.zmdi-alarm-snooze:before{content:'\f32c'}.zmdi-alarm:before{content:'\f32d'}.zmdi-calendar-alt:before{content:'\f32e'}.zmdi-calendar-check:before{content:'\f32f'}.zmdi-calendar-close:before{content:'\f330'}.zmdi-calendar-note:before{content:'\f331'}.zmdi-calendar:before{content:'\f332'}.zmdi-time-countdown:before{content:'\f333'}.zmdi-time-interval:before{content:'\f334'}.zmdi-time-restore-setting:before{content:'\f335'}.zmdi-time-restore:before{content:'\f336'}.zmdi-time:before{content:'\f337'}.zmdi-timer-off:before{content:'\f338'}.zmdi-timer:before{content:'\f339'}.zmdi-android-alt:before{content:'\f33a'}.zmdi-android:before{content:'\f33b'}.zmdi-apple:before{content:'\f33c'}.zmdi-behance:before{content:'\f33d'}.zmdi-codepen:before{content:'\f33e'}.zmdi-dribbble:before{content:'\f33f'}.zmdi-dropbox:before{content:'\f340'}.zmdi-evernote:before{content:'\f341'}.zmdi-facebook-box:before{content:'\f342'}.zmdi-facebook:before{content:'\f343'}.zmdi-github-box:before{content:'\f344'}.zmdi-github:before{content:'\f345'}.zmdi-google-drive:before{content:'\f346'}.zmdi-google-earth:before{content:'\f347'}.zmdi-google-glass:before{content:'\f348'}.zmdi-google-maps:before{content:'\f349'}.zmdi-google-pages:before{content:'\f34a'}.zmdi-google-play:before{content:'\f34b'}.zmdi-google-plus-box:before{content:'\f34c'}.zmdi-google-plus:before{content:'\f34d'}.zmdi-google:before{content:'\f34e'}.zmdi-instagram:before{content:'\f34f'}.zmdi-language-css3:before{content:'\f350'}.zmdi-language-html5:before{content:'\f351'}.zmdi-language-javascript:before{content:'\f352'}.zmdi-language-python-alt:before{content:'\f353'}.zmdi-language-python:before{content:'\f354'}.zmdi-lastfm:before{content:'\f355'}.zmdi-linkedin-box:before{content:'\f356'}.zmdi-paypal:before{content:'\f357'}.zmdi-pinterest-box:before{content:'\f358'}.zmdi-pocket:before{content:'\f359'}.zmdi-polymer:before{content:'\f35a'}.zmdi-share:before{content:'\f35b'}.zmdi-stackoverflow:before{content:'\f35c'}.zmdi-steam-square:before{content:'\f35d'}.zmdi-steam:before{content:'\f35e'}.zmdi-twitter-box:before{content:'\f35f'}.zmdi-twitter:before{content:'\f360'}.zmdi-vk:before{content:'\f361'}.zmdi-wikipedia:before{content:'\f362'}.zmdi-windows:before{content:'\f363'}.zmdi-aspect-ratio-alt:before{content:'\f364'}.zmdi-aspect-ratio:before{content:'\f365'}.zmdi-blur-circular:before{content:'\f366'}.zmdi-blur-linear:before{content:'\f367'}.zmdi-blur-off:before{content:'\f368'}.zmdi-blur:before{content:'\f369'}.zmdi-brightness-2:before{content:'\f36a'}.zmdi-brightness-3:before{content:'\f36b'}.zmdi-brightness-4:before{content:'\f36c'}.zmdi-brightness-5:before{content:'\f36d'}.zmdi-brightness-6:before{content:'\f36e'}.zmdi-brightness-7:before{content:'\f36f'}.zmdi-brightness-auto:before{content:'\f370'}.zmdi-brightness-setting:before{content:'\f371'}.zmdi-broken-image:before{content:'\f372'}.zmdi-center-focus-strong:before{content:'\f373'}.zmdi-center-focus-weak:before{content:'\f374'}.zmdi-compare:before{content:'\f375'}.zmdi-crop-16-9:before{content:'\f376'}.zmdi-crop-3-2:before{content:'\f377'}.zmdi-crop-5-4:before{content:'\f378'}.zmdi-crop-7-5:before{content:'\f379'}.zmdi-crop-din:before{content:'\f37a'}.zmdi-crop-free:before{content:'\f37b'}.zmdi-crop-landscape:before{content:'\f37c'}.zmdi-crop-portrait:before{content:'\f37d'}.zmdi-crop-square:before{content:'\f37e'}.zmdi-exposure-alt:before{content:'\f37f'}.zmdi-exposure:before{content:'\f380'}.zmdi-filter-b-and-w:before{content:'\f381'}.zmdi-filter-center-focus:before{content:'\f382'}.zmdi-filter-frames:before{content:'\f383'}.zmdi-filter-tilt-shift:before{content:'\f384'}.zmdi-gradient:before{content:'\f385'}.zmdi-grain:before{content:'\f386'}.zmdi-graphic-eq:before{content:'\f387'}.zmdi-hdr-off:before{content:'\f388'}.zmdi-hdr-strong:before{content:'\f389'}.zmdi-hdr-weak:before{content:'\f38a'}.zmdi-hdr:before{content:'\f38b'}.zmdi-iridescent:before{content:'\f38c'}.zmdi-leak-off:before{content:'\f38d'}.zmdi-leak:before{content:'\f38e'}.zmdi-looks:before{content:'\f38f'}.zmdi-loupe:before{content:'\f390'}.zmdi-panorama-horizontal:before{content:'\f391'}.zmdi-panorama-vertical:before{content:'\f392'}.zmdi-panorama-wide-angle:before{content:'\f393'}.zmdi-photo-size-select-large:before{content:'\f394'}.zmdi-photo-size-select-small:before{content:'\f395'}.zmdi-picture-in-picture:before{content:'\f396'}.zmdi-slideshow:before{content:'\f397'}.zmdi-texture:before{content:'\f398'}.zmdi-tonality:before{content:'\f399'}.zmdi-vignette:before{content:'\f39a'}.zmdi-wb-auto:before{content:'\f39b'}.zmdi-eject-alt:before{content:'\f39c'}.zmdi-eject:before{content:'\f39d'}.zmdi-equalizer:before{content:'\f39e'}.zmdi-fast-forward:before{content:'\f39f'}.zmdi-fast-rewind:before{content:'\f3a0'}.zmdi-forward-10:before{content:'\f3a1'}.zmdi-forward-30:before{content:'\f3a2'}.zmdi-forward-5:before{content:'\f3a3'}.zmdi-hearing:before{content:'\f3a4'}.zmdi-pause-circle-outline:before{content:'\f3a5'}.zmdi-pause-circle:before{content:'\f3a6'}.zmdi-pause:before{content:'\f3a7'}.zmdi-play-circle-outline:before{content:'\f3a8'}.zmdi-play-circle:before{content:'\f3a9'}.zmdi-play:before{content:'\f3aa'}.zmdi-playlist-audio:before{content:'\f3ab'}.zmdi-playlist-plus:before{content:'\f3ac'}.zmdi-repeat-one:before{content:'\f3ad'}.zmdi-repeat:before{content:'\f3ae'}.zmdi-replay-10:before{content:'\f3af'}.zmdi-replay-30:before{content:'\f3b0'}.zmdi-replay-5:before{content:'\f3b1'}.zmdi-replay:before{content:'\f3b2'}.zmdi-shuffle:before{content:'\f3b3'}.zmdi-skip-next:before{content:'\f3b4'}.zmdi-skip-previous:before{content:'\f3b5'}.zmdi-stop:before{content:'\f3b6'}.zmdi-surround-sound:before{content:'\f3b7'}.zmdi-tune:before{content:'\f3b8'}.zmdi-volume-down:before{content:'\f3b9'}.zmdi-volume-mute:before{content:'\f3ba'}.zmdi-volume-off:before{content:'\f3bb'}.zmdi-volume-up:before{content:'\f3bc'}.zmdi-n-1-square:before{content:'\f3bd'}.zmdi-n-2-square:before{content:'\f3be'}.zmdi-n-3-square:before{content:'\f3bf'}.zmdi-n-4-square:before{content:'\f3c0'}.zmdi-n-5-square:before{content:'\f3c1'}.zmdi-n-6-square:before{content:'\f3c2'}.zmdi-neg-1:before{content:'\f3c3'}.zmdi-neg-2:before{content:'\f3c4'}.zmdi-plus-1:before{content:'\f3c5'}.zmdi-plus-2:before{content:'\f3c6'}.zmdi-sec-10:before{content:'\f3c7'}.zmdi-sec-3:before{content:'\f3c8'}.zmdi-zero:before{content:'\f3c9'}.zmdi-airline-seat-flat-angled:before{content:'\f3ca'}.zmdi-airline-seat-flat:before{content:'\f3cb'}.zmdi-airline-seat-individual-suite:before{content:'\f3cc'}.zmdi-airline-seat-legroom-extra:before{content:'\f3cd'}.zmdi-airline-seat-legroom-normal:before{content:'\f3ce'}.zmdi-airline-seat-legroom-reduced:before{content:'\f3cf'}.zmdi-airline-seat-recline-extra:before{content:'\f3d0'}.zmdi-airline-seat-recline-normal:before{content:'\f3d1'}.zmdi-airplay:before{content:'\f3d2'}.zmdi-closed-caption:before{content:'\f3d3'}.zmdi-confirmation-number:before{content:'\f3d4'}.zmdi-developer-board:before{content:'\f3d5'}.zmdi-disc-full:before{content:'\f3d6'}.zmdi-explicit:before{content:'\f3d7'}.zmdi-flight-land:before{content:'\f3d8'}.zmdi-flight-takeoff:before{content:'\f3d9'}.zmdi-flip-to-back:before{content:'\f3da'}.zmdi-flip-to-front:before{content:'\f3db'}.zmdi-group-work:before{content:'\f3dc'}.zmdi-hd:before{content:'\f3dd'}.zmdi-hq:before{content:'\f3de'}.zmdi-markunread-mailbox:before{content:'\f3df'}.zmdi-memory:before{content:'\f3e0'}.zmdi-nfc:before{content:'\f3e1'}.zmdi-play-for-work:before{content:'\f3e2'}.zmdi-power-input:before{content:'\f3e3'}.zmdi-present-to-all:before{content:'\f3e4'}.zmdi-satellite:before{content:'\f3e5'}.zmdi-tap-and-play:before{content:'\f3e6'}.zmdi-vibration:before{content:'\f3e7'}.zmdi-voicemail:before{content:'\f3e8'}.zmdi-group:before{content:'\f3e9'}.zmdi-rss:before{content:'\f3ea'}.zmdi-shape:before{content:'\f3eb'}.zmdi-spinner:before{content:'\f3ec'}.zmdi-ungroup:before{content:'\f3ed'}.zmdi-500px:before{content:'\f3ee'}.zmdi-8tracks:before{content:'\f3ef'}.zmdi-amazon:before{content:'\f3f0'}.zmdi-blogger:before{content:'\f3f1'}.zmdi-delicious:before{content:'\f3f2'}.zmdi-disqus:before{content:'\f3f3'}.zmdi-flattr:before{content:'\f3f4'}.zmdi-flickr:before{content:'\f3f5'}.zmdi-github-alt:before{content:'\f3f6'}.zmdi-google-old:before{content:'\f3f7'}.zmdi-linkedin:before{content:'\f3f8'}.zmdi-odnoklassniki:before{content:'\f3f9'}.zmdi-outlook:before{content:'\f3fa'}.zmdi-paypal-alt:before{content:'\f3fb'}.zmdi-pinterest:before{content:'\f3fc'}.zmdi-playstation:before{content:'\f3fd'}.zmdi-reddit:before{content:'\f3fe'}.zmdi-skype:before{content:'\f3ff'}.zmdi-slideshare:before{content:'\f400'}.zmdi-soundcloud:before{content:'\f401'}.zmdi-tumblr:before{content:'\f402'}.zmdi-twitch:before{content:'\f403'}.zmdi-vimeo:before{content:'\f404'}.zmdi-whatsapp:before{content:'\f405'}.zmdi-xbox:before{content:'\f406'}.zmdi-yahoo:before{content:'\f407'}.zmdi-youtube-play:before{content:'\f408'}.zmdi-youtube:before{content:'\f409'}.zmdi-import-export:before{content:'\f30c'}.zmdi-swap-vertical-:before{content:'\f30c'}.zmdi-airplanemode-inactive:before{content:'\f102'}.zmdi-airplanemode-active:before{content:'\f103'}.zmdi-rate-review:before{content:'\f103'}.zmdi-comment-sign:before{content:'\f25a'}.zmdi-network-warning:before{content:'\f2ad'}.zmdi-shopping-cart-add:before{content:'\f1ca'}.zmdi-file-add:before{content:'\f221'}.zmdi-network-wifi-scan:before{content:'\f2e4'}.zmdi-collection-add:before{content:'\f14e'}.zmdi-format-playlist-add:before{content:'\f3ac'}.zmdi-format-queue-music:before{content:'\f3ab'}.zmdi-plus-box:before{content:'\f277'}.zmdi-tag-backspace:before{content:'\f1d9'}.zmdi-alarm-add:before{content:'\f32b'}.zmdi-battery-charging:before{content:'\f114'}.zmdi-daydream-setting:before{content:'\f217'}.zmdi-more-horiz:before{content:'\f19c'}.zmdi-book-photo:before{content:'\f11b'}.zmdi-incandescent:before{content:'\f189'}.zmdi-wb-iridescent:before{content:'\f38c'}.zmdi-calendar-remove:before{content:'\f330'}.zmdi-refresh-sync-disabled:before{content:'\f1b7'}.zmdi-refresh-sync-problem:before{content:'\f1b6'}.zmdi-crop-original:before{content:'\f17e'}.zmdi-power-off:before{content:'\f1af'}.zmdi-power-off-setting:before{content:'\f1ae'}.zmdi-leak-remove:before{content:'\f38d'}.zmdi-star-border:before{content:'\f27c'}.zmdi-brightness-low:before{content:'\f36d'}.zmdi-brightness-medium:before{content:'\f36e'}.zmdi-brightness-high:before{content:'\f36f'}.zmdi-smartphone-portrait:before{content:'\f2d4'}.zmdi-live-tv:before{content:'\f2d9'}.zmdi-format-textdirection-l-to-r:before{content:'\f249'}.zmdi-format-textdirection-r-to-l:before{content:'\f24a'}.zmdi-arrow-back:before{content:'\f2ea'}.zmdi-arrow-forward:before{content:'\f2ee'}.zmdi-arrow-in:before{content:'\f2e9'}.zmdi-arrow-out:before{content:'\f2ed'}.zmdi-rotate-90-degrees-ccw:before{content:'\f304'}.zmdi-adb:before{content:'\f33a'}.zmdi-network-wifi:before{content:'\f2e8'}.zmdi-network-wifi-alt:before{content:'\f2e3'}.zmdi-network-wifi-lock:before{content:'\f2e5'}.zmdi-network-wifi-off:before{content:'\f2e6'}.zmdi-network-wifi-outline:before{content:'\f2e7'}.zmdi-network-wifi-info:before{content:'\f2e4'}.zmdi-layers-clear:before{content:'\f18b'}.zmdi-colorize:before{content:'\f15d'}.zmdi-format-paint:before{content:'\f1ba'}.zmdi-format-quote:before{content:'\f1b2'}.zmdi-camera-monochrome-photos:before{content:'\f285'}.zmdi-sort-by-alpha:before{content:'\f1cf'}.zmdi-folder-shared:before{content:'\f225'}.zmdi-folder-special:before{content:'\f226'}.zmdi-comment-dots:before{content:'\f260'}.zmdi-reorder:before{content:'\f31e'}.zmdi-dehaze:before{content:'\f197'}.zmdi-sort:before{content:'\f1ce'}.zmdi-pages:before{content:'\f34a'}.zmdi-stack-overflow:before{content:'\f35c'}.zmdi-calendar-account:before{content:'\f204'}.zmdi-paste:before{content:'\f109'}.zmdi-cut:before{content:'\f1bc'}.zmdi-save:before{content:'\f297'}.zmdi-smartphone-code:before{content:'\f139'}.zmdi-directions-bike:before{content:'\f117'}.zmdi-directions-boat:before{content:'\f11a'}.zmdi-directions-bus:before{content:'\f121'}.zmdi-directions-car:before{content:'\f125'}.zmdi-directions-railway:before{content:'\f1b3'}.zmdi-directions-run:before{content:'\f215'}.zmdi-directions-subway:before{content:'\f1d5'}.zmdi-directions-walk:before{content:'\f216'}.zmdi-local-hotel:before{content:'\f178'}.zmdi-local-activity:before{content:'\f1df'}.zmdi-local-play:before{content:'\f1df'}.zmdi-local-airport:before{content:'\f103'}.zmdi-local-atm:before{content:'\f198'}.zmdi-local-bar:before{content:'\f137'}.zmdi-local-cafe:before{content:'\f13b'}.zmdi-local-car-wash:before{content:'\f124'}.zmdi-local-convenience-store:before{content:'\f1d3'}.zmdi-local-dining:before{content:'\f153'}.zmdi-local-drink:before{content:'\f157'}.zmdi-local-florist:before{content:'\f168'}.zmdi-local-gas-station:before{content:'\f16f'}.zmdi-local-grocery-store:before{content:'\f1cb'}.zmdi-local-hospital:before{content:'\f177'}.zmdi-local-laundry-service:before{content:'\f1e9'}.zmdi-local-library:before{content:'\f18d'}.zmdi-local-mall:before{content:'\f195'}.zmdi-local-movies:before{content:'\f19d'}.zmdi-local-offer:before{content:'\f187'}.zmdi-local-parking:before{content:'\f1a5'}.zmdi-local-parking:before{content:'\f1a5'}.zmdi-local-pharmacy:before{content:'\f176'}.zmdi-local-phone:before{content:'\f2be'}.zmdi-local-pizza:before{content:'\f1ac'}.zmdi-local-post-office:before{content:'\f15a'}.zmdi-local-printshop:before{content:'\f1b0'}.zmdi-local-see:before{content:'\f28c'}.zmdi-local-shipping:before{content:'\f1e6'}.zmdi-local-store:before{content:'\f1d4'}.zmdi-local-taxi:before{content:'\f123'}.zmdi-local-wc:before{content:'\f211'}.zmdi-my-location:before{content:'\f299'}.zmdi-directions:before{content:'\f1e7'} \ No newline at end of file diff --git a/web/libs/css/material.style.css b/web/libs/css/material.style.css index 2cb8242..e971dee 100644 --- a/web/libs/css/material.style.css +++ b/web/libs/css/material.style.css @@ -608,15 +608,6 @@ _:-ms-input-placeholder, :root .demo-graph { opacity: 0.46; border-radius: 2px; } -.social-btn__twitter { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png'); -} -.social-btn__blogger { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png'); -} -.social-btn__gplus { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_gplus_black_24dp.png'); -} .social-btn__share { color: rgba(0, 0, 0, 0.54); background: transparent; diff --git a/web/libs/css/montserrat.css b/web/libs/css/montserrat.css new file mode 100644 index 0000000..62905ea --- /dev/null +++ b/web/libs/css/montserrat.css @@ -0,0 +1,48 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on February 2, 2016 */ +/* Downloaded from https://github.com/Antpolis/Montserrat-Font */ + + +@font-face { + font-family: 'montserratbold'; + src: url('../fonts/montserrat-bold.eot'); + src: url('../fonts/montserrat-bold.eot?#iefix') format('embedded-opentype'), + url('../fonts/montserrat-bold.woff2') format('woff2'), + url('../fonts/montserrat-bold.woff') format('woff'), + url('../fonts/montserrat-bold.ttf') format('truetype'), + url('../fonts/montserrat-bold.svg#montserratbold') format('svg'); + font-weight: normal; + font-style: normal; + +} + + + + +@font-face { + font-family: 'montserratlight'; + src: url('../fonts/montserrat-light.eot'); + src: url('../fonts/montserrat-light.eot?#iefix') format('embedded-opentype'), + url('../fonts/montserrat-light.woff2') format('woff2'), + url('../fonts/montserrat-light.woff') format('woff'), + url('../fonts/montserrat-light.ttf') format('truetype'), + url('../fonts/montserrat-light.svg#montserratlight') format('svg'); + font-weight: normal; + font-style: normal; + +} + + + + +@font-face { + font-family: 'montserratregular'; + src: url('../fonts/montserrat-regular.eot'); + src: url('../fonts/montserrat-regular.eot?#iefix') format('embedded-opentype'), + url('../fonts/montserrat-regular.woff2') format('woff2'), + url('../fonts/montserrat-regular.woff') format('woff'), + url('../fonts/montserrat-regular.ttf') format('truetype'), + url('../fonts/montserrat-regular.svg#montserratregular') format('svg'); + font-weight: normal; + font-style: normal; + +} diff --git a/web/libs/css/poseidon.css b/web/libs/css/poseidon.css deleted file mode 100644 index e3285fe..0000000 --- a/web/libs/css/poseidon.css +++ /dev/null @@ -1,246 +0,0 @@ -/* bug: pseudo selectors for :fullscreen, :-moz-full-screen, and :-webkit-full-screen cannot be grouped together */ - -/* styles for container div */ - -div.mse-container { - position: relative; - float: left; - display: inline-block; - background: linear-gradient(black, grey, black); - border: 1px solid white; -} - -div.mse-container:fullscreen { - width: 100%; - height: 100%; -} - -div.mse-container:-ms-fullscreen { - width: 100%; - height: 100%; -} - -div.mse-container:-moz-full-screen { - width: 100%; - height: 100%; -} - -div.mse-container:-webkit-full-screen { - width: 100%; - height: 100%; -} - -div.mse-container.disabled { - pointer-events: none; - opacity: 0.4; -} - -/* styles for video */ - -video.mse-video { - pointer-events: none; - cursor: none; -} - -div.mse-container:fullscreen video { - width: 100%; - height: 100%; -} - -div.mse-container:-ms-fullscreen video { - width: 100%; - height: 100%; -} - -div.mse-container:-moz-full-screen video { - width: 100%; - height: 100%; -} - -div.mse-container:-webkit-full-screen video { - width: 100%; - height: 100%; -} - -/* styles for controls div */ - -div.mse-controls { - position: absolute; - background: black; - border: 1px solid white; - left: 5px; - right: 5px; - bottom: 5px; - padding: 3px; - border-radius: 5px; - opacity: 0; -} - -div.mse-controls:hover { - opacity: 0.8; -} - -div.mse-container:fullscreen div { - border: 2px solid white; - left: 10px; - right: 10px; - bottom: 10px; - padding: 6px; - border-radius: 10px; -} - -div.mse-container:-ms-fullscreen div { - border: 2px solid white; - left: 10px; - right: 10px; - bottom: 10px; - padding: 6px; - border-radius: 10px; -} - -div.mse-container:-moz-full-screen div { - border: 2px solid white; - left: 10px; - right: 10px; - bottom: 10px; - padding: 6px; - border-radius: 10px; -} - -div.mse-container:-webkit-full-screen div { - border: 2px solid white; - left: 10px; - right: 10px; - bottom: 10px; - padding: 6px; - border-radius: 10px; -} - -/* styles for buttons */ - -div.mse-container button { - background-color: transparent; - border: none; - outline: none; - opacity: 0.8; - cursor: pointer; - color: white; - font-size: 16px; - margin: 1px; -} - -div.mse-container button:hover { - opacity: 1; -} - -div.mse-container button:active { - opacity: 0.6; -} - -div.mse-container:fullscreen button { - font-size: 32px; -} - -div.mse-container:-ms-fullscreen button { - font-size: 32px; -} - -div.mse-container:-moz-full-screen button { - font-size: 32px; -} - -div.mse-container:-webkit-full-screen button { - font-size: 32px; -} - -button.mse-start, button.mse-stop, button.mse-snapshot { - float: left; -} - -button.mse-fullscreen, button.mse-cycle { - float: right; -} - -button.mse-start:before { - font-family: FontAwesome; - content: "\f04b"; -} - -button.mse-stop:before { - font-family: FontAwesome; - content: "\f04d"; -} - -button.mse-snapshot:before { - font-family: FontAwesome; - content: "\f030"; -} - -button.mse-fullscreen:before { - font-family: FontAwesome; - content: "\f065"; -} - -button.mse-stop.cycling { - pointer-events: none; - cursor: none; - opacity: 0.2; -} - -div.mse-container:fullscreen button.mse-fullscreen:before { - font-family: FontAwesome; - content: "\f066"; -} - -div.mse-container:-ms-fullscreen button.mse-fullscreen:before { - font-family: FontAwesome; - content: "\f066"; -} - -div.mse-container:-moz-full-screen button.mse-fullscreen:before { - font-family: FontAwesome; - content: "\f066"; -} - -div.mse-container:-webkit-full-screen button.mse-fullscreen:before { - font-family: FontAwesome; - content: "\f066"; -} - -button.mse-cycle:before { - font-family: FontAwesome; - content: "\f021";/* other cycle icon options : f021 f110 f01e f1ce */ -} - -button.mse-cycle.animated { - -webkit-animation: spin 5s linear infinite; - animation: spin 5s linear infinite; -} - -/* SPIN animation for cycle button */ - -@-webkit-keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} - -@keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -webkit-transform: rotate(359deg); - -ms-transform: rotate(359deg); - transform: rotate(359deg); - } -} \ No newline at end of file diff --git a/web/libs/css/roboto.css b/web/libs/css/roboto.css new file mode 100644 index 0000000..455ad1b --- /dev/null +++ b/web/libs/css/roboto.css @@ -0,0 +1,106 @@ +/* BEGIN Thin */ +@font-face { + font-family: Roboto; + src: url("../fonts/Thin/Roboto-Thin.woff2?v=2.137") format("woff2"), url("../fonts/Thin/Roboto-Thin.woff?v=2.137") format("woff"); + font-weight: 100; + font-style: normal; } +/* END Thin */ +/* BEGIN Thin Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/ThinItalic/Roboto-ThinItalic.woff2?v=2.137") format("woff2"), url("../fonts/ThinItalic/Roboto-ThinItalic.woff?v=2.137") format("woff"); + font-weight: 100; + font-style: italic; } +/* END Thin Italic */ +/* BEGIN Light */ +@font-face { + font-family: Roboto; + src: url("../fonts/Light/Roboto-Light.woff2?v=2.137") format("woff2"), url("../fonts/Light/Roboto-Light.woff?v=2.137") format("woff"); + font-weight: 300; + font-style: normal; } +/* END Light */ +/* BEGIN Light Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/LightItalic/Roboto-LightItalic.woff2?v=2.137") format("woff2"), url("../fonts/LightItalic/Roboto-LightItalic.woff?v=2.137") format("woff"); + font-weight: 300; + font-style: italic; } +/* END Light Italic */ +/* BEGIN Regular */ +@font-face { + font-family: Roboto; + src: url("../fonts/Regular/Roboto-Regular.woff2?v=2.137") format("woff2"), url("../fonts/Regular/Roboto-Regular.woff?v=2.137") format("woff"); + font-weight: 400; + font-style: normal; } +@font-face { + font-family: Roboto; + src: url("../fonts/Regular/Roboto-Regular.woff2?v=2.137") format("woff2"), url("../fonts/Regular/Roboto-Regular.woff?v=2.137") format("woff"); + font-weight: normal; + font-style: normal; } +/* END Regular */ +/* BEGIN Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/Italic/Roboto-Italic.woff2?v=2.137") format("woff2"), url("../fonts/Italic/Roboto-Italic.woff?v=2.137") format("woff"); + font-weight: 400; + font-style: italic; } +@font-face { + font-family: Roboto; + src: url("../fonts/Italic/Roboto-Italic.woff2?v=2.137") format("woff2"), url("../fonts/Italic/Roboto-Italic.woff?v=2.137") format("woff"); + font-weight: normal; + font-style: italic; } +/* END Italic */ +/* BEGIN Medium */ +@font-face { + font-family: Roboto; + src: url("../fonts/Medium/Roboto-Medium.woff2?v=2.137") format("woff2"), url("../fonts/Medium/Roboto-Medium.woff?v=2.137") format("woff"); + font-weight: 500; + font-style: normal; } +/* END Medium */ +/* BEGIN Medium Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/MediumItalic/Roboto-MediumItalic.woff2?v=2.137") format("woff2"), url("../fonts/MediumItalic/Roboto-MediumItalic.woff?v=2.137") format("woff"); + font-weight: 500; + font-style: italic; } +/* END Medium Italic */ +/* BEGIN Bold */ +@font-face { + font-family: Roboto; + src: url("../fonts/Bold/Roboto-Bold.woff2?v=2.137") format("woff2"), url("../fonts/Bold/Roboto-Bold.woff?v=2.137") format("woff"); + font-weight: 700; + font-style: normal; } +@font-face { + font-family: Roboto; + src: url("../fonts/Bold/Roboto-Bold.woff2?v=2.137") format("woff2"), url("../fonts/Bold/Roboto-Bold.woff?v=2.137") format("woff"); + font-weight: bold; + font-style: normal; } +/* END Bold */ +/* BEGIN Bold Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/BoldItalic/Roboto-BoldItalic.woff2?v=2.137") format("woff2"), url("../fonts/BoldItalic/Roboto-BoldItalic.woff?v=2.137") format("woff"); + font-weight: 700; + font-style: italic; } +@font-face { + font-family: Roboto; + src: url("../fonts/BoldItalic/Roboto-BoldItalic.woff2?v=2.137") format("woff2"), url("../fonts/BoldItalic/Roboto-BoldItalic.woff?v=2.137") format("woff"); + font-weight: bold; + font-style: italic; } +/* END Bold Italic */ +/* BEGIN Black */ +@font-face { + font-family: Roboto; + src: url("../fonts/Black/Roboto-Black.woff2?v=2.137") format("woff2"), url("../fonts/Black/Roboto-Black.woff?v=2.137") format("woff"); + font-weight: 900; + font-style: normal; } +/* END Black */ +/* BEGIN Black Italic */ +@font-face { + font-family: Roboto; + src: url("../fonts/BlackItalic/Roboto-BlackItalic.woff2?v=2.137") format("woff2"), url("../fonts/BlackItalic/Roboto-BlackItalic.woff?v=2.137") format("woff"); + font-weight: 900; + font-style: italic; } +/* END Black Italic */ + +/*# sourceMappingURL=roboto.css.map */ diff --git a/web/libs/css/roboto.css.map b/web/libs/css/roboto.css.map new file mode 100644 index 0000000..85321e0 --- /dev/null +++ b/web/libs/css/roboto.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA,gBAAgB;AAChB,UAKC;EAJA,WAAW,EAAE,MAAM;ECDnB,GAAG,EAAE,0HAC6F;EDElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,cAAc;AEPd,uBAAuB;AACvB,UAKC;EAJA,WAAW,EAAE,MAAM;EDDnB,GAAG,EAAE,kJAC6F;ECElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,qBAAqB;ACPrB,iBAAiB;AACjB,UAKC;EAJA,WAAW,EAAE,MAAM;EFDnB,GAAG,EAAE,8HAC6F;EEElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,eAAe;ACPf,wBAAwB;AACxB,UAKC;EAJA,WAAW,EAAE,MAAM;EHDnB,GAAG,EAAE,sJAC6F;EGElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,sBAAsB;ACPtB,mBAAmB;AACnB,UAKC;EAJA,WAAW,EAAE,MAAM;EJDnB,GAAG,EAAE,sIAC6F;EIElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,UAKC;EAJA,WAAW,EAAE,MAAM;EJPnB,GAAG,EAAE,sIAC6F;EIQlG,WAAW,EAAE,MAAM;EACnB,UAAU,EAAE,MAAM;AAEnB,iBAAiB;ACbjB,kBAAkB;AAClB,UAKC;EAJA,WAAW,EAAE,MAAM;ELDnB,GAAG,EAAE,kIAC6F;EKElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,UAKC;EAJA,WAAW,EAAE,MAAM;ELPnB,GAAG,EAAE,kIAC6F;EKQlG,WAAW,EAAE,MAAM;EACnB,UAAU,EAAE,MAAM;AAEnB,gBAAgB;ACbhB,kBAAkB;AAClB,UAKC;EAJA,WAAW,EAAE,MAAM;ENDnB,GAAG,EAAE,kIAC6F;EMElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,gBAAgB;ACPhB,yBAAyB;AACzB,UAKC;EAJA,WAAW,EAAE,MAAM;EPDnB,GAAG,EAAE,0JAC6F;EOElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,uBAAuB;ACPvB,gBAAgB;AAChB,UAKC;EAJA,WAAW,EAAE,MAAM;ERDnB,GAAG,EAAE,0HAC6F;EQElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,UAKC;EAJA,WAAW,EAAE,MAAM;ERPnB,GAAG,EAAE,0HAC6F;EQQlG,WAAW,EAAE,IAAI;EACjB,UAAU,EAAE,MAAM;AAEnB,cAAc;ACbd,uBAAuB;AACvB,UAKC;EAJA,WAAW,EAAE,MAAM;ETDnB,GAAG,EAAE,kJAC6F;ESElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,UAKC;EAJA,WAAW,EAAE,MAAM;ETPnB,GAAG,EAAE,kJAC6F;ESQlG,WAAW,EAAE,IAAI;EACjB,UAAU,EAAE,MAAM;AAEnB,qBAAqB;ACbrB,iBAAiB;AACjB,UAKC;EAJA,WAAW,EAAE,MAAM;EVDnB,GAAG,EAAE,8HAC6F;EUElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,eAAe;ACPf,wBAAwB;AACxB,UAKC;EAJA,WAAW,EAAE,MAAM;EXDnB,GAAG,EAAE,sJAC6F;EWElG,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,MAAM;AAEnB,sBAAsB", +"sources": ["../sass/_Thin.scss","../sass/_mixins.scss","../sass/_ThinItalic.scss","../sass/_Light.scss","../sass/_LightItalic.scss","../sass/_Regular.scss","../sass/_Italic.scss","../sass/_Medium.scss","../sass/_MediumItalic.scss","../sass/_Bold.scss","../sass/_BoldItalic.scss","../sass/_Black.scss","../sass/_BlackItalic.scss"], +"names": [], +"file": "roboto.css" +} diff --git a/web/libs/css/roboto.less b/web/libs/css/roboto.less new file mode 100644 index 0000000..ee48cd3 --- /dev/null +++ b/web/libs/css/roboto.less @@ -0,0 +1,15 @@ +@import (reference) "../less/_variables"; +@import (reference) "../less/_mixins"; + +@import "../less/_Thin"; +@import "../less/_ThinItalic"; +@import "../less/_Light"; +@import "../less/_LightItalic"; +@import "../less/_Regular"; +@import "../less/_Italic"; +@import "../less/_Medium"; +@import "../less/_MediumItalic"; +@import "../less/_Bold"; +@import "../less/_BoldItalic"; +@import "../less/_Black"; +@import "../less/_BlackItalic"; diff --git a/web/libs/css/roboto.scss b/web/libs/css/roboto.scss new file mode 100644 index 0000000..5d2a891 --- /dev/null +++ b/web/libs/css/roboto.scss @@ -0,0 +1,15 @@ +@import "../sass/variables"; +@import "../sass/mixins"; + +@import "../sass/Thin"; +@import "../sass/ThinItalic"; +@import "../sass/Light"; +@import "../sass/LightItalic"; +@import "../sass/Regular"; +@import "../sass/Italic"; +@import "../sass/Medium"; +@import "../sass/MediumItalic"; +@import "../sass/Bold"; +@import "../sass/BoldItalic"; +@import "../sass/Black"; +@import "../sass/BlackItalic"; diff --git a/web/libs/fonts/Black/Roboto-Black.ttf b/web/libs/fonts/Black/Roboto-Black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..689fe5cb3c715f2944fec30e43ccb8a2b10625d3 GIT binary patch literal 171480 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

~M8)?dE#I zt+d-{x3BKi+=siLaF;ziJSKQt_Vn}|?fKZt;FVfLEYhUNuSG6-dw9ot&-cFVomDin z=-{F|e4Km+`rIy7u~>Yu@5M_NPeb0ev2XtpB}!B;F{;E-y_@emQNqlI1#;n^*2usC{T`=)lmqq02%q zhmkNtSe39*VH3hOhdnLdqx{11pDF}Y=v?7`cy#!Nh$0coBW^`DjyxL`7}Y;2J=!_C zYxK2>0Tstoyj3Zr(x6IvDhE^^Q~6Pq7FAYP^{V=7)y!($s=cgUt@_mJPij=Gu_?wg zCN^e8Oh(OyHOJRHU(2ReT&?Z3-D)S-zEY=Ioj>b()ty>*M?JfGzt+pDU$XwN`bQdQ z8^ksk-(W|>$cFtIni^&|iflBaQFi01jaN1CZ!)IIm!|PeFEwk_Y)7+q%^Nn~+QOy9 z{8*3JezAvJMzq`zS0rvyE5}xITI*ZyZ4=ODMw{zxi?*HIHnZK9_@42v+V^VzR|ly> zYUU+X;jjbq3<2mLeHd2qY3TIJZE*7HG9_9Szmrn_-O2(H*P<;!(+$j9cOnMc8=Y- z)kux)joVXcYN^!asU}l|X}Rf~>7MDS>5b{*F1E{Vm+P*gyZm;A@2aw^_O8ae+U!c) zb!K<5-Hmo{*)8wsxaaoXpuMy9X75YbcXwa<{;vDi9B??0bm0EM<_Fi|Z_8g^e@*)9 z^`Wwd79ZMi=-8n}r(Pu}$ z9dkYwcr5Bz&0}qk^*=W0*z99#k8L}4^4Ps&*~c}<2cB>}QR+m@iMA*DotSiD`3cjB zb0?mhcz2SWEOIjRWZjbqCkLPWcxv*g6{k$6&Yya6>dR?|)BdL`pRRYhQpTm9mt8LVT`qgM?&a9a z9WM{QJpJ(%2|uU~zARZjCro0zsVZD-n%U-X0z3ufb*N0r6aeeLe?blCUzjHn7`u7`lH}p56ZZx~m{l>W) zPi}m^Np5=GEO)cc%@#KkZuY!6m0K-u^}03g)}mX+Tl;SvyLIkX+U;t$8{TepyU*2L{V?ody@xFyc6`|D;gE-; zA5MBW=i%~)n;x1To_~1%;md~~AK5-KJSzLB;-dzS+C55s)c4VdN3$Poe01c|tw&jp z#mB`Shd*xoxbx#*A1{8q^YNL-j~{=2;`k)sN!2HDPx?QZ_GH16HBa_Ex$-3a$;Wit zbbWfo^ycY3(#NDPO5dJ-F8x{h_ouq2#h#XY8u2vdY1^m$pH6wY^68$Zm!4)k{rSx8 zS?OnWpCvsT_H5R(4bKifyY}q$GxKwg=Z5EHpI3Tb_j$te!Oy2ZU;BLj^DEC^JkQB+ z%P5r*nNc&NX-0fTO2*WTr5Rf?&Su=mc#`oh#gSe0%lngSW5Vet+lq&ih^9yYla< zy=(Zc^}9~*`n{X{Zt=Sv?+(5@^X~e)$M4?0`;zIDsn0B*SuL|+X6wvOnf)?HW=_tW zo4GP`bLO7RQ2;S;<*_vL<9r z&zhIDDr;lb;jEik53@3|GP6Ftw}0>U-sipl`!est-&c8G`+ej0w?35oFy+JVAC`SE zemL{t%7^A^1fie8j(RLnOl{eijv>U2dP5_6J(jP++4}{V}qp4{gn;>>B2_jCbGn7 z4AWXhdYIqSM+$`wq^e2d=mY|c8l91*{R^S?Ak-d&rfM`syE5$zcYQv^<4z+D4J(h9 z4KyOEf~UL2FNkiJKa`<8R8piLpv#PIeJ3RtL7n`RVvaaegb!QUlZ;$P-6f4!thWe#LW!{j(taR&cFotRsT zqtFi@Qi7~7mCze_6(zu;ynZ-x>%Z+dj=`ADU`ob=96XXT-N_n$bpQbXRWkOfwRlv4~RQi=I$_!Qah4GF*Xh zA>0k%VNk#yXa20~+S+GpKh6nx^t0}nI&15mt@}#;8S+BTt$j9TZOB@>OzuvX?V~pP z=o&eh!#?@jKDj%Kr8b!6lQ?sb7>l>N%925*GUYRk4|iYD!(F5G@Q(}%iZr-;hF6GU-TX)ml|SK?yF0!P;d&}jED-a;OT)*`uhb%dhs4cMyh?sfcVc#(b38! ztrSeX+8Y-v+OcE7{9X4dhxe)&UcNGYkUD>%amT{>yY9x6>tsVKR|u=ja(}F3XIF|# zrf&RWR)eV<*8i@Fs5zx-MD3atbFNO?ux@U>X(*FI>nHSxtXVB0x4Af|Im`~JXnrTv z1YdkfFbRhxQn;Z312h(H6v0Umi{`uI#GUJev!)1i!sJmz!&Re*F118~h)sSM)h?Tr0@ z+`Zf+J(~JVdn`|@(`sk`yK+vCghlp%MmSD=kggMnn~6%i zQ{2iJSsE-1Nai+$`WUWeONnw9R)aq=LaxrXYU&XeqBCh--IOOBLyaPCcZ+a!jrP(o z!9(Zjr43>%c*KMkGh=2hrAueUOn)i`v)| zd$zo*VWYso9vh?OJ95$sPIXaMUf2x_=tT6YtVKvvRAimzZ6u*ZjJCkGp+(T?z-UPb z7XrN`H?1H9(`5NzOhX3N?9^b zbi=M}dstdjW+Z7wTV1NAKnsilH*#=FmOplcwwAZbr{yi!f>1@MEI*M)(y7!yM=R2i zM<6dN!xNM+*(#oZ@L?%gIT$oSIO~I>VOb2=xq54hUp;|3Q948((W1_TVL$E-e$VS# zLO)`s$`|OqD}iv4=gc=_DoN~P?j{ybKT`gy2t&=E=^I)fEf-VyC4pZOMnEblXdKUR zkA{IA5k*5=^zPjvwpTA&AC4?}UNYJ=KgYXdrxgF`hFVmUV{z_b<-PoA`1KF;WA?9_ zvfSIYHfM@W@Lr??Y*ho3;8TnbG1wwqo|m!(3AUEusaP&PJi%aHL052ps#n1&DAW;u zKjPw|gWz2PRqNuS*x6{YiTw5BKk^r9OEajQyt8F|+vY;c_%Ln>SG$3k2|%!0KJbV3YG6$*$lVe>XhmFPrr z2@DtN$UEfSY-|ZJq7%jXBPCTZAY);jyh#U>uBeYP#&}><$TeFUNiAYsHBsynb{_C{ zb-Gj=tBdwThfAp>fWfiq5=`IrHRt@Y7{ zI&%VTLQ{Qg^NoT@vf+w{?pY5gle-r$H5K>q0)pLf;}JI39ap$`ZVG~;4W6D{DE@xh ze(^$F?omU)(2Doo{Q7L!Z`so*Z7C%*-jX(~GCRj!%Rl5N9(vjQdgk*{15b}`k(l$Q z3AY4s=uIzZlo#A=KK`JZOGyaq?KYlInVsyhlZFul=#-@p)=~! z__u$aj(R`|JS@CZOe)vWQXaNo(P(3-&?2cm1&lEXr4(^H`;_7$fUVURTtuy@{spIC zA|lzmp{Im2Jm3ikuOQNhARg*ME26+p^&lch4DFTBbCmo~W|sVZME{PxDSPmUGWlZ1 zC5cH*8z*e3Q@=sIx^?S|WqPkGAD%k$L%2j zA!LKLc)EIdxHHk;-z7Rgs1TiJiQGNe$9_}Kyq@sx_c?DSu*34YHy$xGVNn0D#1{3F zJG1c4)FFLl_RP8R*X`p!%BzmRUAxfzNi%0oS<-jVRDvDM!{C44irq*M|IH|>h0msf zFT64LqRyDu@_XtaI!MPo!Ne8sGx@?7>Abm?l_SH1b=d2n0VkBWR`K0FP%KMWC%K?K z7dn>t=gbs_uul8s%h>&GM|;Xwz{hdaNA$(sAAx95w^18v6fo7qT(5u$My)PYEI5Ve z^CBA2H>b0(iUYe&#oW4liwMPD#i7Du>_W+>NC^TsMN*nk;(=zMLJ^ytn=TB zaB%q`CLgd&=jUx6-J|;Ac8$+HxpE|KTKztuU7E;g`SbfKauv-hX#!>)HSp^z#;{O~ zIYmk~pxcHIe<97}Y!k-2=p1U&*r>Kymm2MmZ<~!CX~r;Js*hDkDY!?gQ&{b41*hl~ zW3bJ^s)3Fo;T62R1P#Vxv@2Hm@dgkh2Ap`og6jjAo2zSpUUa1zK4PQYg1`&SL%ZyA zEywtG{v&x#ZAV|KIWoU@jc}KNtp{sd?Jb)ctl2 zujpL*jrQN4<7i)_UTE{8C4KcNBUzCAQ@&8Y=A?mXlYY>!I;C^>%(%B1`*Qo9l$U47 z>*QI%MK_i#e~m7q0o0xD`EX4hA6HSc{=+7wSZsuEjSG; zAMRq%7X(C-;G#IP;NWm|e(B$z*<+`5V9#!YM#&%LFWE1Xy6^e2Z*b?{!?sDrE2jpo z3G&@A?DieT`pE0sHs4EM(v~d}kXN9jF7gWYu(icW3zLIYyiq8Vlpg`<9Fz#iL6_=Q zK%P-V@}%I@R9tjGmmmX;M)XIpuJ~X4GLRvNE1*_&8_{@iXp#JJhx}CDyO0*ykJ!DI zr@T%aGk)SIVd(6U<3yUZWZdvX$HZ{`A&y8nK;jmM^b;~8=PWkwm z+UMkxyQsJ}r{A5MHE!Gn|G4IKQ0U0>RE2U6`HZs{m5A6LDr*llIBH*5Z#fGK#gv8O zeXh=yc_xCi&$)Nm%p4CPD|Zkp$$H6u$bU;lh#ecqRC=m{9$QlD|1q9SwoDPEZDD5s zDy-(X*3F7S?ge?Ra3yyxi(++h4=D{|+G7o3`W)th`nO-Fsfrs4&oC9Hg)qEbRI59#z`A(e`b^p7-1p0V<| zgYq+ZivwLkeGXC`E$c1sa2?Tq(j2;Q&2P0ovAEss`_n{Pam*cBbNlzNqk5*NwY&eI z+Ov-;_oPronl06C6q074S%GErE2@_-XIndEz6R5Dl$$0inW{N#ix{O)d429rc`Z$1 z679&6bJs}5+#{?OXqW;qW`G6(V^XQyGSOB}EX}IbQ5ad8h@0_T0HP)x4s|5V1ZBez zR3BHKut>sULkkZAsFxD+2jpcgBbkXNj(_$0d-)qpt{dC1xn#`oo^gLf_QS62`*qIa zQ#jgkAWcj}$)>Aq6*At6AG9=FDF1B9u$!=B5p=YDM-j?y9>_+{m8!)Rv>(V;( zioC{8Uh`UB?I*7VtL=r)KU&aMZ2!+sqGN7-88$qR=jCu@3!c-w91?F(2?{N2F2r+- zt|&z2k3l9WQIb|cT#YDaI=-ZG(8(M{Ck2`)OwSnxT83edK2y9z4a99a=wyRDKVfv> z$$ynm|LQGpL#lhhxc(0}0Wl!pMv$V;&$tnmV0ra|R!!Q-9)O3T}{zR4*6bzO7ssXV05?U&DP=6Q(M;Fmpd zC(TXv9-fc}U$$_}qq$JZ+%jb|IS7g`cPMNaVWHd#X%J^w1(9bbu&AL-Duey$WqB>D zyNS|S@>Tgu`pfQp`X{}m?`2oX_{;M-x6IVJd&lmhu!WJ42J#nVtxv&s`GcP>>b%%R zXY|ga%>^N(OCg!^AjqYVoy6ixs=k1|3O}`8+CYSc|H)Jpz*eSEY*bDNF4hQ$=XtvRqw5E4xJqvRm->!Y<1(>UH7e4@?Moe8yk{8cb z8Kr_8Um;6i8HS1?I-+lme1PcGFir?G*vLcRdr8vA zpbnGX;Z!5o;McLYw->1v#`gKg~H-eVylqskG zOqHL`rO6|wwe3bHHf@toX2w4w5A;a6Iyt^`Tuj9dohnZIaP-749Sg}59*0MV!5B$=b`t3#W=xl5Bc_U11lmb+w_ggZPyKaSqp5qg za4e_NhY?fxt07dGGpMEJtdFv+pH~>d>7FygeO%eEP z1t~c3_9mJjUz8V|JSs1gFVK#g&e77WgfRC<3!!?>32*PM4Kg)gTTDdo! zgSS63a?9oV(Ja}GJ!aSQq+$n(Y)JWkp-5@hV$UoTC7O(IbyipX(DR>RU((yHBr4aZgj z7lQ}=246dm?ai$x_Tk~c0pT@u-id&9JKT15qI%8#0K+XsCo17`kDal)`L*`t`Wtp5|WYI48z&c}B$4lB( zJVPsUPg52a$3<+0wWI*N^QkaSNdWS!6X&8uoeEgWeI-O=odaj0lK!+4`8p?--i*i; zIu%STOgblS>UST~UD9Eyo~WZ8V{9FC#dIOMsyeab?!LOwDCbtcgpE}oX;2d}JUJT9 zGfmhG5TN(;a`E)?2t>jq5+z6>9&>f-xM}HaDt_xZeuTV}+S66p0t?A~SR>B0v$47% zX6S*L#@&?`4qJKZf>AasZ0_zQCD@bP0Ouxbd=cm z;kiG$Imy>)Y~sQT59J+kZ3cyu89H3AW9~5B&n6Dgklg2eo=pcs`-4ToINsQ#f@+Bsg`vodTN_xH?3>Rz}x5=rwu=+zCG8f z{ieu{>&I|zgHEvoxb1`tWfzmDH(%j0dg|a837DyHTVb$;1 ztS&^qo6Z#(JRVH)j2;Wb0!6e7vLxDw$7knup)KWWE zVgWzMkZH7Jj+~xIS0v|v@@k;G0yyje9%E++*9(O>3#FF1fXSP1;*8#*yGwY7db5G1 zquz9&X|^{-hRT}`N_8kua3jx36;N+-)$zm*k2?!>Lh>Wn3BtKgJUo;&Bc6X0{IrJ1 zs36w+yL{=F%yk*mCbzTes7`CzFRSrt>w;pK-n4P2rQR3YD9?~zT48-v%ZA{4o2*!1fK!40!3+hla|wlb1K7XB(~Ml4%P+9E>sI zLK>Ou^CCc>3=&<>n&f6LOy3KiR5r z<-nQV7FvF;J(lKTww)n%%%_TyPLRr2>^5}Z*-%6(|78Fy0g`g1AOT~eCSVi<8uAh_ zN&v`Qk~rV}WV%3}3>ZV_C&`yIG$*Yrtt!{foyfHz1z#s1jQ@&j;Dqyds<4vFOQBLT z0VAe38DSEwvdx7Ca-oq@h?`1a%e_1Aon;kJIq^YY>%#+Es#WG}l7jf^ma`S(i}29K z3#tat^ZbjVZPsc>SK1Fgak)YNG}_Qs-chkp*YZ@}5!IwqxV%%WJa+#2_41(H7`8R8 zNxV~Takja2llWX`yhlX|e>*oAJ#!(gOb%A_qC!0@)ESGrQ|Py>=p$>SI69autE_b% z)rAHKU0|Pbdh;eh-chS-3RGvR;0IHm@1Er#BKnocqiyl}{ zX1QsxS~azp7UJUJ&UfGh2XbqI{af5ijmcd74=p0U%6=ukwx=x;6LyOSH#@Q5b-B~9 zCoIm_q?*tEQ+{DJuk1tGyx>vF5wCdbr zdFP)aN`?$uD>088`9?okE8Xup0$Yh>iR#v0rHMc3y(cV+%P1h z^pKSftR!VLVA_x$LG(v|T1u85wWHhG@aPE2d3^fb6g=o)@x3QmA z8C!RK9>&MmmcY;X)W8q&h#gHf8a}y%(gv%hTp4jJYryKVU~pCY)=Q#F;&<@6=EjMY zXRL;!r^Fxe;X_anNQ@@`pkO?$Qc*|&^2a#y;fwvI-!Ns^eE`2{hR7UC`&O%J$nouV za4hD?I_^zy?CUt%QRuk43SKb8Vq9s*$|yE@+A8yb-#jrF@xkr~s}Mn-wmVMWMUR^z z^mO3vTlyC`-W{lqug$nWidJFCjqBE>VPzsJc9_s=;qeozCR7P2TQR`9 z)x8A=yKYNrT$leZN)GtHcK(#=!{c-^IVm1T<&QG$w@9hbtjLmO;~|c+X3pn}f>k#W zY*=k6;||Ov4Z#qk=E6fCC1mh`as@^5$jF7hGMPeZR`Bc4Wyi+p)1~_IKW}B1YGIm> z#W!qX-fyWRsEkJpS_*~-FZ-Hch4ZCYNPQH-EF>3lN=g97$24(NrtOFk>Irr@x8{sE_Vwo((1SO~#bTgGL;fXGWFaAc zPeuNSRY<@Zt0fh}=cG00`f%RqSTMpHp|a9F|SQ+MwEJ%*rSPTJ0I#S1kTvNV=6w>2DlR||nu#_g^ z%mc^58o?7)S|w=0AdAMb5p&*6d&%|P65rQq7*fBntB*Vl`(f^~&OcqsU3dRjDzX9p zc@>JaQT)~f=qQ{^vT(+tm4zmv|DSV7nKUb>7fqKVh2a?PIfvLNik-xKSM(A#P*V;2 zx}r_4MZA^1JE(!05*K)(lNtwQ`A~zc!H)TP29v9@pBpL=fLX48B*OYAzQqG8=~`^a z_w-`eVzX|Yu*X$LLfeIWB`hYnBgJqj~cc)tJq z#pma5$!ofRhD16*+%D9C?W<|h<=H+bYyt9Ab_HUOd*NL`+>+s?2e^LgNWe*ltv2AG zvdKA!o9SR>3qQBck>d7!iyMzzTC+m!_q&!h8MClPcuk?ism#iYJ2wkML3IT%+I}BJxnR#f3(;qC(&f7M!eS8J5?G+zilM{t6Kf^S4r$GV)S7P~CRK z2ILf%yz_P$QPSCLF9u0L$em#bDEp<<)|3|+EDCEGu!R&B3u%x5-(3ZExO4MvD&npL zm+r`o6*8=97Dah^g<&f1YW-@9no4P-q*6|-I(PiR{S)W@_{%Dlvf@^;x37!Q60cp; zseiF2Pm4d@nx9djqz7!!3h5;F2Xs;iSG<%Z6`a2|AQr3*7;Vx_zLJus^9{9rEdzUO zvBH9R7Ej(;Zy50M<)aDG^>SCTR^hM(!O&z?#CdMn3#+aDB)Hq3H+obOsI+dG=JU1m zpYq;m;ni(vI4hqrs3yjSlXJVssZ_dm`PIwQD`Q*VlYesA*EPS8qQ#Gx>6d{PvXHWz zvzSKet>>Ar-Pq!ez1@6vt1sBJ#dmL)EI3siS+F)3qaq?YI9iF0qP;Mb-LZT}5k5dC z`8HLqrW!GD^@WS8#?PACwcngszb`+2V)>lk7xd{oG*Bsq*q#{suy+pf`MqWovcnn8q~@GQwIQ0tyU9H)VgcGWwwO z$dB_s?!F`WUpS0*>Az?<`O0NEn{Ug)uCZ&#I9&ULt&H7;%}kpcb)@TI`8(Af?s|mb z2Vxi*4mn{%4o0FDc{S|)HQJhEiT4f0k>~Ja= zTc$Ae5$y3RW%WprFun`dWGK!N-nA>7%Awq>W81qvZ#xE5A%O3|<@&;R;pV?`hW`_h z1tJBFkkiYHYmB&U`E>ckH~EwNjJE7H`1oLX+1`Yev#8&vAGD~vvqwTw*AyDCZD8fx z&DEpJ$8Q-{u|NHk+M&mh)U&tGG;T7ea)Yk@7Irr#_CC1dX8QTY?Q2!8Ri|;I&SUH7 z+)K8oQ$Nx#q)BzWb;U@Mm?kWc?qjvb1#e#QWf3Oi`zxMYfet7`L76?OZ%YPRQW{BZ z$F;>jrd6@jI=Wf2{>_?2s|ZrX`yV*&nrTPF4^VI!up#Tcve zV!}@O*x{qA)^A^t&@SQs$J%?qM^SZszi?WGm<{ROqo+IMWfs2uRTj2t6~DD@6aQoVq(iI?o59)~I?N9qImQpZCv{%4 z_1go_y#DR>m-}^^|IFm^^YaJH-OexV+sk6n8nx1%SNYGoPqLmLf6RBB{G4w;@d@im zr!`l4L-q$fE5N@Rr3MTC3R=@u$;0hBl3Z0%9YiT59XLwy6A~rBh6e|G35D`W`o$Di z7^*&$P*;Uw3KvxLy#P59S3JF(yh4yj&yZwm84_LZI>I8IT8hL5_LaJqv$vZvVfM~_ zL&h~}wQv7|`Ril&=E|Kb3~$?2I#%I{j+2LtdOmY{X3gNCGndWf$NT5cN{L>;>SuHZ zdb){a96H*ru%xo(-(qYmx_yrov8JhP0y^BL02!33G8^4o3XG{1;hd7tz``ZTyECj6 z5Cdecj(*~vD>nu35FOzZFE0ioW|e8FZ`r73HfTsh$`nCFe1&|&*LogW3PW1wNnMT> zMfli;J~qbpP>l-^NW8doH9-+lHUWH^5DNH{T01U*KB;zkQLNz)(qe1Vv$zK}id_kz zMfH8ZquXX;v>Y=VTB#xhMVt^YQIHvqBuJFhq%^XDYNw_pb$OKGCOyiM?7h`2dhMPO zb!*tmtqaO+w{b?>!7U12l)it2>ppvgEAR9Da>vC}M%9lWv%Q%!G;wp=rt=zQJynlE zBfW_Z`y;F>?tK#QU{p}0X(VAnfs#dNH>;On((HAZV2V0J*haa*X=L7ZbT()zQwzMzK7Rs zR1el*k(Ubq29UN!`bztcewJLU4*!Jz031LzOJzx=%jHq>mjHUciXO%xtkJ`JD28U3 zxVTPw$NC=pie9Rp$3`bFPPkDzCkhWE0>q5+tw)uj1QHBgT#2EOTk|vg5*x#_YNYt3 zpnD2gC1g4$X%Y`bBK8F79rt1uTPveW)mj(#ELgjv%S-vdS+85+uGS^8ZT+efw#=H; zX6o=}SmzVy>HPuLiCL>^Y)Y#4oCVGFbqcQn>r|OD2(J*;W$H4@7=v;KTB`m*0;h%O zMv^XV)bJ!>tyD-74v7;OYTtga1FB^broX;^QnNOSDP#)BSypXexE$RFXI4+j{(1L; zwfh#XeQ(`MMVIT)oY!JW6f5(976ZH3U5J{X4e>5ENouCM^ z+qFCJ?|`vx@{3@J9zpc79i6*~nUO6DiWy2fV4x61L5DE1Fhu}`l8zJ(WdP9invuow z`G?jX&v$9eUq=vO!H7Y>3fmMFtrk;RESBr5 zOl^j$)O=K@%3V>GisDrI+pfH_#IT|uONMF(-FB(5*5&w&rMO|SkTy2h{>*?l zGJ%Zr8wSvcw^Y=}e%?}1C>eWJN~RQh!NsOT;Ef&0uT6qH884}F705BFnawf_K-Is3 zh=&2J9qJQan#|5{1KLY^t}xm5AQF?Yu}3%bZB3o^xyp92RmAB z`tbNR>wr-s`{(r^_4E*E({`9z`ziV_r?M5`Y&*Vg1mdI2g0g69H|uUo8aq*TNm_&N zD_Nq&kJ+Vo>1F8KP;JdRNor=`Z;;5f9)@JcYb_69E7ISdlr}Rv`$(qla-P4@(PH9n zx&V1q2~vXI|1ABD6fgI}LEEJ`OEv0Xr+rR5ti5=!915r;ODjHIzK^OTLWp!V{N8j| z$Qh4^n215>YWNIcbI*of;@=YgMPG#Fi@JgeOdarUX$R3A-;RC%chEPCS%I zq)j@~pk;1B)2ya#B-Tm!`{xUH+xG6#&fmHwZ^Veaj>CpY6IN|$(4c3(7EQXe|MdK+ z_dBiV+CH~i%lY8s{>%DfAE6+4J*x{ECEPMY+lTJdhzOy6wZsEGVyA`))D{dfBibB% zZw&7ORVmW0s6P;;2Zr1dCHajiDcl1BOQ3KgL{%6<74(l~%JzDt(}Ve}HGGnEK?!mH z7{os2%|fJlC1dV8SSx-IMFGHA;NPpr16-xsnPoYFUi_<}C6RqX(-VJ@t$~PA%lV7E zjUtOszNfMl-lDZwwJVguLr5Qv1lXEk9souOfNRkBr7A~ZMhZ(g#!s_q$M}!@M>dt_ z2OQ+zvZ@F9m;BU0R)wFTd}Myv7HezkE$GE~_z^8MdYTHabrRCqr~2G8wEl3#2)nkD z;YnD!Ng{%TDnX!Ql5$eD!WQJ*Gwo3T$)quI+;dVQos2G>-P#%e=6AQw%0Knu$Aq)p zFsniF^C$VbN_<@XZh+akiz~2&_49$c)sW0h7XS4z8=OgS+s4Dc`Gl`6H@Ba=L-k9i zZFBob`>UQm0}dGn@BJ`1Bna3m0BAMTZ75-=wCxEF(Iv5NeL@lo!xG9URQm~#TLZ6C zX28G!q#-h6C3v7#!rbD4Fb|X1$6xFznS(|QWnb`ptka3Yi4%t4#5o&V{K8icBz_af z{D*wZ&^~<$X*&YWb?wgj^==Bm{(x-!L(>p3hZ-MewI6+TcdVMF^RS zdfGJKvm(7r9xMHfROsn6a z*5;;t@*1>mqO|$xPVwFmYr4hdc3kxGp-(H6cUNmt|B03hr?)QL*`Z$Bp+kwIfU&W? zjC#x`EVU3f_*o;luvm0SBT`iQ13kFqku#E&%n~)gDYk+rDh!lRUqVKW2p~BG*jYry zh-wk_BLF*lH{xUj{D5fjCu+8Za#m>iq8`a6$WAsJdmt`5JsTJv{9Is!rqQJ~RP?H; zNmNPh0{yD^K9ORRH4JDU&`Dsd0ghp;{=sF+P6 zM~;E-6Yx^y%-IWM=_!7Us2vKqN<>Q#+ceY97B#P45zeL(-p6~3(9{CXo7Ygu=s zG@<`M0trb1%{|ao6^N(GmX4}j;JGRCp!>A~Sflfr9wSwQ%Zn%uECj@A4E4YiM>gaE zaSbTtEx|;h;0xijQbo`X?B&dHGF*x*K@v22>BFoq{`1PpTeEMUnLapUgxY?=$OR*R zdt+NWKC;j7+!+IU4YF3`ZvM=ZMf}Pu(=RT#!IXxJhK!ofIXC5A%^8Cyl+4?{rho4I z@k6G8%j#L4!OW>k_=ylE&9ojLhEszrDZ~<4H0Ibc;%>SvTomckbJz)-CU4bim>!2w zI+XmTM7rw@**V1fe<|}GbG9fbSW-~nj@7;s=hrS9t+Yhv@<`A+nbt&CS%^=a*>!-A zv9|*itUHAuHTG6Qlc+e7>jE*R2~PqR2$9Mnom8c2x7JRRAx$F)V9=*i^P)V(E zoYEKebxYXiggO}V2m>H6XX(6H15JtjudxOm6r~Sq(DLF3tNIYW zsL#reeg@QFT$QTh32IQnzaGK;?VH_KhN1MOL}Brrd44w*N&Y4w6Zc>TQP5^f)u4n# zy3aw>yU7A1j98~>2YL=m{6=htZ1VI7!2Ck!OH_ogDWd>r_C;@1#nZwfE0fHR^FFEcB_k;%MhMrF@pt|1u-)>vA~Ixa5#!2@Yjj+8VZ zEF(jAH$WRNADK0)bh!`Lm;f8{eQ3N0=<%mDzB4(t!uO!9MQZickx+7xqSBf$X^6(d zO7^v}a=`^h&oV0|M2w~5kj0cGfhL95fIc814^C-t%^lY^M3)HvtS!fn*NXRs4AIK> zN;gVYcZu@j!IE=S$)_YA{Px>#-G|>I3urRFd-E5~6Ml(%QWO_eMkd_a|Y3l^}x^71S-i`U?PO4C!+6dIR= zj!?KB<8dL(;_rpa>`YlTl=~B>U2x5;@$P$64k`trw7um9LP6TA-5HHXoF5Kud0 zY{iNN9@Pu)WUGV59p14^{_cZrt%7US8sDJV(+vp2w)XXN-?_V?NoJkOw2C=Ie_|m2NEw+}(bPslzhmNMx-2JUAL?7H8lg3mh=6Sc z>1b*V2vf=ccLu*2(efk<4K@VbV;oPilodu5%C(Sm^!#V_0B{lbjyN zHnRA)Cch>xp-OJ5>MrR#xc88P`Rg`Bu3}+(S;jsV{&FOhcB9Y2_SiuAV9<`8{L7sf z(;!>gz#9Dp)@UrOpBk9JB=%>L5?;WRO;kT1@=R9^`mO?DlwHnrwVG21iE03YS4*!( zhGaG9h9lL;ysY*J7@W*iBgF(5T#c?E!q>1J6MVnKCm9Rj2zC90@3&sv|EL)Wz6SAt zFofdJ0EQ_9BCp+M`frwh>OBY_a#;*Z(c<6xj(C%RQ znR!kWOQpS9vS7+Ii9fi$w_r$e+?QF+X3Subx03mJ7Lg1LV#)EC-M9&TAT^4BFAub2 zsWcEP>ti_-0W1-TwF*pw5uNo255kT&=wnpeP?rVKUcoM-$PkYx z(ESSu6JZHvYpK<5q^dKU(QvqgdPB+h2Q<#YETS z>-dM;4)L${uqW0mjhnI1sL{5wv+GL(%vfi9{O&v*wMei!L_FSy7~U1 zhHr0w61Mwwe&Z2*@S3{2cxjI*uRjVA#@$4Xscj^%>@k+Es?(sQ#C6nlYB(Z*;ravJ z`#`8epn}k%&6xIqu#YmGiUXa2G7-{yOc83Uyu>fw|Lk9-sMoD^3STb$!S4R%(UMew zd$cxJHUNt-T(yguAp;-jx4?wX)R{`iY_AFHh!C>WfhL2#Q-D<=o`noGh+pC?EMJ)2 zKxpciNC)`K+I-=1e!QFk8kti=*>K=M`O?cqgyh_)AtQ0OUqv;;Ibmmd`X`vQzgVZR zNQ?*Xu|YJA&d!}Z*0ohgt(p^*x+U*?PMUiM=9Wp8+_GPlKtyF7$U6^J?ONp;ymMUy znm)+uod;_8cLK(lj0-h9P+0Zw*^Sy5*RQ-UgaN8h4N|Bj4OK55frxIS*%7r!5VErC zEQQ~_cJ84xHhe-aO^v)f&&kgy9r%xbRxGpdQxdaZ-`i`b&>N*&NmYO!`zt@l`(R=E zX|)wB78(E=v1D57UKv7*5;;8%h z{Y{&ADQkL6xfZlwK^R^+%PW}_+%R`GsE?t}JQV2-K_e8GK!<3M08b-fk$D@@r)7&ieOk2aV;#_>W5*_qJ0is- zStckgq#Wz_;vK3ODr~6R1QMfY7)f5Z5Mf$G!JEu=iuM4M&7Cm2Pr>Y& zef!P4STiT5W=2kqHMr~e@m;%&9UD=(iQ}d0)(_s*m^psNpGQ@>;%DJ*Yx* z6#5Cxyy}Ng!WMY|;2CQn0!B&1+Bs}}`=<3{(^6X%jGFstT8qqv;XA918?X-;4AjXe zSLE?FJ0d9P%LCA&AoW`zwm4h5O(t@a3xv(VI|}kSq*iD_Q)hIW5YpZhAY7k{BE8obkTFYQP+AWQJnM(L+ z7Vs-TdVtdVa+Rc#;VQ!%!E<84v`#x1-tI|cCLu?VBrRc4{QmDpSovGGtz{IL=hS~= zEgvscDEk~)J%{Bf6*`YR2Hbz@zagbJc3PxRZcJt_wojQ)0nig9rIk{47+Qrrb2d?bbzg1jX zT3meP$`)2SRyn~p;0FJ1YmoT{v_13IATxJ+M91-}o zJKW(>FU+?WpU!(qDV=6}GWVsS1$Ek#!(Eh0zEOTDn}i$)op%o1f4Jn)_&A6y{b(O$ zM7msb@Csh2c=Odr1-UsKkzW|X&HUD1n9xkEcJE{66f3>W=kg(C`ysi|V^~ga?2m!D za=ctqRv=RJ%L6M_4?LFM)}XFen{@6xt22-kmOqs1(l}c#)(JcxRmT%Us&$_f%g!ZKx+wpqIn8+0mB#h8-H9Ja&=8PLEVxlvil4i zG|2M=co1F%*Z^rf2^C?7qQmq21F~=+fMWJRFC4J;DP_Pv&;#_#wGUQVt`rm~n zCDUz%yn_P5*Yci^Hl@$Jc8laEtVi67BUF+jV0Y+#hbitTxalDErEeE7WgYc1qcQ3o znhbjO8DmHapHWoaw*hh(<9rV_GFeTgw5MH=7jc&(58=mIeRfXo8uTB%ZQ3z;74;4B z8q%J=L3(Q&hO@L-Qv~fF#S;)mFCK#0=+;l4%3|gmKLB1q-_#?(6RCaa`BT;lR%xuUQ3TP};C8``2LsYS8UOO-JEbABr9a$Z z2(^`tqFxws4_+pB=XW$7w`O7Wc1$1mHz#_XWulDEmZZ-f+XT&;#kaWs;#-tYJxq`H z2h3jSiuVshuMeMPtW!o0-I(|W>dCwxXb*7=)Y=2K5#nQVU;dF)tK=?wmG@Sd`y#L> z=XeIcyIN{1<)8&2$8bv7f-g3jVG;>zH4)h8a@q|5ni2U5e2w$m026#&huWwXe}Pwr z%!=1q(#B(EhL3?MunCchQuBE6elTqf?2WEZSaKA>Lv4!Pk&u;ap_Ym00uL~_6IH?J zif&1XJQDdga8PAU%^PDxvZ+V$z`=-BN^xL+r z9IO844ko?ix6NH~++2UDLcsD#lV1vOUk#WCS|@?a&tSh%mI9c;N|@N%&~SgzqO~!``i1p%(K3f#|FQhS+p9HXfv+o>DMghU zSNA7P?vpNx#o35ZDD53L!_b~aEf+4zOC!ZM3S1asNGQR5B&Q?#eUN2q$n4~RAqhGL zt4j%!a|5g~nXNzR)OY8HmoA=I-NVVZck0}`di9>WfF4U5Bj0oE+6ifh2?ngzvMKJ* zm(Fj*zG{tq&g1=+SD|ep5xIOpwP<-;UB!rfnT}D9KI{8ZQ?)9o)bs-vQvXPWIP8$x zH&bBo9EZf$!M_I1ZSF|gg}f!9Q}q=olYobbHMp>u)7fTlCm!kG$S^+1{`TS?tO z>QEFOjEV#frE-#>c|u=Bq2gwR@0a*ylqx@PfN5FUH-UF!BeqMw*wV#KV-9RZ@kTaa?MuWI9=Gg*Wi(z zdkt02t#diU|MX9+R=I8U&iv{(&dsF@S<*iN8y+Ou3f;fIxO3;0!TTd#g@h}>UOOr6 zASdRlvO!KnK^jEqw@8;`#?n|dDM|p4lG4?sNfZuGLK$#eQhE{;Y)DdEk~CJ`l!V}T zvM=zU+LDPTYG`s2-PtH41q=j>NusVjI45nZ2{h8c)K}eG(UFOqVfH7~K z{*9j-F{t|>7J1_ov+@1inl{hhzj5!IEHAT3y(iYa^X5yvr^hEh-*UyyL(6McuUm7# zDeOC&KPR7nWRFLmNLl2Gy)LckZV5UF5ox*zDvK3CXzalh@@5Wgs*TebE7@^R5LQXc zPD>Nmh@6~+F!=!p{ObmT{dVqMBl^%MCisvw5V3#w_7@4xd?dU1}Be7-QC zEW&_bTyvKZs7 ziolGUit|i!PplNrCzKfy(LyrWm4W8jCw#9Me8Ts#&K-Mn&wK0ylm}1J@Do$Y!d4?=@#9{F=lPS%d$`f4%}qKAKCj%D-Pek1G2xSs0-h*U3!Q! zEqhq@yYe)$2T_(zYLLOw5Fet;8vW$N$9pfOp}(0Lq(yci#0|Zxu#Q?z=8SS?+Od!{ zYNH211Kwm+PPb&M!&0-8SbV|n`77Q>f#u|SDFd7McdYWys&C>S=dXDdT!REAb6l704r>Q_y4{K2>lW5Tvllz0}& zj$p%Ull%zii1m^nMAQiVhT<_aOmaqfE)&q_Xe1DtfbD37#V=?hTqMr zv`b$7^sbC2dbx@wG+@RCNPS^pPDa2DlOWQJ4k?Pq1(-{y@!kDx{%Gc0KkE~iPY2#_5 z1U+Qr3!#+&nMi%x(>vg`@A>|>l$&x(o+y}P`wq#k^liE2pQN4o;r3H+5b9RaLv?Ds zL4eHAyZM;d(i@(meR#vi{uUD5MaJG4;21*|gE&T{$2=Es3j4 z{X4%WzjuwIM=^Jp)Yxr(Z-aH)-e5Vq$&P%-by&nznH>BlY(?fi%YR~_m08G5(AEjr zOWk$C&}T{SG)1Q^7*$`gE-Wa7U|jNlFe)gFWC@+Q7PW;gNKiufmdZ6NH;C$3So9u$ zSN=z?!_yjM1U)Q&_#pdE(@X_D&lVMdpp0*zxeS>X$Q_PjVD^K zAg5A6WG=%ryA~b`&Cl>~oykcMp(1@j(U4)$Lg5#?qD{Fx3bDcgNc5R=h}I=rfMFnX zCmJ$4$reWGDO^-UA`{4|OC>ob^+BBUhiCiT&u%DsZ#O|(w#^q6I z;sQSNUlK~-PxS4W_NMy=Ma(C2Vc}D_9+6Hbd2{ff1=%q{3|sK{qS%>SGQ}7aHF)GqJ)#!?z5HE}@aA_D;ii@PLoZ1(W z@hpXf=17hy_NEQzuwio=H1nIvo?L(B()Yt_Zy$E~%6h=I?myi08VkiBm^J+N&W&Zv zdJVqzS$_W0fkV*rwlb@9jU?_k+^`>@@!~KaW|V5JEb5W1q29W7lTYo9b|uCL2yOKHILun%6=(C8$lphB0 zpKdO_GMm*O-@8dWcKr6w?;iMqb#s66Jj-Yy)hgfgJij~n;=*r>2Ty!vK=T?b^d0A+ z>o-38G3@lJM*j>R2Mgpk#5%&P#mM(JRV{`JIbo*huBHylLM%^2TVa+N!vd&gM(#r- zUm^$#ETgv(!{Gqy56lHES(^50|3B*tN^RJt0>6S@F~7#OZkW`o@!VHy$P)XUNzq8V zv~YLlS?p73ds`Vvry1y=CYAjK4mt}Cio_X=RsFRyKw>MwV_BLK$2!PPu$s>F)DOWR z`c(f2QS1TWCbTkuK8Rq!*)_nF)C7y@Bbq|@A}Pv24L?z+D;TSG4#p^;Y&U`KJs1$D zBRUe;&Q49ge)-v=?ChekSFWc&Z@bI~C{;@jR8g8YUWXrB4g5rd~@Lelx=JE4YuoqF_XZ7=2!|(2+ z0V}JR^B@MSG%k+;D^q7pnjXMr@o9lmXZ&Sjzj=nNwD^uS-N$dD9v|wS^MGi2;uxTxm)?nG79LH^$ijfQcGL z$iV2N%JParY-dJMpM69|u`$^u+3c9ng_M4TUYL?*&3SJJ^LuF-|K~OL75+~7kWruQ z?~^gCPjIw+@691Irwm4w$`fqrrNu8^oW^jLPB;I0k;H8Ve}S&Auv$~jAL$wPaF$%92KgOh4|M?4)&Q;j`caXlld=S#vkr< zUv=N7nGW*MHw$M@FO)k-{dtXPm-rV#w)R!&< zsIbGqrrw)L`_MP_h&_%7ulGNgweqB9tRL^qH}m?eppf-r!OGT$c^Gdtl#*%GbBrbe zk$j^Xtv+fwtwswO1ReKck`3NlgCjPIHYrft8;o}~IFZOh7!@m#&_P@PMVesXfF$K9 z_X}_CW963eWBjiBwtUw8!hsP}r+{czCw#Z)JAMy^Pz=N{ zB|WhzPvn(>XNgxR;mQ)M>r}q&O<3-2*+Jfw9b#3MvVtY75_^~TXRq_t(v$9w>7QId zdfEMvR8RbSy7V%3*AVfNUC3`##zdtw!;A{kA$Vi6ps_I|k)}1{N<%6(4I40oVlibT zjf4#Xw5HKwo(Q)Xx@wp)mVgLl0vQ0Jf)34;vgOPXy}A_gtuH)W%@md}zHq`+bSEgi z!#`mC#`PL7Y3QIsAMaKwHt(FD``qCXKO7&ly=Ij?(~n=fe7s4I-mORWU<%D61{lVg zgtPOvG&k7GI&TR-UVmUZF0b*a^F}42`EXNeSC4v;-ZCUnfH-<2q)5WSsFtibkXS|~L~HrC)}d7? zupVq!RXBCSGt>^4q?d_SkUM4+-qM3n=10Jv*OqIL?!$w&umyTiuvs16^^X_0LU zteck~9p?U%jhE{%>G_hW`a} zhtZu8T&CDqMV8+qZx_sGnhW=O&-~qZrX$}nd-a;7tYgU3)hn#E`96MY-jD6Jzx_6A z&FqUVoCdjYsCi{Rl zoj8oyf}|H{|LD4A?M@LN@_S5IPUreZbL>P6T9N)AJHduya@G)5@WA1r1?Ztq zPl1r|tAbAtv2+oroPSaOzZam_!~A;zgpWgsmSkFqIGHftYR*RT4<0P_t*E4vdy_UF zO+w7Xf}(6qFZV}(5z$p0IxN`ZXPM4QX9-o(3|PV>nSBiCz5huylY)DUYOWyN@oDBe ze6(~&Xl7Wb0h(%7_HyP?&A5?IW6jINIffuAGTMVK)=+fxRkuq`CXT7LK(}g44kHjn z!Zt&%jZh!cQIyY+9><9rcYvd3-cs-{44Yv6ln<%wsHB0|ihM}7F^C-ucE)IrU8K$9 z^3&`LzyH{SzHGxv4PjDRsd<`^VWnZ-Rh}}f@{!efSH>(Mi0z#gUfgIkz~IH1419g= zd6DF!nCcg<%|Q1{FMmMxi|`{C&aWxHsk8mag~J!)wI;8}AUQ!{Qm{y#iP{aCQ;X@X z2(lqC2>qLy#zysxZIEdxJv!0zfUE$lJST6H_i#b>GxA?_<{O%e*_S?OUhcK>Y z%fxwqG3Q?X{&yX?$`?+*@d`zFCZleuKlVno4l~t?TEq}ySkA$WOUo>wCAZ^CAk)UkgrOn!%rs*dW=s`aQ z!(iPwXF8424ROXfrLo$4T})ARQk@#5RrfoO;Q68uLq^^(Sl8#2lAj&td#H_nrRahN z`>@OKr(p16u&&4M9nXp{{62Kh%*EHf|Lmhi*+aix)F7jNBlK8azoN~=_^9c9Us=c4 zI|r*(@2M0&QMq;%=w3_$kUL|4W+bQ{1oj%Wk4J*aoq1PGqb`-ROHblGBufl`+Zu+S zLInRtX#9)$KY`!{k6|NBWBvX0Fs8|Ou>&Osbh)T*hQvis9ADw*AVP>(ts(milLxJZ zg?FaudKp}tWS1uAx3bALxy@Goy8A7@gRv_tejhrrDEXU*w`MPuo^&5zE&1C()Ya<2 z&yfX^0t+M_>yEN4RAocfS6CSOVuiNXADCk0UNZg5RnvokYG|0KV27{P`~T*v;eKM+ zny@=zFvCh#KApk$Pko1vVY$4BZU3S_%OCV5+s^lwW6k&=s)MF~Qd5NesT!L8*{$w( zvCI1CTYncAlO)UYYNDt~)!12FN_}HOs*+T}wUZ36kVHQL8cYPtBoRCumzbUi5R-!7 zKCWw=5B0AG$0gz^it@%7{@fWyx(AmRSO%mFDF!VVnOcV$oVPU8%hi&Wj+x2#|NYUA zrQJHb!~eSQ8QQkwH|x;2>CDwHOqT8rFIe&J_EjyPPj2$=O7)#}PtKNNoCWJrt+fBvCnql9t{!C}Klu=y4_R)QPs*U`kx|--{af zQqv~>4wP4&khA%l_wO+#DSq3wvtKk?Sqa?b?Gs>!h1yL+cp z$NdS`p-n&vzfffGSsAFqW)DRtz{Uh+R z(Mf~4C5H0+rzC=*fG=SAERA)<)WXp?xt@%24m6#G_}GkG{vVg|pU7n;6)xx*G_?HE zBc$>EF?j!Q@%~iJp+}eW_xExqz|e*~4MoqyuswbK;d~F?+C};2(oe`ohqlb({j9rC zc@l#dd{xUd)vEQLMvg9ygtsdVdf;)B$xL(>5=e(pnqVH@I1;kwh7xV^-H`w@7zwUa z@`v?tWe_&uVfru?kv6i1Ix};^(h@10WDj$~Uij|4#*J8h-KOt<_x(FhJ<0bs%6*hoLvy&IFSIe__^!wtsWJ&5Q^RJd} zmfd@#YjlqMh(ftC;n#Z_x0r68Nkr%ttdV70kNWl9!7f!;Cl#J!Ddl`m;#5Czzt5VG zB+A3Nk`@cIv{03B64#HB?X^?n|=lkkbLE8B) z!%{kq&&Xt*So3nHPO<)cefLKG4A>aK!M2uS=SGiS=P+@#^#>-gtY2-T?Fz0FWPm@1 zWzate=?=V*`#KSHsjFH7J!UcGKn((y z#Id3487XJOs3@o^=u;?EB}u!Tx>G6b*th!fbNtSAZbP-`?7i)~wCgH|4LC4y|F?1$ zv${|7do3E&Z>!n4ww~g|{ujQxzHH&U>ksrj2!eaHnkCrlyZbCzTE02fZdg7kT%Yg$ z$-Y&;U;{U~9ld6jlm0CkwWnRT_WCwR&p;FF8v)hHVlNasp-I|4&s`_HN!I9#HhS*n zbsEw^kC0g>GBd8xyf)vufV~pd7j-ikZR%zsbem2@odZdp8-%YKZPOaeMnlUqjqVf; zEh9w>PbtdhMYzyIHPW|wH7tsHvtrdAi_0?<%QL?jne)t4c4q-UUh?Vh%)x*At@u8x z-=gX2h+DV9<<>71{Q?BWgWyvB-L=p13%)s64BEm!w>1WSjOz6_dUD7FT+t&tCV^&x z0_c{$;0|4=t1OhHl-4)5DRPVlR~v8kaCoSG&qfP zR5ZZa;&c*jBUy9d1*BPnd9g2sC$pe0uCu5>#;@Nw&HsokBx{Pf^s!*OKf1tw{k8r> ze)>i+?*$$^>m3XRN>l}x1$yrGKvmJ=b10_PDPfX0CJW1JZH8M!EHxYj^u9Ax?Xmzg zd-XAMEE&TB13L*#c3kabaX!O6|4hsbkN7@kvN<~I(-SD zFNx8jO&@?Y#-?17LgOtcn-=P0MLHcLBo7gIWXu7zsYC(NBnP?*dt zl7{utLa19W5!f!WVXy^OX@P=f@L*o);h z-AmKqdZlPO=E4CejW5xm#D`$oN3NvM!PuBVnPJ-mxQZr`Ww?Vrc!PfRHvMfHZMu9p zY#wF*4dZxIW&e@o%Z1&?*9gxgfV#{-&f+!MUb-X{=k_?$59<=13byD0h%w9O_ug%wIPXFZ|{|cXjHzbu8pp z`LohS_b6M`wI@ycmJ%pX3~q0XWtFyJUEL?5eIKS;oD67(a`k+W(Dta0oP9XO7l0|!-a;tCovD(Ky4I-iPW92JloA|0I> zZb^u@IQ6WH2AD%0Mg!EzB00eh?}|ac0SUOd+$e#Yn>aaujpj@JCe3<&y2AR{SMl5X zfOLK@awp%$3gp3PQr(*0_-pRC?Ynr%yF12CS$$(BCb(2~Z^kWRmVv0TzYNWpXn9$! z;IS0w922qAL=)%f5908fIGqN1{z(mvAvT->fHVx(;7-A4*?}T`qJvNCsD8l$#EQm8 zhL|`N8yF5;jS3L~$PkawAj~^aeL!7&wf4TA@#?y@_vWbb%>Oh^QqN!5z8nkUf7;u% z^=RO;{C?CZGWet!-~UBZ)q9UR^7J4Ywf^^c-Uycw8VN81>vNz6`kW!Np<()A*dP$C z@q@T)ItzHiCf5cgBwde8| zS;>2{@*Z>ZCA{w6Q`km6fc|NX5u_;$y(J~gf3YYtu=D^+DCS;^YbTbdpU{6VQI9>S zL0auT;sDHN^bndzTRSNVOl6^@5&f5$mS&gEHaS!?kMCp)*wbu2-^Gur*-1&L|9eRP zgem&@{xxf8ea|7vz7_P0f}UHbhG-QtI8WV<5$n_GX)55>O*FA5lPh7;{d(+LWdd*l zbEhcGKxyRUVbDO7z^Wkdx_A+`q|;kmB{H(LE0rl@(nJP}1K|%b&R?8|qsW@emn^#? z`c|&^XR0(w&Zf>3`f+GSQu;ZYiQS@SpN*zuL?9RL>5nfqsGo+(o_VYc7rWIxY4O0T z@-3lc+`zU0`xFHmjtb%u`4em~60PZqRcAO|KkSj(rlO6Ac6j;*aeS=kpG(@oJQ`8s@eF}+BxgNR$qWnQW zP1F8N2PnvZtD-u~i}<$_23axTz6|x8bY{WDYK-;@%$7IT^I%dV#68Uf!Z$cY1kTJ( zPQkfGBq&Eqf0OvOe$Uk5Tf06x5I-u6K^H8u)^rbD!;9tUkjp>tH&}}yy_oxN$-mnG z?qZ!UT(SY__6DOU4q?D1;T)aNY|p9XHO*FDLmbjRO%=MPed@Q$d~^=YMstXLqI+PV zFH1tG5r;6jQO_FfJ5`(?O{1hLO0qYojYtAb)x-{Sl9b?gRL1w`cHsLvwZsqd!4LQ! zN-y`}9v@7UKoi^V?#=NAVV|B9i5M55>X z#GTX6PaK*4K-eX^bTe5_A}gl<#@m0@-!5EQOaw7z6tn~%$`s%U;x@)>FM{7_h~pw; z9tMa%_Kg3qTm7VyyeEw#mX0Qsi5vL`Utz7ump%Ah=G*#@tF0Y4yNFX}EyneB-$2@Z zwDgjDSjffiAo*Ji>8&5QJW!nUWXPxEIO$53c*`oaoT!hZ?7KcU(G;o_6w&YU|B@nl z{95%7&?ypSIQ?jZu|0`THCB5C$Ps7c1F(RZ944j!H%PC>-xn9SJ-@5;JUb*KuyKz(U7@GZFB zWd3-CF!Z_+aFU{kD*Kf5qC^7>rCeePy(NqvLxF%Af?lq!YN4+5Vplr4d!@&v!%0i4 zmQF9NT#S-dTmpYr=fAf$3!WujKq%Jfn}L5*5j352)S1~euCzi1*> z66IR6ebkEER|*5t^3UEY+2`nghkJRkt98>| zYxYUqdeqrl<)c|MUucvu`Os6+8#<{FRi@d9?SbbBgAR;EU&SD;9vzM0;CkX~v`5yM z95Y>p8lfs`#UP;_DyAP8S#e55B-R{08d2QSFkuy2NAo2C+@a1i0Ya49t~eaF{#sffS#4VONC@4b)Z zH~7-G4qJ& zyi+*LWL$lNg99h1$Pns9c1zSadP2GwK!o1IsWLi8Mo}lzyR5vYjj80fzC0UVe*KyX zd`$%@ZL50$|AxB*wLYe-_vzE_9Up%TOBcZhYn=51SdJ}Bv#v#SS{MAn{LmkG&063% ziR}=vHZ&2!AvkjY&&YJjq>&-J(Pye$xpx|TvN}mgPHW`PPxIM4jg4W8>SRhEfA9w5 zHyc+iISPeDb~h~9ny4J5Ocpa_vV^kHX(_Ig{=mB$ONdwvkRk>H0;>^=5tVQ@JDIN$ z>|R8poFmH`Cr8&wYZCA&EY|~U&}7!`8J2X|6^in=hBe)o{_TDNy0S~zWNUj{5^4@o zEuB@H=+A)-BO0LDIwlaK&c9@c1(Ua6s*8fQ!`UbKYcOKm^DWXG{TF}s{qx_zvaRhRhsA^neSwe309cd+#CasKI{hzZZ9 zut_YX0fz8RP8m9=5}(DtQ{FrqKeSIL`Ybl zI!8a&%K&y?OklzNGnB*8?KFrSwPJv<$K#8EdvT?RkVQtZDfK6&c&=E~D30GsyxX;pL{{4fq)2Dy;7yn*nY5yE8?15<1Q`Xmy9UEuw`pSkn zPYjuLC7)I1=}qKa{V>W4-DY{!oBY;`FS4Mk?Au>iIKOk7XHR|R66?(OUb@3Ry~Q5R zt6a}9EF|&Vb9_n1`a=sdo{IzR2O)yh!1@WQK%TVZg7*GRn-T3{;z1eVYZD{w^($*~ zfc}H_;a=MN!UaToL(6ToQn-j7BnaBWR6w|>b}{CV!eW6=E_%X1W@yZs#jcv^MLC8i zYlpfT6uTOvyK;+NxgOesO{sVpwacjSCEDA-xkP*_FeOui8k+n!`IY+DS$S6W?Hfae zsNa^c@;CDOwx~BzVfB=W^;-1oaxS3jD;u+(7&7~69>`x;kROd3$}Viaz)P2*d-gAD zFEWXj4j;c^{P2rw)?CC7Pmh&s^D5Vm)M)>TPJ2m+k10X+L3(bCdL`DKaEd-K1e zrq`A%nJ!`;B6eym5;Y);RGWrAg=E%yM2X$&9QXfA43&1DjGk-hbkAYh8fPx2cnCwg z|DWQl`u_hj-deg^uH(n|9rz#Pt~Ue`n57Nhq-;j-{%CZbDDTN0ATmH^x^O{t`R0xP zz^6gk8^FNE)DPmTh8AnsK|&ZR+{I*wc#GcU0}{xYDG%dYi*GMv2*?MqNVr)~7mgk^ z_;tAEg=y`mt;()c}q|PIrs&=Zg#TrF4SU)1~ z8WGds>mh73ahYj5`YY%&hkJvMPH+s&_RJN9Nwz`4n z(q@Lrz8ixNa-`)Xq&Xe#T18AsWJT8aY3$ZWb`F0x&FUe>? zXdeQ5%Uxux#CO`p0Ri;kCQ)+RgfFsrxQU zspz$7Icb>=cS*;3?0svcX2W)`vG_f#4`AYV*8O1`QqnQn14Pj_`n zclAqm)zP>>5B?STE7XKH;cSI_JzUL&mC-!3sIB2iXR)i3XwuLrw5X@yd0fYSDr@PL@e^T^Zi8;}0=t4IIJ_eZ|Bz#sYEf!^Ky z#IuLTjmfN8Ba@vUSI~F-Gg&pN*J?SwpzpXbS-ms)XLt@j)u^87?x4TW-EN%cPxyKL zg<1GP{B5RkxAV}UoonP|)#Sf+9$MHXqc(o>?@~Clb4Kl)8tw=5Tl$IrqP;GshBe0f z7lzk$Db)TJ<*5%>@`3CdWIPmLXElGO5~4zB)HyXF9Zm^#E3T04O4W|a6d4lXuS%XO z1aXLPchQgqc7KGN7J(pYj@6NZyOU*QIVnynvs=?r>U;BQt>ig<*exDzbxpp! z?8lkYI=_^|hE=aWWD?`O1D9hYuVm@(>CF{^PB+8U*hJCi5HT#hCPg^1y2>$G1wvu! z0UO;YRsBPS9SYc~K?$Lx?U6uqsI~{ujx)gtsS+NRq7Tx;cAVKto0)wF^yx9|<$eJE z)+^V4<0t1aQSZ6R)h5(ToY1dlsN~>xR*&sCHsfhv_J0`k91sND%Hq57u05ldb$mLP zd}BI|ZuZ)^sh9;MdTncrYx2}}HAY^!A?OOpm*mj0=?O}=FzVwitzr4fzCdc_&F0Yi zBi=19vrdGrGWG`7iRIPLaAFPTiZ}MWt-Op^z~-*7d<}5S8sI%g@>i74!9fnhfMih> zqOI9vf$Et6pW#BC52&M?&&5>bNa1Kf@4$Qs@I}N30Bo=q#CkHRkQb4ltXkBwK}3-i z^e*3ok-QC-^D@_;3+8Uq8kzI${BwTgZGL6ZU#!dCH{3L}mXM^EV6#xKo3?5>Q32?! zVliRcyisq|JQ!%A$`N7Y1}?rndF7?Hh-; z$0X4}bVV(ZvsaZ_eqF+O(^E~JYjcec!g3&!0{D8r2UugOlCKwC6H=vW+VctqN#ObMc%C}#rCRQ2&&$XPVCSfQw5}EBHAuA? z&g;K@2_XaY$iE)tQX?&d&`0CGBAek1nT>FXDoTtTqm`phhJ1b8`Y>lP3g6p zqHNPp>ql4KFtQM17zq6XB5Sk-E&{Vp4b?3uV-GyqR4|snxyF04v0WSCp zDbwAr^fuO;4NkZQs)k@%5HnPem{6-?**c}Y5`vbkcgj@#BvF8f?vwwgr6OyI<;tT` z>B6T;r~B_=MTS}z!Z3>hCsezDn@92WHtN=p3dtpIg)&KSd3SJmdBNrPJX{X`#@--F z?Sys+#*|eSLJcysBhgJ!aXuzhtt$la8!wl8v(1E2F_0c`DTOEgV_!&C z`AhdpNJ_d(MAw^9QZ6R#lysuOoLCsvNZH4yvGyQL(-xm97N^tNq`JhB=vPo@ag_;v zF+gm!mOdV&?PFGKD3H}x>|O94mL{#^2ldJofoUr(J*nZ@u-cJ)l^@NQbXvx%VTRsi z!bsFD1U-o@wj=JPzGz{4LFlV2O3Y}0g9xO!zd_1V=OKeNfi$V9o|QdD5BMUOGE(DESZ*NYEcr{~ghbcruUieOq{PxYz}iHF*Vs4!THnyyiJ+5)5$9kgSCwMq z3X5v`q&-{#5lF+7Nn~TPy`i*BF_ND8G@`Yn15@TQF-}>Ft07-tYkBGLj*n0CeTSFu zP5A>p-WeW#WXva1mt2_iO51Mk%}WcPY+pY^^2_b8k7Xad@*0a+!>{gLa#=mgK4M7+ z*6~H_`Bil>zyConyZ!n68OIK;n>%I}S%$63ezsN;xTYHD=B}jIq^ z5qW%sI@U_xyfSUk>-@&riD%{wAKjCVzN({joWLY{Zx@O=oXz-@j`!NKFOOaTxAwD!PvdsVN^7QWG|Y z0@ypA@2x#AsX}Tx`4-!Kygmf|qGXRo2r~Qd;+l9JDWe`Bd1EkBIfNqjp3o)`z8E3r$qwKBbpq zfv$6*jba2{FKYBto)L7-1HGdJT_0%A%P6DwNP33XJ1u#tVvwCCo6BS%=+GeGR7Gbt z$S2SfUWvgFM(Y3w8VcLNrb1g5qN$c?LRA-gr%k&({hp#uL_OQ?`GY@{kOXKxWMC-r z<(I7J&O!}af|aY+9RGaJj$w`91f?q09${&xS`B?=O(8r}Twj+jYEZa<-qn02;Ce=oXG#p|P8da6}&~=5!xhMT;l8{#F?1MuA1(QA4vg49*C{H0N1EphD_M z=<~KDc4|AAo>v`Zzw$=xebLFrJrUBStf=gUm1adyEWS{6Xt8)8dMbIOiz!d&B~83G zNEK6949bj-GM;#kgsfp6BwZFmk`1y0UJm-(V$Pit4Jw>?L-JPs zE`Rxz*M1xgjgWg@nvjLm?5W+?ZaW$wr_|Ss7{A)s-TxlATTR>v!;2dKy^P3 z4G~Hmfn^%Yo1H30uDQEBXwCLxGh0g+idqDW4ep`C3ZFFtV%8INr>k>Ks%z}#F z@7+?P_;-HAJ-gS?F>iG%l=~cy$?pL^1DMvD4nB*5C2Z^rZwlX-P>WtsLlRJ!j%3?J zh6&pMK9NDOqHvc&$n09I8}d2+ro){dLcV`smX}jX?FcjBry@Mn_hMv z&zGafoRFk{YR`+999f}l#N?+)L>qL!osPgHNlig3p{w+$j~<6WHzlotM8X6**r7&K z4r$l0I#{Of);pA^mf#HUl7n*J+OA(l0_J3G?4zinB+UjBKvyR_=(7jbK zDz2!O1uiWL)Jxv|LJT}-8>nQR&Sw?TO(VQlGMGs1; z7A7KSNaw;{HA)~bwZRkiNFcP~f(MbD6G1R&aH~^p!1}s>-OcYWU&fRjAG}pKecBND z2X^LR-qcG=7GInuxofk-)A|ma%CN$jAW14#7z692x9YE1FFi~s#1B1K{Pwh8o zDp9^`*)`iTbf}6$Txgyet<8}_;ae5YzC_?euT3&ZSslVG92#$3k5v}k_<%z<7O{j@ zq(0_qjP^^MmSI2vUCPQvhtJ!a0-zEAxly=+(JH}G5~=_RbCMzwlLBy(n%4E=NAn6; zesqm&Rx578kptB(4hj;%f?7`OP+4lURpEf z(AgdbXTSDvu5#!gOXj6bHoW%IyrwA#j^PfMUB(bb7Z5u`)Br=@R$&|KQYev%eMpwF zCq(uM@eHH$hGR7h2T?=ByjN898Z$g9Fa0kJ9!o)0hU= z*V9ISieOkD7RV~{&sY^ZEAPnfY{kyI$5fT1#eZ$!nKmB7s z*1f#gs?zcqht3W-S-A7z96vU&S@*%%Y1Oie*2=h#Dd?ycBJd%aysd=IRnm2<9;Y<5 zJhBH<-+47ERkWXvN-uI4B2k!_V=zF~B%!r&4Q-zD#Ga6Q2OtF?w}iAz8iAXDF&YAn z>y)cXjcV1AmBy@f_3})){{E?xfpyWT?C6LIywt{`Pu*N~RAG^Wr5R&i*)ea%lc)Yr z#?NTkNx3Wc8pUk#jiI5Vn{f8dYi;WDfI>#XM(zO!*X zcGsz9<)!tk-$*r>a-io(rrh}aLSf487rK4_?5(zW&-N>*aeUCXgvl5J-Le#?6^|WR zJrim?SQb+VUysJ9fuhb4;0RNk5LqKbqcAnbUu#{C*(1Ik0kJ?U7w40pbx_0RsD=>Z zpE07zCLh~Vzly?t3f#eO$}F;ZUY92P%OF4Gc z(E3u1iY3S^=}cp5UznrRnF*pryIw#F%HQ$R(IF#0cNI{>`SSAjz1rl<)?C(uF4pX8 z%g_8QD;szPH$VNNuj72lubU;da{608;a)+OoFuex1H>B_-fhBK zG0G6^%pgO=&U|tbJEL>M&XQ!wmgH$Qg^lTgLlc_VnRH`sXxMuP3$4kyRCQjbah*m@ zCAwabwPoqKGxIOpU~&fkw9N95x>0T1&3BIWSUo`;c6AhG_M!|9Q%RH zicJNa-S-!y%OBYxi4k%z=s#;_%_rLBktXOtnqcOQm8Z@JxF3iLnE!{f?*NRVYS-Q~ zGrOBg>ZZ_=uqpIFDn0ZrEi{D?dWT3+dJ&M`yC5B;!)6H0h6NO*MFmj=(a(kz0ShI2 z@;~pHo!uG4@80{P+1=TC&U@Zo?Pw`>5?&JBeQtzQV0PWKRd>pJbCWN|9YT38yGdtNlaKF*Hq$o2@jTV>yYJJU(S z=V{Pi)bO&Nu#~M=FQ(-$K7k+kp}l#_U^78-LE~O_13|9tH8Tb5i{r`4E-uh;buMV zWQ4A24HVU}h(H^rhSsW`1P%cCBV>^_#1anoXj!bDteH^_s!9>8Z3RjB>3j))f8K*_ zulB8(za;C=6G;9TDkw5L#)l{~`2<$GYWpsP1wG1F zk%0WGcz(39iAC^VOMYt50f>M6`r!8m1M&UXPJ(FT(jSyxf$!0@QiE4jH&g`OdV^3; z^iD&T7)lA$;7Fop(@H)18^*K%XIUHmx4ej7VmWnZ+OW`eQMF|&cYpNh4}o$9)l%|~ zIh_@C-m-dgdS1AroG->IJLBB%0Z*%-&N!-SwI}0g3KW(U(#E3wQ}7OqX{4T_dBy`* z*U0*5lCP7Psi;Nb{Uqza;=%}io^H=A&GsaG1^;yJA zOj^dbjq5q;g^OQanmBFw^L=Ka*Db%Zh5y7=wto77s9n>hwAXz#p1=3sA ztH?+!18aox&ww*ls5Zs1@Z-c#fsIImgyRuqzBrADbcgI@NPZVJPHcL?C*SwxAMI)1 zy=^X2HnX}8WM+2lGqq;l#D%$7&w6%8x+DE1*^paF>wElqmDE&G<+hfV?C>2+`wNZf zJw2qR)+>0f6+2vtC2vNgcnSwTU7aiaHA24CY5BrK zJMsh%&E+4mEtUuPo(S)XsB4Or_9;F|df*SMTT3V(W5fA}ou+Mk|4l4{Sz2HX%f%Xy zSL*u^b41Z8WoEMaQts;ZBW#vK3zv6le!15!KzW=JE>{2qlGz+P3{p0S4HhTVNxmgp z@mZGL%(mC_mO|?yl>X`rT;T>80ggUg`T8_K^nLooZzRsiz#NGeSb@ z3W3mVbaDCQTnHp98MPk40=+Cmusn?QJArv_ucM1YXVzM-UUFHk4kPw?z7W4Z1o>>u z@%s@LPX}V5Xz9Oi`blBQ=!+3tPWNki=%X-EzT_cQ@+7kf#SjgDIYVO zk&m&0b~>h-WZes|WpmeY5ZcJgxHV+{4B+i z0e?^j%K{745qQ#SO?sv7gmxKHzNxYWdHa|(2LnLQ<@|v~N$b>&QS{tOBt%(e)@KAUz zL~}K&>4I|MkgFbDy1G-B70ZtnEnAt}Y30)6?)rOPTwYYP^u>K_?k4H|O|G1p{rp(H zwA%I9``(9-`qWFWRTs~WV1s7u8#HLoqL#Dw4jj05zC3yMzJ2ps&Dy(nK}ore%Gi!2 zbyHjQ9+KIlNu9R(1L?<%CXLfOq&J_7a~x?IFSV6d0q&s|$PHDpiz$GT5EV|AiL|2r zw=VMES?EPc=40602sW3_zrAIgR83x~eGWrJWUVY+rq3b5f}Cbnj)n5yFWtH&ujG>> z_+++>EvHY0SVqa?tTiDA{DFn3@WI!roehW`pxut5H51-uNgJhikZNg{;=#N&0?!0L z@hYUjATs*47Sb{~!1^jy|CDLAv>;-g@J%rcLHH3+x4h;ST>v8jeco2!F3va}?m^EK zD`%KH{;4(0X8pENqh3iEtTc5lzvh0#4h-ws+I>?_z)FS-zva82w9RUwVKTX?zk_bx zsmU>7nqM+JMUoFMDJ9LI0V1pp-eM8GIFd!ZEN&x_4w!l@Br+~CHB!zi4D)?lEgvZy zZg6+Q=Vlz*g8V_0eT7l_2F}MB9Z6%?(5OE?Zx#^bmzODc#erTBAg~0y>UR zY2Z$SZYTNF1{X?N4O#?+M!}cC53Hg{LJ~Zh2{__5Mm%4hvcz2##bYoGJg!85!rXQT z{?Jl^G8(AS7yhWh0{-yW3dmXay@P*zJHLLDZ+jd6FhB*JoLL{(y$=rT*V4fG4I zW==>gm}o=y5*W$M)O)D2^E!hxJ?qbDdM)e2c|;W>qw^Kl|V9G1|khpIKt4H4JwGH zkeQWJijD>NkWMY5MVioi9|5-rfpQM2U`ERhLMN2?;xTHP@fc9Wdq4z0OHd^qJH_Lq zVzsjNv(U<_6sy&|7u5waW+&KzjPxfb$oOBU+ywM!M(6u%;A6HE)#M4$*-GfPimX?) zO|L~>`eh|w)4dyCyZY9X_4x!@ac$U(#XPM`?tw+dssb+MP`>Z2cdH=^Bw~pRg z@`F^%jb<-+_~Va1a{tkzNAtUT_wLz48e~7f8=Z<^X;iR`Q!QG2QE&1j7_N?v!rP-? zJQ2d=H8A$=MNW~?5orq$>Fa3n9u_V_BVi<1sCO?gEE!P{+%ZBr8lgl1WyLA)UwNP- zKdAJ{;)4(^e;4ZE{HB8x)@46GdP=F+>EI6j8LK-vSfLpV=x+WYuO|millHs&k3BgC z{JR?_wDrVR~kI zR~zF7C|)QVHkwjA4Q1XjMmL9g3Oy&)!*GqMjd4S1Sq8cy^(};QwLH8C>NTpYKsW$U z#Z*leLUyB%IS5Tn6lES$p)1A!aKH->yfoefp+>|5REbCd(30SccZykyQYF4^1O7=Z zUT3lOPj6#v+nKuj?%9td-VXkhwybZhmOlZ4=f1B=^)#}5Yn`pE_Lh}fE0o(hdoBAo zuFhu(j1NZpQ3j3GqF{IyYD{OUVS0@zc(O?g@KOZCw6T8}E{$D9HGHu|gaTh&&~M?i z3O8i_*Z>QGT9k_Ki@2!4A{C$bG`Z)U2t6=I0z+seJAnca3CJz^@?7=cv)QYncW+>8 z`Fsp7{$*KJ-YA>Ru0E6BYb+mBzHIgM$BSI4JUB#3dRw0TalXPZ(#%o?SxxH&$6cur zhbgH?Hz(AHFrh?7q!RawPA%#b-4}Ck3u6pD#=sCY?IxP?6OhM9avB?qG`t546yyWL z6a;~T2ObD1ZYpZ8XccKr6^T8F3?LL0*)y{<@fVih$adK24@1JUGnMr_(9Pud*7f+u z`s~ENc}tn}py?HU@^GXyv+9>sN8*q9?W*}@&5`)y<>c$1wxlzy1InOj;8E!QpiTbN zHg8IB)(u6Uno1Iw#IYVTQzJ#n541Oz1v(rQPM=f-FtC9rO9c89g;L_X4cTd#S>a@T z%LO-?RpN)&i2vDyFIFMb@0q$Bb71$s%ql&HDum22{OZQNE*Af0;!WbzIao2}PDF#2 z7pk#ZCtMgqde>!vfI98!F?1IY%7%e;zPJKuSfDkWOh9T;en3G0@|i*c;KV)^aETu0 z1fbWn!RP0~iQ)Y?Pe?FmR)}Gk=JF(g>`=M_9!5>#MKzm}fI=&145FG%-pt?U+up}N zsp)PeFXbQb``1}r{{Gdkp6&Pj-o1NSnQRs!9hv;~(l0Q@qvhHD6ESZQeL;V*u7g*w zEOe+$)MD;a%hjt-8Vzdi0BPJ-5w~?9|FgV>w5BM15`2b9bYNIGC{0dPg99j`;8?Zb z`oZmj_uqH)Q8BEEUbG;RJ)e5h)QdlZ)e1rA>j=^cH-e6dIaoDC}E9H z%*>?P>uf{}G^-koPq10pljH7?{^CFKYx!MzK7Hez-RFOMZOEw0FIFkn#PRcuGJ&rJ zvwq()>GUhf%lP%DQzi^$e^1(z=*-v#tU&FGeV_F$SR$d&5Gz}Ds&Sfj9T)0i*Il?( z#BG?TZ?bS3>og(oLB9|wiTXt)Ppn^%c04ZP32m@B@F{>sf{KUQ9fQonjw211kZo7!jp3oiE1CQHDRghoA#RB#Lq;K1+S>gA#7NNVWR$&AdKGg;t3k6 z%e44;C~b1dLw6M^jwT4Zzh?XS(`@&6tbHY9(h|gi93oRp(yDHuW=DC>)#UXP zsz2#(WdlT#Rav;KtUmi2ag7MyrRvb$1k#W4J&Rxw89tJz(tt*a@ILjK$wp@>oK3P4 zdi=E&f6QBcf6j~F%#q!e2^&ry;LF!dmdcOoGj0(p=oHMmaWoc2c5T5B5%|b}mB50{SAis($I8m?U}Bk|M=f z!ll*XGlx8%x#IhbUYjL8lZ($xfNV&S5-c(BtkCal^L*xY-)HnREAg2=_)L524t&OG zNtKq0&rq0@*0#d;8Qzl1NB)#`~lcs3t8M88MaYlw*+|Z;6PGL=;?v@hq&8OoQ%_`7PDnI5uh9 zbA9TxY*=gJ5;Oo#3t}^W+kA)r0E0d7xN`EfmP+^tSCOOQ*D-bZNWP@teg6Jlmhhm= z59ktB!XH+A#ImQ3gg&sH-&KAF1nuA|!Z-v`c*zB3o{*Zx@iau&M7K!k`5Ci-@?fR3 z;dia?JZxlp6W^DH??cl?WV~ftsBZ-knb&a+K_mJ0>avo-XtJE_q&d*{XEH{?ZziAM z9i(p_wUH9t*OdeA8tkCd+I@(Ztdl;LPOXK9nJN_KNloRy5TlWiwI?{1?>Ds7%72xt zkPD=y%n$EQhgRDbEA+E8RAo=J#gi4S&fR}n`Wydp&h3q?(xKenmj4F)nLl~( z!u%w~TBY~Gfs?JZCk|fNyNcD?du-0&$x`H&(uEtAty;@6m~~6(BL3~775qb9^1@4n zEvrV?Xt{CY%xRl9wyY7IzNI7L$)y{3JL$453cM~yl??U9Ld7&?<{_A2icuhBVagk| z3g{#O-Mvjn^%_%@>zA4jjH#71C-&^+u+pH#%CT3^IUWSONZREf z%MVI*WN}7ex(BLrWA%P3Q0U<>fi(t&5lC9bXOob+6k)i8V+KbAg;X=+@Bn8E-NmeP?y+HE+=1$>OP|b85+J((16-J~7iDrqzk*6U$oG**9FOmXkdF z;c&WaN>u?fE$x+x$_99$5oU}qXZOKD1pCA;ZXww)@;TH;OJwAPkPsxI0(%1L(_q+a zSfY^O2}(ux`Qb0L{eX4s&AUmp2ffhZJU`T%9b)A!HCn-xBTPA+^CB-fLh_-eB}+b_ zoWK<5cyQcL8(ZpbV0J`UkGcP#%-ejVv8#RQW+rX~*Dk;(zEV!&6P56Z&%B@b$@2*- zeS&Ug;s)+?%%udKqX$2HYyOrxs-G~Yag^X_8qNLFt4^}iW+7Es^%HlrQLrNI<}qnVcQ|F zER{+}vyQ9+_E(sBkwZ1Fv{YDYtoWTIx>W_x=}*|e`&hn1ab z_L=;2+wSjU#X+#HdZF7;GN!4MKeDYf9`}+Z6wup^=c53}7gc|)1F}C^LK^gh7GWESFD4$Yqp z6g;?Tle}oouWQcLtTSrT+m|m(d#D|*-07#p&&G|c$Nvhc;EMRE#09e#P{WRu9b#vF zw5^B*>s#@j1F>b@LnapNO>IH^%_zkvo6WF(O-C$4MB-bcwsfrAimnfxy4OGb$tV2K zrcLa@tzB#!KWGiGztji|#EM(K^NHi3I03BF-WKaj0cUzujnt;cpV(V|~JfvV8)tLhh~<8a~?Cu#q*@5;ESOc#%x4XTR;Uo<9lk;Bh0gozF;p_m-j zNyTC!pLT45Ek_H0z!mV1GbD8a|L@(j>Drmr-C115ORO86=;{lpbw*8m8_+t)_ggzs zS{Pu|{p5Maeg8SR=F^os8BSP0uCnEj8fnOLHbG1%CI2({gjMuJh?(XkB?4lJPk$E&*} zhW;a5Bm0QlB7A+2Fo;ka8{R^^z^*P!#Pe8S2Bc3A03e79XdntpwpZ^pj(Xgq=C)pJk9@fCo86S1mGs1ImoIjd&0e+oQb zV+J*`&zkzyGI5cqgQG4k>gunw#YJ2FLQjwsw9^8wv>;5#guB?s7gR@0svSqAS54fx zYC@&(xj)sovf^2!OIGsv4Am6v*}9>H^?hH2{%D1b4WO(a>S|r=YC|9+B@PuJNK&BC zC)I*PMT-G}qL?NMIt4P)Yswn*s2D@|C@3hX$H3xYzGkK?Z}e={qHS!sSbk_hX6v4B zxA}uV_-N6!$rM6u*P_KUB|E$S^?q*an!jAU5G3Wp%r*om$o(@JvDL zS@o;esFN7m_4C!Q4m^m9+o1zG%-jCK+g*S1#LCz}$?_BAOgBE( ze#?3Yc_QTd%CKak(8dB^UP)ykw z4MJTbi(NyDT?5ho6^&r2WvZ)XsH<VQpz7IrrL9O6#T8!jh=y52n!VnCY}d4mV!o_xlg4e^ zHf`KSezo_oE1RVe7y#FC3nV%fA&fR;vc)*f6~N0&*e{@*oPmHlG;4gH9ojyjg72ziw+%HJWDsM zBcIzLuXAp%tm-8nu=*d48S^1OV~b-I#_!!bo`18qpKP+)Pn6BMH&9d}%U$I~H8wVY*JoMf~V0lM1EEM>ulqV!bg$M;!D_ke*ts*== zXv3G3s+O;a=i!6Zw8Df?%`JygN8$7dPF3S7ibrt>8~DrC3`ke%rv?UKM_Gz?o;I5! z8sUGL;;r~oV&qdLO-h<8Ar%wb_TIf_`Bv$iRIggyI(fZPKm72+c8Ve?imgjY?ULH^ zr91WN#iYz@J9FKl8R;2~Gx8fW>D|89K6iU1U^DC{VNXjZku?%+>81J^vTa5DIo6|5 z=(lp)1KvQVrZg~n7It;!)(L<|d~(9iImAPc=n-2yU^*G%m#ZRpylZOdl2CrBsV zi&@3A+TE(9eZ7DFhF7|+=*-%#a#!yb*}g%w=eNz8+-}-%bg;B6gHl^j4o8kpmd5CX ze@9=QH{_>SMRx!NV=H`%0K~BXCt@*4=~Bg;E;cnnawP7aOnP#&(m$ow!dwHYwt~YS> zR(wmL!qJKLCTMzRh+reoUuc|J0Ttb#@nN|(${Q2C>ES^PwOE~DX<1n*Xq@bkNYbi) z!(Y8H^XiPx?kn8I4_4YVxlNxAiH)1pYr~GUc<+sI??3zJ7X$da{4EJl$L@btnbNg+ zzjn*XgVMb8clowwCzUZ>Izsb%!%9U=2DdbXf%w00SmbS@}?@+8KA&O9QN#@myy1zVIeFrESr zvcl~<*9$t;zg`daQS`&m!ArGk zy(#-+C^I*Y`{?ap+-1_!dAX4u_f5y5s+er?Ib5- z8k^9lK%E`BN{@I{g|Y44KRf=TrPIEg&S1aHZPH}J&`hbe)Vt(F@~NWd-W=9@YyVy? zb2nxJ_Srb!M_7L#GEjP`R?&$J$B4*_wjIGY^zG>GL6rrF^le1POcrP{SzRc-^%IFZ zY8mk^oB(VulDsL9z<4arnluU7#ucL3L)O{-`zgf#AKtq4sdU!v?#IFb(~5xU-=MV= z%z%lnT*ZrX!sV{7MK6z4S+F1(L`+I)RxeW5#;99xQ&hla&>-J9mdd2D6eKgaO;vv_ ztb(3i$%Ye%NEU1tIRge?xLP4TSwmw@X#kNW5a&m+o=qEXBJ^58c*JR(Y}$-9ZJN|U zJHAIBe9M&K1Ha)ve7oT5v8-#amaW@$@9?7Z+N7FwswS*GdF178GZX7y-uQZ--J@$X zYns)vE=jT)IFno0f2`#hUr%lD7QrC({d*;uAsz@WBI>>5PyrHfPh(1TDEZ9L0GryI z2|(NU^_Z}K$OY1O8T;uizB5;_1f}7<1fzs;jg;Ww~t~sKWBrc<%d4oz3JFf zBTl_P9o_h z43MmB`PaX0T))r${POzW$Pt(`O=-}oc>{^PbMxNsH<|6rhkt(|pL>np7}_^4kIoB4 z%}Nw#Crk)i1m>Y+*#h_!aayggJ{D zDK(4P2n1;k@$qc9bc$Vc2k{|nmvjqzEJcNk@~q$!xli~76a4jG5bRL=Hq1~&FGS$L zrrpUFifXF~d#-EX>&Vu5R!uG;2&c6C;I|9{4jE6T_!)yFjg3=aY^@AAOTT@<8GC|V@ghWN> z+fC3sCad8FubJs6_4-~_sz?Jk)Q~`G8h9)yq1 zVqc!DViH}*ixNpfPN!7)MSe(&KcXYcv`SDcMi(V zBbV zu|RagiGp&It*m->z?&EO-G-t$*u@>meE~t-DAS-CP7rn13s`6A756h> zKF>(65WmB(T8|4#>1*nlYvOk%{o4yHq8Qy86D!fvMKPUT$R)dW7a;BcJurF*-w5Vp zAd@gD^xi%8Ha}2_|9p-A>_o1?5_hch!=rIUQbEZLxsrQj5!NMEC~v|e%A*%Q^F;bJ zq7r@zKZFc}9qeIA5enEK*zpD0cPX>+FbEyMjatROgIb5K_+`CPTeS$8qS|%QVkbUN z@}6X^W3(b*orN~c6WIbjjx8P)%r7fTcsje_e&B|00h}J%*HWw`#8OMO8u~Ds;I7NC z!XOjBp*(W3$~}#rqz0_ZpKpBr1Iw-5Afq~<82;Ani|@5-*{L^vXDEK`Bz{M>cxvfP zXQ?BS&=M2aN$TF_gA`a5D0{#YB#}us(Z9rZIK)D=HbPSoXG22)YP2+)ZfeoZa@-h= z9PQTy1OY{5IuzCC1OykcPV5A44ai}+R@PBreYq%)y z1%&-AP5%pCtNxM@I{s#6NktyM4e1CEl{P`1#S{1;xu^RfyXgU5$pwO4R%rw>zQ;kc zo}@;K>J#l}O=lSoS34yNs#3ftXMi6)xL6Gh5Xpc3p-=#zS4aA!I`Y%#CB%9V>QYn0 zht%%y$6%-)-i#1XRo|PjOicx#p=Mz0dZD^U2@rGSbgcHM0bdqweeHrz3;6Kl*7H9< zrUfj0tIX#&VqRb6Qv_BV3H$o%uuHp17hsmw;H!fn3CdfZSDB$dn@9=Q*{e=zYI!Xs zyga~d$PzXCU|e?OjUbLM5=h|BrN&t0DZLxbYp`i#>=*&>oA?t%W{O5_{I+g`bQ<6Q4{ z@3qB_;NX#=6vHc7pjr&w8ce#lml<1vM9!lh(<_DK;;G2qigAhXgUEG6=7hIo*M$HI zu2Coqj|DROVG$x*8a{|<(lU{g46a^jGiUHY4YQJEgz6`{HEB60bgNH7HDQ@9Emb*;ynf;s)Mhfcp<#c7 zKZe0w_%d@QGRZdUe^*3gnV(`!*y8`9j3Ug z4UjNGQ|v4ksWkv3+rYQ}CCkc^N8{vnArXLN7hv;993W-S1j3`O1c`y4(AALrj9Y(W zeymQfy38?(5!D_*KS~}zV_v~5a+xUy$%Crt zW9zUkyQQ(p{K#m1f*{n_VcI>}X9gdhPrNDX;0Wl$4=F7@NK5s}7R8BQ39__MZ3c_m zr0?kx&FHChS&2^nnPExItUVc)5z<>1q*^6aSQUO%KI{JVl+=?S;3ezX`)ob(EBOt; zycYC7^0kH`#!~@VjJ|H1pnBkOSr6@)Y!ELVhlGk6*AS`{QT_cylYg{ku%R9pnzWDe zlb5-y;Sx!84k|MQEmQM2#R_JUn1}`@2sAPpP=c(*6jZ~=MJxcF?!M%IK!g38e+mUF z9RAHq%JZYI%YI)BAEU&C+`f3_QTXuTArG%!yc0U!{l?&Ff$YPm)2E|&cHktOBkhpR zF&Y&Dfrc2Ci84j{h>lz)E+oVwNkoh0pS76giGcT$9H1N~hTD_a5>?5BXi0<+ASm(ElhjWbR=Uja&u8{2+$^$pT|g#vY6TlAmr{8zrIt zG((k$MOgsZQT#a6ry)R!Q;-q4ipK6-urcF{ zkj9k=PJOK8Kao}%Y(J4zkWSfhKDOk~5L zRGb==Q|fA~E2n5Fjo#Q{q7pMj$+z+Aw|FW4X3pI$zp}V6%pV%re@g{Sc>FoAcm5fL zt$X`>R*S_XxS0RqA3x~Q`y*a5V#rRip5X_yL+u95#KU96v=}3FfG2t+OgvC$4dk9M z@W97dl^Px@Sao=+QB@)?VUT=+Z=zA0WV54FTq6nYJESlup$hU7DBC63Y3<~`^-Eyz z!1Kpz*{io~oZuHB&sBe86;WIA%!w`o26U4blv>a|^xhQ4tbS!WY~Rej<=emF>%Wmw z_~$*FH0q@JFzlIv1D3U{QJIzviM@tDNoz2%L7g=S=C32G%EV+GF`%VrHd{sEl616D zK^dB&&CEfWHbNu@la3S|gLy(Kw%HRx2rPCk^hjKJApjG17G@TRJ%8oC#ix_qBwZGp zmtJQdX}eW4L%_Y+vuUD64_9=cGc4L8dZn9*8g(5~Y)~(PposC4sL`Wd7KZt_4ltCK zPP7*`Q;n2&{R-^RW&?X@l-HRbP>62?Vvd3=bOR~`5KA(7 zM@@E&2`6=rg)tz2h7~@!OlyQ}ehG@}c(@C;V-Dc~)>T_>P024;L2~ml%3GgT5;e)6RjG92^NRI-RQ+Ieq`8H06{Jr~)Q@^ti z{_B1BBUlX2743URUNB2K%U6Fyt9)^ST#9v;fyWuHC zXIIFNU`X;r$dB@(#z>RorrnA{UD^}0Cj;|To&FT3#%SiE0_O}TILQ_#VzF_~RHrmn zErruv4WTDGI`i>F(H7@n+!e+fVz43cPWl22EHWo|=#fg1Nrb>FIa}f2%uGPPK7o3f z>1ooQ<+a=JgC(DS|5xAc{f6;JKVRn$SgzEm)rMDAtz9Okj;fZh;mEGFJ(pB*F3#V2 z?9{I6b*4|2FX}q<44kn)`P&S$4F!4X`im)SV>(u^Rqkn%?l1D9JX}Sv7P$s!^$>ra z*dEyw(jxbB@PJJO#L1sJ5WgLS+@%16W^Ez?lbUV%ZLhD-ra53xacsx`_hli7vB@0cx*AC7G#JxQ)CfpfLC}N^s{a35e(z=R29`_2OkAG9KS6AY{!`X{7s=l# z!5{?U@=PJF_Z{=*G4{z@U?A^}vAmoJl(W&(hCi2Ya9a zUhVzQw&1~8{CtA;r@=wUJaG#4(O5wU%>?lHr~gN(D|8?oKmPqlNVHI2EaSn@qHGK3 za2{s*mSj92u`ozEw6Cy|_lsgq{Zy+a<%F+FOwPz^+jHa4Y!d(xyrh%+`XSsLcPr7McRbs9LR z({b=6&vLsrYutfk0*jcFOtgu z?-Py;0$$?=vl(NIht|@rD?|F!06x;piUDe%(7Ir(dhyQ0Qgpl%8`afXHB6+J03(d& zgyc0*Q4hzLiF(o*@+cG2X;V%)0%2!ltyQvBX3`Pn_Z|CyADZ)K%?{0H&1%wF3hjSv z+v!K+sNzlzYS@O!ZGexUbtMby4F;8$Mw$jBi3NLzL_|!>U3h|r z*OYv0+JImY!ZR2~J!+>?ALF8QRJPE{(JZQHwq+F_69R%?8yK|er`a1pLIxrhrr8$0 zfyrz~-TgK8u<00k(vXjQwc7r=!mR)D>QzmVWIHZgAjJ}UAUpmP9+k1oP%Va;wix=M`R&`?KHR3Tl~?lyUWlJRW9GA{F!p8~CNnf@#x$d3OD2Dp@&rL^`*Fr0 zmSJjuM;9}8HPB&YqBY%q5rEV2R&Rh3Aljb5CqY_}=`@2ao;*n$qF^0PQwHHj|KWFE ze>{bdpA@8}rx6lX@}stmV#R$_f3x;zV%`4>h0zq~riy(O;lktoFDj(<7*xo2OOp8k z%*MC~@0dr&*POcUDe&j`^n3?AN>+pC!9*q|R_;Z-5G}-n;(0^(-6f0Sd1ANFytFVj zsrLCZmMp-I-rlO+%5IEwPn5#GV zX4Ye1k0=gn+mZnNC>C^C7L++e4c7D{$Wgt@#B_3A@Wg4=197QFHGsf_E=D*yut8&b z75w&wZYebRGBE}HYLM0vodrb?4N@@lF(FNx`G^=gT)s*MRJeU{uU}$Pi>4u)8G6Fl zSfX6=ZABJaxVBx!-NI)l2C)khLLYo-WtRW+pXj&j`egnUfAH=nweH1Kk>8j!EWBR< z_DBBw?bx3KQLlfUL8lXz4)Mok&>46NbPGcsG!zcABb(7)sQF{OMfS{~!yLs3(rm$H zB#iH5z2%qqAKNfZ;JPfA4`09J$g1`kca9WH4B{CRltOeSxIb{adl8Fg_IF1OroEIj zn>;jpz$jo%J#-cd0oKG|_EU%7}YR+~Tfn5zg{y8Hv<_g3uS4}ZCdyoD}OZr4S7SA5A~0$t^Q#~h!x_no)))T%Ra zp;Y_wMx)yz$t}0ruPDFjtM-R7pyusuK^oj&P*`RHzj$jfB+EB~cj#U(ANq01( zy9kspN@GYY7!otc8)ibke`fcgRT2gU!tBdVh*JKU+~+Pv3DNF|kzG2Eh~&MJpOE3l z>U=t+z}3N%9EAg}2ODeu^&xSMge^c>VBp;l08~gii@y6uvQhe|TV?`bIeNg0)hGBG61*wFep0ogMSky^7Xz!@=bJKOfM?Z?ZX{=yGs zXVuBJvVMG@#E^C{JhD6*YbgGej|bK~e&LS`Z?tdNzt8d5_~T`xc?qbeH8SzeD%-&4 zE@{Uhys(dxjamuEp~&cNsm3$BnBJ-2MjefOL#S87?GykobZS zt3-A3YCg_KT}C&hbdy5|d5Iob=q4UF#5>`F#~D^Md@uS3nekW6#}TPHILM+?a=}XAY{HS1zqkZe_ zrB2og4cfY~Vb7HjCNn6$P(1mCz%{ z7rpGwEv}MI=9C9g-<@*=-&+tdeJ$%9ehSw3r zL4y@Q4!IlwqQ&!*J6(dnKws49l9H{pMwcSM>Xf(Az@|U&Pl~_6!qMjPbC$+0UToW~ zTic7zKmYvK^}d$MT|Th?qFj%a>zvaNjb^C(>Yvg(s2Wa$7pbBl)*-mKUN5Uh`c)P! zY&w>D>?XU-N*Q|GoMMpgV~$~9AghjR5~nu9iF)hITu!Q>rxZ3d9Fr#DfQ<%~7~WaV zbLd**PVyk>^~F?V2Ew3{ck)nEn2kAl4oa`#%=&8I{0#>eZg_p;io$Q|XVj@Nst>!? zB|fQa?BHorhEA+qW$Dlt7LKiunikg$xopVZ$IUyk1HOtf=(FQfxAl$c;!0)Od=@J3 z^DWT@#{`1Su-p9tMfpy!co&Eld3FvYn5Li=S$d{j8rQMo*ZfEb>-{xfvoMr(R5xu} z!G7TNPEmFO`s+zYv4&Vv#iYIh-N=LGV=R3}M@+q9GTPAT(W?0t%tWPzMUclqjYS&) z%Ik_SR!j;o!cRvP-1hc$B#9r1?2s{c{()t4Ui(Js(`!WQL?nK_v~OD3S(7Jiosw$!ngn*n-S@d=L!q4z?_&}!Gf`s$dmU>LKq2Dy9qcGCU#qy5>-Zpfc6YCbv41L`?1;e9TsQ4C!(p^$eupnsP z;6l=I{>HDY1s@>Imo~ZwNrPPAq*~2+0$@@B9bC}0%WP4cq2L_t*AnUAf)YjVeCq1yTM?xi#``X@G8933=psWeftGQqTE&R= z2ob@31v7W8UA^~(f~^Y{ZM&5Bbb95gwIkZLZrfVQ8JE>y#>mN&N4`+6Ucu1G6M37~ ztrM$MVf~vlX^9hWQhHbP6Nm&C9IpClTo93TeIl(;wDpY&wTLt!DMW8@y7XOWm3+YV{)wSST%$2a3Lgu_{xj(o$s4$~=mK0aAOn%;doqfnYyF$etyyPH$zVv7K z!ky^RSOqoN%`H1rdyAGhDRPviitL8^#oE$@GNQNq5&9nuSxVL$z*xLu#_mWKUy!Pn zv5S~IdYWhXa?jNKEGS{+vZ!8FjkAmQLQPBcy#v2(>iCl!TG-V0Dt6|imb$R4QqXoF zI+{ARPz3^VBSlXF8uLaT^|WZjS2e1HRh;6iBt{<{!zinE{*zw>$f#v#4Ga=o{@ z)UxFV{_W5~RmYE-zyfdf>B5G&Qz6!dO-ZkHCfnA%Rr7A0)^?J&_81kl^^@Spm>e71 zzO2vT!5b$%HN9$DyHRaw?HL-i>q6wC^A+l6G-;TggyQeB_1d&)5LvfPo4Ulw8z6ez z3vdoaRhNW(Fuz)_D| z`0PUAq{BoB8D6VUi1U<8N`;f1Bi_3BEBmeZ}Di>S@F^?b+zXDyyJ zl?688>l@m75C8CLzafL3`ASN5U$eTty7c^(%*6dGMsKKHvDSO}Lx!_5&*b(RM3hMT zX@UI&S-NWIE{IRxfZ)0Mg?<9!ICK}H`oBwo(5j!tyn;mU{CO+})lbi|cX&UkvHLCb zQ@kLhyZe^>PV4Cl<6)@H^4NOxUR+{5I(j^LJ=(BRO7u2_`vF}Fd=Lo<)}T4n+1C zR{ydRo2dA(YIlB~dO(3?o|UKs6HKFffv;ri`E%@5-qIFRvIa1b+^0bS;N40i!Mh!( zp2T!NVtUBf4)pL#@w>Y5rhES$&uWCD$>fR@21Yozn}mW+O5r4(p5}e;GR5vW8kkVAudK42F-f zqUER>t<|6+E(e*9Tiq&tA}rM?ZTNarYSH1SQ(mvgCAdb4I!5J3LEBpvwI%9sl#B#0 zJb5f6DlRIOuC|c(1W&<5L2oXInn9nw8O6q$y3*Op2z?M|Wbo|K37Y!6cx*zgGmc$G z|1YwXFrq`V4m-2U5192}-moA3cvEfBY0CMnPc>=QdWW)P*XFkyN6*XKyF+51ev+L! zOu51`GjlRLH9Djf*g^f>J$YGVn_4}XC0LV=StjZiR6qyoCyKL0sLI%fPz@B%lZ9_j z0KwruD&eWl^IHJh-^Cop^T_W+#_0^Ja{fG}LNS#Mjfy0WG(kyY2W)n+D$yyMR4QNv z26Eb+nW0SC(@d6rwb|P&9XTWVwf5L=ur z)s|yZ@(AJlL~&KZXXXA7ZVdQ-F#Z%)IdUseIe|zCsNP@dw=X!KLo&yYW6>&pvEDGIH+Rk(u@DXSz;U zq>{DDYy3Cc1XvN#mKmZ~x3;hlePJDKOvy zube2tJwmBvQT}=4TZp8UixF=*ksTxRBMTyDL}KM=CK`>EBa$sv4y%i7?-Zed6Aq^T%5CMiZ6a)~ z082Nq7gWB8$+1sRdsUm@cCk2x7|>ZPl5PM6nBwJlF_XXy%Gc zYHci#DFzC9ES4IBjx6*Xfn2ELnwIBU+lX5 zyK;N{_-g}M1fa1B(5PiQ31~PhqXaY*e7oUS=#95ZU9pDtF#>Y`Hw?Pm+k)E+Glxv_ZhO;&E} zYmws`rQ6qUXjYGj<>n8!)vOdfv3rNq#(o)ej#xS1_ZC(jV>$N!w{n!Yi;7-sK)|kO zS-_S6m`kNJ>xJM56Dcewpkn~dfSD0MuO1EnI*JG}5H&b1I5jvYxMOgBa6#~lV7vm( zqfsHbI}GTLow!{1=8)hM*M-ru7&@B69XIq)A^NJ#2O9{OAbyk>Hk{?Lm-* z<&4}?U?I4rpu`-z#w`O3aEGBL&WdAkKp7)~t*ojWftgaDrW{<30Cp*nKK&hTFwsV^r_3gEg z!PiV$BE4okfez;H1BuszqYBkngo;TY3?Xe{m^spo{QGE@LiRR-=)tp0pHbrGZnviTzr?)_+7#mrGJ;U z{*(AFa=ERChfu6v7%H@`C^2r62Ds5Knn(+!lU90nlJS?lCIVIL5f(Qq{n?s=xjW^d zi3J&|p@eBiUrR=xR}%{j5>yMnoT01*T*zq2spVx-y>WCl%Iy<9ODF{Fskry_NVGbm z_5>oh|d@edC@rF-}V7_ zdxABkBfoa=1ODbwY^C}ez_2(`QTpd8+UOy!m5jf-4X~%fc{;YjtD_=;oD#DC;zzmvY)LISFbl#wUnds%^ zbAWK90hVx38RiTUIZH~s4oh!lqz$G76!h>+>%0T}9G}TA9Fz~StT#@vj6)LNeq`n9 zL$1}UT++Mexa*BqS)2FkoMXAKzRr)HwSUMTed@ea!laM2HOtodShEB6erwfFv-jbZ zG}UnHSS@IQ0#cOHX;?G7XmP$vv1aPO1NX!e7A~A>)_GH&{pEB1P3K;x`GvjmUY365 zU6y`;@$HA#zO?ti`gI4SizoTj0T)lW9Vc14j}or1uCJZshdxn0e{}!OjR%}?dM->2 zr?U7lPqeN;>&O1&7I7k7#Ttg*a85+Dilpgra%ewhxk2CC1QuXx+2IYbzZ)biC3zzz)2vJIlWonKKlt95=&Ah#B|yF#P{rFBUl`?Yi3 z^{u<3IxOs~yZ8D(zj%yfJHr;Y&P$z;+w9oa$F~pI*?06PT5Aa3p(Mbjh(#@1xFEwYU+Q5@ljA zW0pLD@$-AHZ{b(oJ@sDQwL6y{c=?qfly-EKzf|^*BU^r(Ippu28>T%sW(}ys$*(JU zI5p}JIrx9-5cxzah+N(V=LeY+LfNC>GIfiziE=#j?kFQ>LjfO=dP{y0(J&I38XvA& zg+i@Uky1C-#r+aSVOx=Va)UXG&yT&y9pXdwva|n3lu_-4&E4j z*V^^~7GxZv7NeW{Ykp7)b~jfN97oHlP6W%dAKfE9uRu?Qggk&9J-g~S;-vPChH(X-gOdKSx(e{h*=$=MvN^W|d z7oESL-D|lCKLzG0A(dIgAvD7QKM3H! zeTOu2{<4I8R`R^qo&z@^PsU3IPezc(Y8MhzXF4W#j0t&lenA9R#=sTAw16IR$_@EE zgyDudZUPvdG31N5;M#etT-fSu&Sk|GC`RB&ux5v)RHCwW+q3S*yijk?#X3pvQNVzL z2aoP--g

sJA&+D8dK4`xr=pw9W$KO+iD_fBfmu%^(mJ^fHDJ6zrfRiMo z^MV0Be*P12|geo{S6*55CaCbiO!+n(VjV>*-=puzJAM5?7?&M zqbF@Y^PgEiF5B{8k+g*W?OJ{)r&W(7Q#uTKy345itYw?Ku`;)o{k~ukFWs~G55D7g z&Yjo@cCOL!X=~PxoH8FgvvdhY#=gg@Sb|U!K=S};`9d7iKqlj;Oyv9t4+XG7w4D;P zniz4MLJ{#xN%Em3A0JoNx)aW>0B2{Da;|}Dv zKqhE`QN8_j3JtBQU4L+1=M`Da)0*&;-(6etUH^|(Z~5flI`|-JHmTpb-aBtK9Ma8Q zR{Hk(_1QD-$8Ddv?|dVokxcAo0?vW*fzWl&o6VD_cb2O~*@(PnvpMH&A@j5!KLF6w|1H>Ic$;cgHei%A* zlaP=gNJWDH2-fhhBuG=c82G5<|8oA7p^^ML>)dBe@lX8F8wIK9qxOVJVGsG0h0oh8 z?r%o$Yy59JOKis*wErHkse|=bKs>K3y6;9AJncgJ(-*BLBA8g6Y!{Kch!9bPVovab zYZeEVK-!bV(_BQ+>^r+XRBGC)yTlqO{L#7J`OTSkHh+vpSNycpv-O@}_WM&UT;H?Q z(lT317XEa$Qy*TRi}}H{&TIkavJ|P)dN&|arvT124bH*})tBs5-n}OPpsQAyXhRJU zcP3N>kPXoSq`gov0JTR+ND-XcN{W0o&F`_*`Tc_x{gG~XEo#_~_kU-d2lW`5oiVJ( z5GlpYs*aJI4=t0Ivf6IH1uu1B$2)HzYN^A!E7@X#pcB;_31XJJD}i!fH7;OUx+w@G zL@b4|OqdwK5NQ+4;jvLp*qKTAXgfm+FPe}-ABz@jg7%nX*y3;J z4)+N|gBm3Fs*{phHh*f?jDD-n+-02y_8gT~d*suD&`z0EoiL!J!XNxzqwk|5r!N}G zYW+QbBV8xm6Cyl277|E5EWs?WFVl&DIhbUAgs(>p zR%;0pCn7wQm25TU{=MyQR^`RNxBtN}v(6(2^cy+4U;bz*WeZ=(FO>#ueYA+xWEQ{q z8`f``_woj;uOIJ=zOh;y`&l(sTOYJAy>q+Bq|(4823%;A8m)EsM&wD4;{7{*2|Cetqo0Fn z6-EU*IFmN>Uzw2?n=l~!F&z5+?2aMPESVqdzxp&VXHfTo+OqqG>4dgf(Xs0O0U+AOUwf$9>ZF%S8BQ-0b0 zE%S5tciwHhzfR^;mp7~}&$>pkl-m_v;fwfp?%D4qRrpV8rrhUr*?rho2B<#{`-*|| zpNsrOO4A}4L33w`tpELVo=E-RVuH^9CnjJ>c_$ZKym5;Wo-~SoG65u22sUTW`09+8 z2NX?B>e!_HoN4`5zx564yk|_!D&sc=NvQ5*iC!KM8$M&;wwcT?`i#74OaGr5BR#8>-g`jD%Pu7w`zKNzouEQS{t`3bX=~Sm6{Hj8wg-fC10?mk*U_z zRF$N27L)pC6P?M_Abm81omdDa+Mx0%EL$qNjV`!Hh74i3x23|S=tS+7!pp1WQGL6* zDd?;8b*QK0Jqdv&INwl<9_E4j6MtX8U6-b6h}IenRZ#}32YlVM$}sIsF!GG0QwX#e z%J0>1eGduIBxFS-7wiyCwPouG-pWrZTe97cpYFz2N|h9c`;nc!Tax}>DEs1b&fiB| znIpve1KUG<133(GiGXJmdKk#jqCOq|Q9bT1P)N5~y*y5`2;!Gg)llsbdVm`W(#Zrh zSgurGg`l|BH-+-1_0Ci%vKqeP+sXX5Zw6AT#n5)$yQ9y<+CfLh9{cjDbdTS9s!7AA z2>WZ6exNK@-bI`?4){M@wQDnQ5bvlUGSrehr(h~W)ip1*Yy_!UYB_P|!blrK^j@u` zz38+HcQt%4Nk&>{QK(3wcGigMWhcv8N9*Y9bRazqw+u_lND;O|wtRmrqtf#ObGjc~ zyP8pJ>%Fsk94fh2hIP(*>N-0S$`5DcqQkX(e^aTY3iXtHYW#c7D4SbeQB9gHeo1VCANTQECPIpDB>1TCOi{GUKcHmhz6gfd#0RW zY6SfD6`m0diVr^X3_5tAMe#Ew=h!0toSciKB6KU3b5BjdKfZ4rSs9&qJJHoUg8ZnF zlrs1PZuU>7sxEs4@sVx^8D$tWj>ag$k6K3DVXc}AEi;o5>C;W5P5QP6+2_yki6Yqt zoxeZh?O>+w3YK>19l#$AK7XC;-}UT{bVvFN)@vB7RjKqpI3Jp?OLp93ZNYj}>Hl%} zCeTq;S)>0w_ui_=lmr9>lu-l}85CsDFbO!}#NaF{0*(l21Zk&sMnzlg)^6hf2#Rfk zD5P;#5)dca00xYTK}ZNykrb7J5JfR{|G$0iP?dnM-}n8$x7J%PS-b8~r}o`v&u6^a z@_qN4>Q`QsMKB}G&aerySvJ#GhsoJOn}B-{mn&782w1#AebQSqw6f9z6Di>_h(Nzz zRHnwceIXe~@~&KF56ILw&K_U7RDHch^_6G%X8O%_u8Zqo$TKCR( zHBZ}s-!Av`u-<=v-n`J6$(r4NFL-`P-|*h{3l?oE`1@|vO!}?ZUMMst zV*ve9bk3;^W<~!>x1Bett+NZ^bkkBj9(+jY*=!vL$GF+=lv!ZBa~Oqdo?4tu=*E2V!(E4{#BBU@h|0^CPWz)BixJ`d3p*3-~nQa}?Ai}c8_r4Lv}Q#x#ny2$Y9(g@`?a9{UAgcbMCcKI|x`4k2n5FI=U>z5TibtbNPd=ipY4K<_UhFiEj&wW z;dqz3yca1wCJFtAFDu-YJ=GePKIV5YFUa{%Wra?ciY4c1I*BgFreo8(M+xEoXwi+! zp#FGG{bdcQ*DW=nzJ-UOe%S`p2i=Rl|EOOY-AGX%U~ZL;SbO^qPA;qDD?f!=-tj{5FPOYuu*ZaCLrL zeU~&-9m^}3d6BkWc~8gEK;Co5W;(ozZ=%`m=JQA9bm-pg?1|GKnmI1naYFJ@vvngi zcz$TbuNQVdv)|AbubnvS+WW_=a552E@!Jgb3$5i|$*b+=^b{UXEPA7XM-y`WP9a^Z zdM~U$j?u#7q>8M?()lQPyq+l8SRdrA>2w-orUs>X)r-vGc>BuiY1wyWvmo)q>@V3c zcX*?w*~es`n4Oc&9L?kCqx3t{q?0G@4KI>*_eu4vGT_rq21@iMaQ$SO5AS^GDqBIb z#;F09WS5OoC_oR}>LZ&>{Ks=8%06(zeSf6OyNOzmu*?$&rdW5=u<Kin~k7gQVfcqFI07Y4h)3J{`?>sb0*UrRVvj z1FHRa>r&m6(k|7DiRMT7r32i4EO)6M7~oz=9)2j*X?_o?CHe{Ou(9yOL#g&-6q%tN z$q>x!HVF3?IzOc_2l+>uHxIoMR?MkjcNBKWe4m*-6-Z3y+1mTKS$VE;Ys&i^l9lTi z8j@)4M(5m6kUs?Rw!J3-H-i`N<6>T9tV+=rVD{Q8Ufh?M;9_2M#-Y*K*BckJ$jimN zSp6X-uK=^}uid!c%)^{vELIj{0iG@dRBL&WJxs?3T!Cjh+IT6yxi=R}Y+n!l`5sM6 z+4pGmyn^r1@{evUBT_4l?xVK~$zLcNzg%!M-8D1htU&e<;bUgKj@*{@I&xRA0S9et zJx$4oF0c^574e=Wq3(=Wh8mSA>7 zevcOVwa`8Kth&0#j2o|c_7!4-Pp?{4{`NREYH=yg~qdo(d_1)9k}`@an;rA zg~uPN*}hGW!+*Bc{^7`6lOgfB`Vm}!#t3Yt-;!Vj?vy?0SQFI{H&)}*2OH= z=we=EqgQVgib!@@{VqY z{&3dG^V4Wtz15?Tp% z%CqL-ee-(z86(@xYJIEJbNX@_9viGP0}q4R9b^V+PsCDp*F|`ul)sVwZ%sFnz~1W? zxwWTnN_srqzNy)&JXL!gXT{3=c8uFrmHrTEt4gLQa&=p)em~80tiQLeGZ=N94{Gbb z6kc@KeFywj-@n1HGZctlb}3kB>`qY-)p&)gqgSC?L%2>ZUa zv199M(#vzdbMhf6uel;N=5l4Bu@uVDY?`>eJc+x!5X%x?z~yXhSuB3`sqBgcGxkJ@ zLBcJtFJjoRnUQ}XJ5lGa;KN-f*R-yV77Ff2oL8y)$AR*0qky1u;ftm1S8`m z#+z=`XZ*=;XSqE|(vETasJy-{CQ15sud!(F;bO@}_CbG7pjY*JrnDb%@rfVt>^|t3 z^836R(zhr*&j~R5?IM>_@hC3lG>;Ns_C2wSSw@6hnqOAuGa}@9hye4^r7JqP9>T>f zy~KiBdWmE75;KCN`M{J5?MGa!;-_4!uQE$8@KXWSHl=#h6aEWU*H5`v>-nj)K0Q4` zMO*I10mOlQOJ~~rxL@>w;z0sKly|;MSrU7PMR`}}Ykaf_}SdYyO(Vp}sLysOB zOw6}e=-P0mBLMe(`hr%5pZ_o=%=%8(<|?mryHsrg`}0e?xFv^naW7UkkuTwOSp(*K z3KyfatXzzXjo)Zl)i2}X5%2Bdspkg+8vR)8ViZ3v81ds>0zi|30@q z=1O(CU;Dtr`o4!YarkdK4omBR?9aS8HFBdZQaC=uizj29MWJ&t?u8DwJMfs@%)V0E?3S!`E@R%*s_kv7 z+p?wcjJEg4tX4;6wa#EdtDe=GKTOWvlzUlxta`I;0cG<$GV9eH&;LJyRo+2h^Ue{o zoBCEXj~cdc?qpnwMc4Lc;SSWn&G$yp!tLPmN7q=U-+BG~hm@YJZcdf|!n*W92ES~G<^e(bMb zR@x;)Qryv6pT4pL*HoZp)u_}1$hQ$lJyKx+I5bi60OQ#-Yej&jDO zo}$~t@)Un0@djFoi-OXOBz6es*X=|p>$MXdT4&~mL0K6|%sLEae_t-WpJzHIWn9_b* zr+#U4?vkLs0cO9a$}KCD3LWU&4VqF`+WEP*E}`p436a1xQp54=OFv4}|BXlgLn(@M z>=F8X-`cSBY?UT2J--}-$U%`g@jPo*#irM31l&bOb;Lw1pBO@H`p zY3npH)5rOQ6iJ>mU3v~B7k)>${zgY8F@mfxbTUmA&y4q5DZs$M~e#g^^Cb(r6T^l?(DCV<{*yT%y(MNxD(=ClW zywZ!+#FRV&9rI`X3{9H$bcu^nTKB1}Og`gz`9Ep;DqQ*|26YJ{ofpTPr-&4P(-Faq zSWBEvjQtbq(t5MsrOb5EJ};`}I%y+q4WpW~n9_68FfMXNrD`0M_IsQM(6_u%=ROjt z=y7f@Er=VS`#sT1{lu`bIK_nkgCE(CK^jl#+Lmh$FEDndo~PSLqME)BADax@59sHW zc|2>G%=6?wUt`&!c{aOMz_TEC@_Ts4CPUVIjf1k9?`@LL`Z(;rLbc$K-j7w*?0S8( zP@-GNVs~j6u)8PgF7+gLx28hkvt%iErRt*lTBR<2>tSr&0ht@m^C+$9crH0Fz=A)~ zQJxWG=&hI4{Xct_p4u;$dK{}L0P|FYyf?8t?c+r>1L= z(G0rWJVvA_ITE?mcx~}%yTiUUS%KIARq*J(>ENMKkaA}iX`CMtE~r8M-Ncs)a-{yKMvKUb;!SNm)=e!}>R zh&XkvURdAj#M9lWN@t(c^oRG&pX*v(HFoS(W);$r{*X;tPruf6USO1^^i*eDQW4L3 z8)TH|>CplA14()M;Fi@nDcPswq+KOrX6)2%t)Ew{pEp~bl6qdCr@psvON(cAOUpQK zkOk@PDWT5Kg5A<$0a6-m9THF%l=XT!-C1p6=?ghdThSZN(PBl(YoPCn@F?yxDvuVw zQTGQjSSNWEeCiV(#(kcm@6T}R-219RmuFnky)WgR9*pjc51(!@deTzgPY%}j?+4Fx zeSgRlxBSy`-{Dmpp~m|D`(Anar?@}SskJh~+j;KCnqO}w4>(n{;dMr=)k*p|+Lp3V zPF8kwaoe_aU}I(-D}<;WT$gdY5`En*xMqF|-&0GzAIkT1FXT*{c}Y9!^~`Ve|MN(HQDj}UM=kpG#B46l1R(z0oQgY zT=esziS!U7J9u8;m99>e4!TSIyy$Y)ZTHZn*LbC|Bp-J$N7wgXH}MvYyTiRad4`QQ zKB)4`Y7ZlH_^tlQP~9oKpR$bEqt9zz%W(45mF zevH_JWyBB8KM1w==3?Iolq@&~&hOODjfY*P7ac$k1g=xvcr0xTj z&Ro!M-23X1#WD09-f>=+wL!yhxfKar{=-=0zC@^OTtoba(X)A0fozL^?X{mEPiT~@WmY| z6&-+Y#DX`BG^y$quGHV3x2$n!9S})L=`AT82xR5^sS)&BUZhKJa7*h*?rEd$2N2wT zPA|55h1O};Bl$hW)3`rM_Y|+xSZ1r~D(ETJ6iR=l&LW9bdc=*LOnVqN=eN+SR$aLU z10*6>|6NY`W(Rq%Wd2~2O_Etdn>Jxg=8UE-1rPizFA8X3X6o^{= zyLIg@Z$psH;#xDE6oJaJZ2Ks-j$<`z*KYFuK!!G~iMJ%+q{rF_F z8a}K?-+$0$78&2AWT5?u$iVNn{7+t4OH@;YM5&3EyuNn5G!_~J*;^Sq@lEx7&C4ROylW&8}QrGLw}+J+cst9>bq4aWez@x^Rv zb{eIK8|ps2woGHuaf3`v?i*E_zhdR-V}_%Qv=xehl{R);Vuy?wj-oN=)h*zgKQiCb zTBD9eB4OEm0t;+wePLe3_*a;yCD+$Gak~X0)!leewonu@Ptg=z&3v^_Lwl&fi&~Nk zB|8{;ww?(fhTojP*)Co#JSa z19|Ow)_wd*XQzJd{SQW18bN=rK}`u>5IOL z#*H~*DIQVhFX+NV`%n70qmyGA<&y{aDe7!EAqHMK<*!s(Ho2j9Hsg3k$!%I zs4W_P#6%tIw(9yWGOtd_QMY?U>i+Do;~=e*jwNxowA!AMP0&j6`(_$EOVc(-KMOoa z_J3%FlUM6GI(GxJ_7SXPflGRol=dB?xlhiiS4ASxjMH=kzEAT+k8j%-V&ATeTuYFd zL#`$K&#i?R?d)bQVj0?nmTNj+RvFNFI=OLE`vNGKuPM^kIzDkNba!S&qbDozkjP4Y zZT8c()Kby4L@IKh**;p=oIUnGHCME*;W_8%@;OBD8eLv=O|VO8ZP9wBlWWoLJW0<- z9%9kjN<+(`#my>m2Lz+`!E6KHMm1(l2bguO-BrUb4q3Ab4xvNmmF|2Jmbry2i zqA87frSJ9i$m5vfme%`a>a0|A%*{%3*)b%T8x~}xUNrXB6AH&BqWz5f8G4Q>@zVG1 zZk8)G2cM>OxjfzbrSOSg3azpd(Qr%4d@T3rud4nj&lW_ER>3&DWDdjiyis|+^tutH ze9x9PZ^qj(K)i0Kr{+bOOARlt5}UdZ57|k};TM;htA!eDCj3-JHvTRGi@F=V?AN_t zF?Wye$#TM%hqm&4lKb6_KeMx`tE=~FIf-;T>X>_$Tob#Vs297Ihgy2E>xHz~m0>Km zyBe(n5AYt`z#6FCvQRhG%a0mCE6vwPclG3hl*TVeY5P9sfL}UTKY5&&JJi96J2LL` zahn5;k5wa|b2>wF^C*Ajn_&Mse6EK*Y!2+O#V;M;KB06)Z#Uu`1pAyc?1PNeDeM8| znWbz8s3X+HU~is=eNfv&vG*ul(aS9@w9m;{8_+)3_#lNnpuK4+JC^9)dZGP{@Sg+P z2e&>f_7l^we-U6m_4jCRmBQXU{7iuT)Wg%>$HOis#_76Ju_!_Lw=$ML*4#S-K3|IW68g`5S_I(Ra=z<%M#pZwD4*nQfygeB6t64n_(LkXun#T_kiB`j?q zSHi4){#^CpOlq?{Dzp0Z(VNxpywbDNj@a2=(ta7S>Jz3`y)385P#z40L$!n z%AW+;us>#}+uY%YTrAMC%)Zz8#n-kVXZ7b(H_R`VFuz&v_QFgge@w;2XnPnL&FNAn zEeW>C_1CI`C3L#>8=6AA(qk#h4k&D1%Fa33vIXOouwMx{u*ksQS+<}(LqK7M60ZYDIE+VyzPxO-bv}f z;Te?vvsd~lXQz)_mzI`GP&)aL+Ni&|BZk#e^DOFRaPl(v_p z;<2`Wq1|s+2=05dZfe|U{^u#}32=7~Bqq6qqa&tZNAu4LVp@Q^ed&s0UHKYdAC`uF zPgPyF8mQt9s|ICwQ}hbE|E4YbWGp-1e$K_A@Bd9(_KieLcQxr?&*Bg0@|SU+bq|!k%)5WIzVEIZ z5l=vQq_Zs4EaeG;VG+OY&0UKkTmg%ePWJ>s%hGQ#fjc}+&treylYDt<9=l!uFJ8u< z7YJ@f9%A5r!7F`EYCUO?o%m~m8sZ+s+hBq@^18I(_BFOXZuI2tVE>>)`bAvKvgevh z^|H_iHQ1MBfZ3-*W7dA%rFxnD&y-&eaQi(pF78nQZrO7zu$+J+zTb7JrngIT1U(rO ztw)D=UB8AmChrPQ(-91+MIS;ZYJEWNq0ur=MsR37`Mb!S7ThX5w%pAa;@vM{9;dWs zPq0tadO|x?<5}n|@mg?=b+n_rmV8>q74$zes@I<@dIA<*`p%T~1U|4{P9P){A5 zb=#Z!cj)^UQofTe|BG8*-KOtPqhR-J9Q7 zj|4Gd$@Zk~XlKx|D&s-g#u*PLp?_GTJJ2$>1k@K%Q(f&}noI_SV#IQkET2&9|d(Emw`7Os+}R)a`xxX?0CB zy6Wb;@0xB8x&O9r9{VP_-yTx1YE|9J%azo%{dEX-$al)rswKl82K$whzp zi}h{Y!@JF=>T=De3d~7tSn>wXUn-+(tc$DveDN^#4yj87E zo~~9GsE?Ary@x?NZ_{47jQY1U?mtZZ>uIdd3{9DK$ikUSmou4tR`tsqnTaC5%>2ET zDs4DE^#*oZ&z*N)Zp`MT?4r!(y}W^}g?<@WifwG6toJb0uR=E;;w|y(*Q={nu59q8 zczayI!i5E2doPPu&dbR1E<907TA`0qat++R9l8`Q`yH%$ctQpHb=?6bezPRF|yJ;1O$;*F6xbU>mDdsFVRSEC)NlHNl})c8Itz} zvqu`+ejS^kt))BmqB`@sH|V-=UD!^(X=dclO72oqUVlYRPFBoq*^>Fwi&_>~*Vhd; zKl|-DDDy`BjpoJ_msRz+qgTTEe5Sn7w$s^6j3e4-0$Eb=r zNo!=ZLdM)1Q0&{grr5vu9f~s#OR;-5MX|mXie)dk&PPHqpPcuz9-Vw3`O?yVBo`c* zFriXin>Sxw_2ad?@4UYno-9;f7N|FqCF+FaSc$|F$@9lE{uY+GXhg^gsL#<6^#mO` zyE&I9he{+gPY-=y>$dv(damzw-^|rgzQ2L%Yuq<+>FW!)K2u+}*X7Cb_^u zPH@}9eW2zF8U1y8g1|3xi@fpy?afNHd4hip_)iX1Q(pSW1b-Y&_R0tSGxPyh~OXKl|Md(UzZp9 zS<^53&QqSe*R0X_FZTGuxQ>rs=XpXuqv_&;(PMezT!qFz*u#Hk3V+b{3r5p5{l{zk zmmNxf(E1BT>yuYP|7<;*cheMUOT1Bci?$Exqmx+y-N@DD{&M?O-HSDeHtiU#ooBfB zLydGBM#@KY`5s>Rfx3Ku+O#q%DA$sqW8y9cjO3 z?Z{-kBL>gY^M3tV&dWo6_46))4%d1l&lb9$)Az*&|I-|~N$C1Lx_5@Q1+t&5Ho)sl zgIT1^$jQGtONc@y)06L~?~}wcN_j(_!#+u`r0l4VQ>b~v zh@jBw^%PP!Js+-{%u$y6p%RbxPloT;J%!9&(f4<_b))^5wr`NMD>aWV6<+A_PM0Sd%KcE0zTX-=Z|U+^c=w0t`$NGabLI8!2O7_2egE|2lh${-{8ip_ zr^x+e3u53yp>MGGei(O#`a>P_kMUV|Vi%u&s0@-_uol=>)uzjQeN#0?o-}r$!eD8LLIrkjmEp+KThL+6g*4x z{mowa=jFab1h=2ocxt@zz2&*?dVJPEg}uGfuoB(}D%bAZey(MmCfE2V>=G-nTg$bq z!r)rU|4CnyyKsF2a(ho-GpEV>Ue?Hk6@3csb?6;^z1_do>(0XJ2YszqkkPLbmK9{) z?}zE~rTY39o+tYogk^sN=%;7FzENLq*4O*E&ehjj^z|HK$6;2l|IYdqsqG;v@%0M+ z_w}ll-JYG=sZ}jHUdcKYe}<-BsikZ1ou;?}N1WfG$YAZE^@OIpT>nlu`+$BboYUSR zlzOU8u+!G=7)LROsY0{pc^dk1#3)aWzrkRxXXNBwsw<}e&)8leOslTsjp~mj_ z^|U}}yGO6fOnOgyCiOI(vB=Z>v2jWtBJ*^eIh@R&)lVnSI*bnJ_0X(U4a>T0zHg`# z^>lzt>eOU2yO}-T=+1tvNA@^gx3AcWN}5#1$)?Ogt6XR|F0Gb!$LS8Cc70a8*{+}+ zZSG<1*v4a{d_37s&0N*7U7vR5%>0FN!)ZtC&Ejm;jCS`(**7V%@~G5irM&J+?{CWf z)r~f>i}lJQQ!CinzjlzuPsQv`NASK-n{V4=U;vL?@vB;(%H%7zm-IOvaY@K&&MD8_|^CQvwbpC?eDVU zz7q%i=?-hdZMXF7{QSR`z!Tmk9hzm!o5pU?vuCCL!J;|2&{;Fg(45gpt8k`?)d=?8 zMJu_@s(wvIHevUZ2bw(1q27C(Xp+*6#vASGy`GD`X#LkX$^NtbLbH-@)+T4`HzM)s zrk{8V_7r((6YHq^sIPt-2bF!;MUmY)jP}OG`I+8ZUs%8?^y#LYFS&r&Fr6KlmNb2N z?Rl#dXN8!w6?ggVE!kQdeurI7o85Wo)&M&6j_N#sRSZ^!tXt^LD$s&OydvjQ_29mR zdsN5fw3(+}yQ-PG{12bl>e&~zbW65V(VnWq&J$LsMvwpa#kebz`!0L^nOD%thMvjj zoXiZjwb^aaq;)2}qSi@z4yLvxy&_s{i5Xhs4_zPWw{x_W>$Ur0<&>Qqlnrd$AJgcI z)s<^2+N-VP1}70(fyDJXXd+d)*+uTV@0^p2%Dh!as@W#vSXh;Q8=CkZQ?hNz?AVWk z`vb@>(^jaGU0$cNOWL5RwT6@*rMl|!4e!rZdsFxI3c{~7{+o>5a^K~f_Ir4A+M1)T zlDl=u8|ux>dPP0r&CHrzZ)Vmjj8ri*i+uCOdNVIxVK)xh{cQ*{Q`2W&ydtle2fw$A z1+~Q^Lj5a>QGCjeOl?ikIBJVuI=wwXt&Ktq8`@Hie%nW$gJ8#Nw z|KgoDGkOypTzWE%6Icgp8Om-1U01OmlzXm>HtKTk+%z)POz)i4`oVoW?L35D-RqU1 zzd^m~ooDdIo7LWVBcrKGdgo1ymW(>M&uU6G)WJJ%W?X5G^3F4j3Fb@QId1?o_j~8f zjmDNSaz^g$GjF{9hFP6X?t9Wnoz9#w$frcLWKPQJRe)3~X(PMvw% z)G58k&A4{PtQnn7pEhao&EuwCKYQAwnHNu;dFzccrgu8I_euQ*o+fApx}|!#rQBcK zcx$Igoo3CPG-c{7lV;xBX~uQlqk6kfbU%Afo^i{_8)n{k>#Q3mP45JjsWWHYI%E2| zH%^{9{nn{dI?bLwW$MgMvu>E$X+-X%$sD|IPVAH-qVEl}X62sRr_Y=@b9ztGrFzep zd3~R0E{G>+qfU)`#&rW&_Wa;9+`pHnC~j$dC3%mO;0ansPQ$;Qpx zp9QxU{`ZI2X0@2}4JaZ<;Dd4_^ z<4m5~3H;abnO>{j^{(oFGnw+Yz@Zy7cWwpajbIWA-Fit)XYo9t`dq%7%(JFz4osoU zY|iEBGdZ6H4&mSkcr6@ouj>EiM7}>9C;Dos%;J})V&i>`Ir_ghcv84}L&Hos+6RuN z({U?z&P8q`nQ3-Db5lk$6Gyn)w;nfBa;;@kw~hx=2k13g04EfekKJku>?_Tc<7 z`oM;AcQm%I(5Tz_yia?O2iQqXF*kiKpO5mdjK_?>^O?tNafSa75AvF^9GhIpzcSuu zcWp&KW&xjT_*blH{+Q3t_*blP{WqUQ#s)q&8DH_anSaHc@*nst<6jxO_*d-f@FSm9 z%u`lI9si0o9?DS4Vo!dhj$q!3Qmt7hq0}+FKSildstcc8SvO><6PP8aR6piFDCUR^ z;PW)a#1Az>jpFl6brzpv7-dvytQyPb1ZKo5HBnJ_b*;LV&nc{fQtAeE1D`jkX?#vs z)A`I*xqPzoDNp%8tuahhtav-S+O2l;S)*$BtW~vq{)UfJCI$GkOlUSU%nUvonN9fQ zP49fRHhFJ}+0LXE?6%*7&z>ffo4w3leD*c_@p-j5na?}Saz1x4PfA&Tw&1z-s+G^@ zD(fRY3#_et{$RmFYlju(^GBwzK7X^}b|@p%44#Xg7{nNZ7N0_6UgQ6t{4eEy8UL>vuM5vrH`Sf*L+H&b z=)!XN<3_i}XzaNR{he{{1f$*L+hdi*GX_ID6=Uw(hT^(QLDLiX(?yb2- z=FL-QPG@u}6(5C)u$fH>xpxH6m}hlmBwQK4a^0BHt>}e0iXQH^U_F|)KzlS=-qO*7 z-q#b@Z~jEAr#BWQ{^(@J~#C+78XFg^=ZvNSP!hF(v%KVG@SMzD}8S`)E-_2*uJo7p8ZS!68ee*+ewOMGc zHP@M+nxC8N%`eRj<|eb)++voP-Edy1xt*~{3)!1rkWm?%*3#+Bo+G=C9vyQPkSe>jcR#)pdtGji))zj)_&9xr3 z9{muHj^{ka=J!d^{ymBP|>pkmz>jUdU>mzG5x?E_jvDR9ft*@s+U$wUb(tH6?H%p%+U2)M`9p3G9dY7ihmJhq_xW3)C=JY*2N1mVal&0pk6I%2vMtbRF@83F|2S% zo1BWAzB%)#m;B$R&vpIW`#Eg}ZxvqTv>9>JX;V(iA2B5-bHuqL#*LT)oa_GQ7$e3B zkKF%ZG4QAT=X4ml#r=0FcDd)595c9bO8?d>?W;xc?%RoUIK3 z{CTM-X?b0P%tUH(--SGWTkgZKt@=MUjQ`<34cR)pRHUi@r%=zEsA>AY+;jikyHiG- zo0IAOd-rdG(k(*6h~Cr>4H~*-L`zK*9CmeQSZoNg9Cq7~tx{9!S3Y#hkTzbK7CBGJ zuh0??%do{c9frkn`VP&fzUb6&Bn+P_;LK0*FEqPS0z;nW6Pytl=X8+oe7X7@6rSmS zBrjYLoHy)4{sf|e`1vOI`2o!J7{q@oy!$X>!)W6SqQcq6IrLDS%YP^10{%PGt2e>uLhg1k z(c)76yVK`9k*IMs|0f#r`0qu&^LJv$eE$0o58fh%yh9u~g-quIp70U>%%mmPVa6-} zImRda4;|Vw5~If1hyN-b-w`)VNPwNltUWdXV+L52=UMqsGJPTY60AsjcdJX6$dHclFO|hbl9kP*rLV zD*+tk7*Cs-#M)=bKl&Qan*GQ^^32iZXyZBaO!F+`d5KiUi^QtCjRnN3XN-kJpqGrd z%*Eys<0Esa`KGbPTxEV}d`3K4ZEPYY6&ha=m)07^#HMw|X5!OEV~fNj;|HQrsZmB` z`oSnCI_)ra5~0eC3L;e0*hQ53(TEVKs*EVnYL8J##Hu!aBx=Z)&QzJ!dDaE$ zNNa*MLAAD~S~sertXr%Z>KN;f)*n?zV%%e@6R~Z+>Sn!Uy`%szKIL zYpEJ6kxiW{kxk_g+1^t_iDw_F(L}R#>MZM1>oaw}^@a7Ny3i`NHmeE5wy)L2#J6wM zCB(Q=b*Z)8s!&&25i6puwRT%oYBF&zp{82UL&2oT2VzCj43IpY)1fqL#9s+12V@dcbGX3qQx6qYCUh?YmT=WPxg}eV={5 z`q+NZeo%d4&$Z{OPwi*z7u09=e0x4yk1w=eQbqP+dojD{y=uRvHrY$Rc?Q7f3BkTdi&q1(%xuqRJ+M9 zi&d5Vt^KWv+1u=GYL8uMSE{(ZoAI-1d#}A$CG1+emOh~-#vtBDQw)?i>(pRiw6o62 z1hRn^z#33)jvw<=RH_afy8wfYmWoxnYBa}dI8Fj?2IeN?Y986skQ= zq1g|(#~E(k3)~0X4?F-o2s{Kl@BF|V%pc4bf%(7!U?K1lu*j)07XvQ?uK-JcWx(se za^MZ%O<;wy+gu6c1OEb6IhE#Hz}w(|2R!d`e2?S%96#Xr5x*$_)&L&^p8%f$@WK2y zPy}oQz5+G_UjyF&yPUq}FB}g7bxsxb-V$qXiLJM^S~TEjCx zc7CwdasN{QIa*&(7W%DXj$46p%I%~~gnLz7$2jfvsN*qu0b=GcYfu^hW{JdR^Gj@>!-;CMX86FBzdcp}GM z9D8%@!?7>NlQ^Etu^-3&98cjmkY@}AP6LJj!+;UMNMJND1~?D602m9P^ENtfUjkeP zTmei3t_CInlbzl6RN#8xM&M@P7GMVO2kLYyFdLYI%{>PV=%f82zk3OIg);x-h|bvs zeE%`!J^}vi6xzkWZl{pe+#qMWaVqnsPGd&P5N8|yV>|w1yK3uH;6t|KL$<3b`Y-nw znZ&b9^Eqc5K4m*TWjj7)yVZ$R>0P+j6*$?*v?ycs2TlQ)$6yTv1_9>*=K~i2k5Tq{ zU_P)09Gk)MHSi7a9rt!S71lmyo1F{H1ZDwqobC2pjs+aoIu-Uh;8RA>|7~R2#lUXb z*zKv!25Pg1nrxyb8>qXmH5X>{9`4)v69}eBk_lo z_`^zkp!mN^{9h$LuM(dpey$QfSBa0S#J^SI-zxEMmFhtwn@lEKJq$boJZfa)pDOWB zmH4Mh{8J_VsS@8(iEpXIw^ZU=D)B9q_?Ak1OC`Rg65mpZZ>hw$RN`AI@hz43mP%~C z5?ijsj>U#6t?!L&s}$G{M7WLtdjSXiQ-BF%0_}l;z+m7sU#o$#8Ud7;53|__H zRSaIm;8hG>#o$#8zQo{548FwROANlm;7bg?#NbN|zQo{548FwROANlm;7bg?#NbN| zzQo{548FwROANlm;7bg?#Nb5?Uc}%<3|_?GMGRiV;6)5x#Nb5?Uc}%<3|_?GMGRiV z;6)5x#Nb5?Uc}%<3|_?GMGRiV;6;r7oAFLL5w9Fcw;|G$6KTqcH04B^aw1K+`3rCm zsB_Bk&gFRJa=dRj9>!36 z#AscFoqa|V;Bu#i_+Mju1pMUG631)QgU&vry-z(1JOVuG>?2;*5HD+pRW-z$8sbO| zF{FkVQe%DZ?6XRN?LZaRHBJpNp@x`HLrkc_ht}XjYw)2p_{SRjV-0Pd?tJzH`T?f^ z4**XCdBDGbb=c@;s5t^S3OE{Q4|D`N10#T&plUR525=T|HZT`>3iu1~R{)&mGr)(y zYM>BU3v2?Yfw=`J0S*Aa0>4p%UcfxySpd#h{Knb?B%rH^&Ic|8#sL$6 zi-Ai4xNBbtTm@VMECk-AZo{d4gxW`_eT3RasC|UmN2q;-+DD9cowaZv8xBO^Km-m% z;6MZpMBqRK4n*KU1P(;tKm-m%;6MZpMBqRK4n*KU1P(;tKm-m%;6MZpMBqRK4n*KU z1P(;tKm-m%;6MZpMBqRK4n*KU1P(;tKm-m%;6MZpMBqRK4n*KU1P(;tKm-m%;6MZp zMBqRK4n*KU1P(;tKm-m%;6MZpMBqS#{PZ~DVUANw6xmG1SwY5GVXQ;rgSl@+QV z$C1E9r`Wv5sUSP7AUmueJFFl(tROqAAUmue+H59^t004`AbYDI+H5A;Y$j8xAmVH$ zTdE-HY_?u-iitd%Ey`MJIscCHa?ba0tmXOu=f4u~ET@SVRqrs9_N`ETV=*)Ub%!6;Zn)YF9+9il|W$wJ3u6 zBB(Bc>LMsEg5n}5E`s7BC@zBHA}B6`;vy(6g5n}5E`s7BC@iAwu#R~C9zMMkpI(Yj zFU6;q8e^R`#yDWSQ({cucoFw5BWt0CIq#!0Fr@&+#JQ65vYU8tzR3ZgxtE zJ*C8+Qesajv8R;SQ)(`C*5J)c@a83W^Afyy3EsTKT;Xh^-LQ>z!#3It+wg;B_`xz_ zRVlHmlvq_ttSTi|l@hB;iB+Yz1hYy#~}pp~U)Wdf~CpoIyvE`io1(7FUs zbt_SID^YbTQFSX(bt{^fKob*aVggM}pos}It{RQ2M&qi{xN0=66pbrI;}U3G0*y-Ak_1|kKuZ#6NdhfNpd|^kB!QMB(2@k2kU$d>Xaesu0VX@u zXhAhvP>mK;qXpGyK{Z-XjTV%m1*K>~Dbi0M`D!GeK=KJBUW&v^>Ca>~1{r=+BpyZL zQ6wHk;!z|XMaog697V!WE!odGegXXKM3H0^DMpcE6e&iLViYMxkz!QMHJYkQplGThMdGokBj>lO?XETvOePo!(A5 zhkIja7mNpkw3jZY#6%#y&GZr1>v-43ryTwE(|UgAx1riNyXie{0-t}cE!C6^yD4p{ zrnIG+(w1tfHXF@U3Ggi~3g`zapdYBfx{!NgfpNfi z;Bw#!;7VX3a0@UUm;v0%{_V4X*}!eUAL$*q4}A9n(x#*j!+I6EmU5K#C0-64thB!1 zp0qQIIpRBMZ#Jd9*_8HXQ*^VEG5jXzr`zVN;XJ+F*_3_}X?tdKY-u#HTLW!?|DCqz z7=CjeZ~-tDK+lL5yNMUO(dkq>^=huAt@;P>-U`eHq}}=w_r12O{W14G0sie2*c<6f z*km-LU#5T^j+)tJz;4d>a(_QiOAp5{9Dk+dAy!aMb_73-tAIFwU8w%(6|zy>v$_F2 zfD?fAz)$?v2EL#yGsI|JT?NF+`1%8*0IvDfK7O^2U+v>p`}oy9ezlKZ?PH|pa^||r zFDG)O)j;&wuQ9Z9_NQYZD(ojJ?8l4m2V)HwYrrUNv;BDP{Tg#I$E`pWPy_f+YU%99 zGw;VU@5htw$CK{IlkUfJ?#FZPhmsmbfWppt;^lf{5IOp(WNbN5BDp#lJrq_$VFc?F6B+EmkHRE*zQN1neQ z{#4U@RIJVxrf7iKXj_rRz;;2dtv? z@)oU>O!E0`pamf9fn&KRZ31ZzxNU*`mb3+4<^EepZVlzu!l5s?UQZ09Rb)x~U@OP( zo%O`M^~AmP^j2+wn-Se+sNqa8A?V~uh;ncJ&bOFz^WQDB}UoIm^h&i^-RZ z$(M`Cmy5}li;?VnBs(9;&PTG#km@p|x(ul{fei;?PXq&gp|CdiA6$%%`} zgNw<7rRBE_sTLyDLZn(~m2oc$>~=mwvdfTa0*NNbe~Za~i;?PXB)S`E?naWk$a{;C z<}xI?3@I){g7cB!F7nZ0q_~Wnw3z&}nEbOCiOxr&pCQf9n6(#nRwBujNOA#^`~*qv zLz1r{$&E;IBa+;xZEFq3V#<^NTPgDcaoM%2wMcQJwygxmy+94HAE*U>ay~_p8o}s`3aKTh$O`yRU^%fNOJ+wEJT_ck>*CEDSk@qa6w>)BGK`DcM)(2 za3wHN`!cc3A|zUgG#4UGCZIbjk>pAwxe`gPM3O6!Es5ovycG+#rS;$y`|#m`nF)s0AXBT|ha)fiHZY5P2ed%4bQNcA$jG9*}rhZOHvjd!fZF3XVQY9zTD z8~g(4m0^QrNUjXYl_9w@Bv*#ywj;SRBv*#y%8*)0%hhZS`%*p`#9&hNTLi$lp%@L zNMbdTSdFKwhU;Z`#%j2}8jhF2@iIJPHTF{m$II}J)p*8gI9&#(%kYTR#24Xg8JsP{ z16Jexs_}f)*h(2*uNse6ZO;bgIIFRfGPqd=H_PB=8Qffr$E(KMRkIK8Abj4bWT>uf zMB!`+oRyx45^Q5H94&#P5jYz0Y$J|sRKnc|+%3U2_QK&3I9vjUOW<$`94>*wo8WK> z94>*wC2+U|4wt~;5^N(1XG=WW;Ee{H4<=TfiigVKd??`B#$Grb!8R)Kc9n3u1a6nW z?Gm_M;@QPs>>`R??7=P~*u^GnAc_P^;Cu<3FM;zVaJ~f2N8o$}&PT9;C^itq2BO$N z)U$yo(kQ_O;z%Rn*?_e0OR#~xNTdXblwbol(T=H6^JOyyG4)qVn$C3OG``V99)nHRK*i;QQO;FPWHBC^{z0@#4t@cu@{nTndYXy#@C&~445oTj$ zVk;5sBMaNeqIMDNB2#;}@34cdoNohupdIbzam?BU%N}|*_EG*PEb$j?DT&9ijV$aW z3ob+yBgCEui>i^FyIyM-wjrKt7q(GJyr{r7GO-QTzjM5n-%SCo>vv}$ifb?u!Ah@APX)=(Dy9#Jqvx$YT(r*^NyguS#ULi?q?0Bth#HVeJYvMt(YN79yS1snxPF2=kQbTgZxMbjxeT%o-W#0iF#!b-0EYHhH22Lb0!cGA> zTLBVVh#r1{w3Z{Kg-B?jmzPD*y>&>U2q}CG_rHMi3w4$}n&X*plu;R-8DC3Vh%q?0 zyNI~3k$kCue5rtZsepW`fPAR{ZZ3y|3(?7i=;T6laycAi4^rTHU_RjHL<`~8a&n&n za-RZnp8~k{1zcN>KBnh35o)}g8ZW2D%c=1~Y8;`q%c?4P_fqv-GU|Mq{IgaLdAxGNlSlceFY!|hxz{+;1 zT>3s35usK)snt$uwUb&^P%9ask#GQzlHj8sP!o=hf<+X#*HfKQ8vHSnYceu&qpf)6$DK|D(h zb*`b#HPpF^I@j=oDrR?%C36}FOaQK=<+p}o43Pf4IM)fz_X1>T#(tm{_=)chaQqcG z2qeic4Il(G!e4g6clPAGzi#lR+G0>leZ+uTdK)hB!jFbXQ?JXPmr5Si>umh3>*X81>^x_ ztmGcmNH#TQT}yslOMYETE%rnCekhi)>sqL+g$ij?9&QY~mi)Jt9JiMIww9c>mRz=$ zT(%Y~N*}|nwQ8JN^4MDP*jn<~T5{N0a@bmO*jjScT5{D|;?r1oI1ZQq$o!HRu!n3W z!ErB81MCNC0eXkvcN}lM2cE~_dEEGooHXgg;d>mu$KiLJ+%rxs_Q2~nypH3s_u#Gf z!0$Nxj>GRb{Ek!0J@7gXujBAK4zJ_zIu5Vn@H!5!rC8rN=B`Qrxf z@SFDltO2boL@Nu;HNeNfC%|U_d@`9c$k~K|M!g{6r$nf z-WWnTb50XzdLf!#j;0rS;|PUTY8;^%;|R?dM`&j4)K*aFjU^N^)>UZ9Si&#dKL|L? zp;Ukg$PB7Xjxvj?{+L3c-4TuM3>*s_2XqIH2YLd%0NIV>B!GTQ=8%d-721P=(|{qs zFkl2Q5*Q8quV!go#`jkM69JjAHHl;T7zDb5g%x69jY`y#_CzL6*@=xxl-fyjdY7l|#9re(EfX7!W1kY8-p58IGVSEa-|^&3Y&DLp z#<5X}Ogo_>6FZ4xCvof~j-AA@lQ{YxNB`sKe;oafqyKUAKaT##(f>I5A4mV==zkpj zkE8!_^goUljiXa>^eK+#jN>`uc+NQb5=Tek=tvwLiK8D9-FD(Nky#v>#r0@RA(;jq4fx5q7p}xL{~I~qlWpL! z;QOf#D|V^%IlyFgte8qRa*K11xeBX&3vE6YcvY+bU`8<62HFnPpcO5F)<7G8d8=d@ zg=86pWEq7}6DQLsB-1D~j`hZc5>T@rYH0tF5lLo8UI<0VhuLuLc(P<`lAVp^`YO=| zX0)Y`6{W_9WMseL_>h~0{fF@(ogtKvA(W71$-K}KGK3N`gc34@66<@Q6xdD#Xhk-< z13GuWz1!g4T4>!&uJ#?A`yN_%z`YL8y8{lc)mhF}oKJ@CspN0BI2V&G?jSqdL3X$U z4y}bNYl%QVIa9zUBNg8T*aXvAj5J8Dl!k#{74xfNcIf(vwdYBCsr~26FB+8Ab^C_8 z5oLgffQNxcfJdEgsM|Nx?HlU$4I^Y{G9&gZ;A~(FFy6_M8hdt$CPkGo=9`=-nJ>_9fdDv4P_LPS` zgFu~=YlWOPRtC@rXaY0?7C3qM^jxx*TXkZNG&I2w0#sct=%q5r1B^Ue5!#?w{ z&piBWE;gEnedgg;bFtApY&6ea{r|Oh=J8n-=N_N!%|;*_NI>>Q5fu;+H>!ey3Ir4s zQ4mEIQG+00tyiRCD{}3%E~U4KZN<8f5P};Ds1%T$0OA6QpsWGe7byyA&iy`f^5%Vm z38=UK-8<)V=6&ATmuH?cXP!B~p{=ttl0vU#hPsY-me1gvBi!GzYc-ua6A4nC*?l)8 z#c%k1nAz(%=tme|Yq-DU-olNZz{$K^QY$94L!`Ec)OL~DE>hb?YP(3SnACQWS~02Z zBDF)LR!nNeq*hF7#iUkDYP(3SnD6kza_yIV`P_b8FiUS>+-lDuPO*KA6Fm-8s&82LzD_q_W=0oiZh7wQS9d>X!4jLZD*(a26 zH7g%GPgwbwLg_x_8^zwiSk)Iz<*<@NYD~!yHLj#UjSu>({|vg)1|!wHAWba{Qq+>5 z1GC9Q+F~eekw#loqb*XY$3W^4p*`kOmuu9%k`n5YMeakXPc=#~R0Tm6^-9!6wW4m- z82K5xPRUNB&vfcogF3dSj@2mXUDWd$>X}MuFVi_ChiK3F`qq*I$ddDrMz>P$6zUzJ z^sQ*ywR{JW!W>w>BdA7w%CLseXbU-VhYgi$(O}DpnI32d|RD0orbp znoOI|hBxNWq9MO5gGaLHg~hbP3ff=|bsI-745n_6Qnw-W;}_KJu)ZDr+#q!4xj{a4 zTBg^b%PWRQlZYA4GLjhs4S0@R%2?j)K`ysfnGLE;=D z4&MmEAM7ZCN1lT7Gnm1%sz~Yj(iXER;fi1;z4s~c=1|gI#C-!U`iS_;h(CoA3s-L^ zot5OhlC+9w`2)1aPD;9i_V|ka?~Bj6U^cFYaXp0V*VJJ>&z^m%1~uPKI--}}PK~!w zx@~J}3(FaT6tV}q%C*1x)xqVWiGm7n#a=Z&wCvIy-a)x~O z0cL|^9k4fRg?;IdVL>jve-FKX1-(C=-hY8w&!*?c(&G=(q~{V=Wc27LVxWq1R=ei*)f0KRSpUq3|4{g##+Ld$&)Uyp;Y zC&AYb(TZ=v*V2+>Xo1P}eJ(A!gPPq&hjyGp+l1fA zWKgTE!qc?!dVJU8yPmd@w?WzAwG@t)-CexpQI11CUK(Q}m3VuISFNPLIeL33l=SvtVu2xo3-}e0@mKdDcH%=>?fO`A2D8x1`k+`4?e?QAKV3YINxO<%QE;Lq99;h zfPSwUh9oDrTiPP1#eKl@IcTI}f+-cZZs=Q)9qi`GA|bPOvYK+xL};o>cOF)sH7wq3n!)r@0H+-bXJ^O9>*hL78Uh&EPryF|D&RK z2Wj0f*I3>ahx{q7$P`8zw(LISq_#52Roc=&C5EFEC+y{v3X+ER$0Cv{C$ds(Hy;LD=+|ThJ>;$5W*vSa`R)mnfxUm8Z2g z%-0k<(aTIQyd*c6TJ|>#b9MRuC;JyKwJHx6avMCEMec`#M}x0|HYK~{Uua~6lx)WD z#b6e5=csGzC?&rlXPBW07rl%GN(FCK7R=qRTL{&QF4DYz2G8ZMdzSKO=gasK%6RE zTAp;g_ee^Xnekc7lh5IuC7EwAFU74BU(D13f#@1=hy;PCuE!p#=dp*_m(JB#^#gyg ziANne)1wYG18-<1DGA2VxgZQJ0EbAB6kD=~Wi9XcpK`Tk56kCZAZ%n0OIx*>cbf~; zUam_Ox@o=x{)X#vaE89;eW64Jsv~DkM!@A2q@m6pY3M4CG<3BVjG?X`W2n2w7`o15 z4E6LFL%lr45C}tH41q8N#?Xx(W2leE7`h3Jp~qD}{YSpp8R#*F277d&As$`m4v#K$ zr$-mc_2@#wJi5>bk1lktM;99D(S=5NbfNn^y3qZg3%#s<;}M0P2T^E|df6ifz3q{M z=6mFzg&sL*kw*?%?2&_(fNoa|_K@HQE%W$6%RPS33XdOD+SVG_F zULHm0R*xbyz@rEa_9#Mkcod_fJERQHO+an6i^@u|M^oT<9J)+P8k0`XrBML3{h(gOe zqR?`WD73;O3VrAig+6i%Kot5IM4=*(h6GFK3y&rArNJ~Vz%PS zW_*Fh$@pr^3ZmeORbyAkMTEJSt1bvcmw-Cio+}e1qF?cjc_~*0W4;q5=**SK=)Vet zN*Vu2e*8Pee@|pYFD?rT+4Y1G9I_f-79=AJ`teqG3s*8yp+BV;G_pi57c_F=b@V51 za2fC^-=u6axr~<-$zD=8FDVkeq-fw}L?#mAOX|Iys{wLi2ewFx+FnwCImDIdB}IKC zMTx49vUp_S&&!>v zUhbrMxs&DPjv%Z)2*RqMnpH#YJgN-no8wg$2%L|r2&kJA@%^)zq*6dTdzSAKCz~l? zlD=SGP&GaBSrT$;nsTN9{4_EupxGu+y^4LbDKB5Y#ksa0#VaYL==bus)YTFE`7f zSC|#hMWzUPrCABR%B+H3ZB|2n0Pb3nS#Q=;PLYx+UP@N+QZn01$!sqrv%QqeHiyk& zLLMrD}?oswrNorg*8E;-zYem#U{DRU7d&Tw~iQxR}Y9|vLd51@#Nr2ZrBPueF{9Z*i6!uM(WG&xSR6Va#t#r}nm zlPtLNAf7%;O()yQ#CndMn3-Uo{tbGHox-s%pvNgIQl1)t9tT}$3rTH;ouL|mjQR$+ z=AfgJ=3DIEYzjsyTm(+)Eb^UgXA@?QokRG!HpKp&M~?F?+yZXu0`h&|zE6$|?Lw7c z7uiKBiKoR9a#?DZszkfYE>ktZqFYYxE9?potcon$VprOgsy0s)^mt&eeymQH=L$9$ zb=d3eddmMfUnwTr4eTwb#WQ9T_7=N^8f|4qsj=JaHgeo_~IB-L2|caIc63 znjIxPWDk-1VNlmnEjT8)|BD@{nf9nXN=d$DC#tc>>@m_g&W==LzvGml1fE+Zq#4*i zCGqqE#o7T=!@OO(3iMNpxe9l(7$rOg1*#U3VoTo4El0+ zIdlit0lK5>2z`aS0{TjKC3Gj(3A&5xLR)opU1_Cmt{ZfY%Yp9hx)rLxH@F+1dpk}ta5uUep}`u&W|p8*WR`$E0z|!3cQ1Q3lHEv< z^-|p^z8_6?qe0k9b@%ZdX|l{A*lqlvdr&oUW84^3+apvvH_nYy4csH{5!^v2WEV0B zg~XcVCMoBhW&f~olO6MJ_Z(-0MVMPWPf4aYP6&`0hN{5~;}uoMO?6Y@fjpOoFZhMn zg|3hkX1E!o35p>$D26KAz2)A*eU_VrJ=@JDISMSDMUS zR6UuysKzpPfd&T=cW@910}di@0-75BD{fUcw!T}N=W^|e-cx?$CT}LD)HVEVToy+D z%PnYKDa>F_W40ovL&O{2<|v0;@*6Y2L^E#z(;O$(2AD4;W7ohgU7v{s{l!$Y+?6F@ z%1RVc{yns^z&~>5pc5sn6Ejj5sf8FD{*>Bb{mZ9ch4{7buTmOD*5?#1jr?x|Z=YyM zsx`Q!^-lM;xM-+`{-1;wmoRtj!-!j~CCZmr{KVJMdr?H}5U)MfHendC!&GDcGfK-M zE@3TVc}slp7xxB~uDq#5$zmmqrE7hhkYl1Tq>aSHN)*NpT`RNe!{MUQs1BctEx)0u zT}D+F_ut_zJbfxtllO)u1j^*yfE5qs^x+*JPu!xWDj~|RZ)E#8iuj?a6AfDFZx}-2 zN$$~j5My($y;6`|a;5Ut z5`TpU3 zBkC?GMomR-QNP%47%KcE~GV#Kzd zr{tf+5+f2il+B?mlo+DXk>^2_|0GDV!}B?WH^ZJN{~PkA@Tasv>=_fbRG44Xj$cQPml9Eb`Hi-= z^u4rh*gj#--ih*r^$Y*S>}bnIU1YRLd1OA6>~HB|8AUO9DY7^A53-yQxva{#H|AAd z+J@sT2?-ub^e{~+Ws1JtJfmDC=cQa7){PTbwFBbD(oH-`$`V#M zJz9nY!b*=;T#kxD3v&NY4=pmj^3d_6j+~^_*#ldT8mOKbIB?hqHEB@pop-1ygYFqV zOuaH>h7BB{ zj*l9}S%M0?wNAipqf@cl>P+nObvAZ8T@U*L-DEVebaUT=SoCDbw=#01Rpr@kkuNp< zEK~cDB0Zh#ZwW87gcJ#)kyc{MI1?Elw#>YADW8Px6OIDmZ#i*JY~e~d)kkdManWFi zEwWV8zqM66*D7&;(v+kXY4@c4K5a(Y`)NDU)6=`9-mAXi;(988QR^VEj^H_~*rg!LK zyhnRv**H^>>`a^n)&;fwe0!hO?NQiEw6UqDkT` zrW8}fTw;?8h zdy&1^USiwZU)f9TW%hF0!FFVo=}OzlcD7g9tL-(mi|uN=vF6m>_ORF5>ugWki?yd4 zY;Sv`?PG7UH`~6ppS{KQx3}8c>;QYa9q0l($PTtc>>YNfz0=-h@3y(DPTgaN+Y$C& zJJOD_qwReWV}EV$w-2yl^&j>@JH|d_$J*c8hwV7~i2a@Yy?vAkj=XuUbgSHI-aXg2 z4|(_e*nQ&Gx^?bT-ayy8&)o*Mk+;w<-6pr$ZE;)OHn-gsyB%()4tOu!#arnfx7Y1+ z``rO|khjyroDg+{_tT>h%Nf*>NP_#u9dpOs_olY{!F^{^O=hG@M4rO`e=5`e#Z#GL zBY&gXWA6PWW84ffqiib~7r{Rcq8yWKlg@a2+{&C^g=!n+= zB31bIFdSRq$d8v46-t4cNPu7e;&K1T&l&mu>v8|%QU4j^TSj>}x2yV#mBR^zJ=C>cqty#d)(z;fZe;K7 zP3W@vqQ|-g{newKA@~?42tKaDvq&vw$LmtH zOf6@}Ymr*1R&kEbZcYN+%YNhioS<`19pX!>uhkL7*U4zCzE#JNH*%8B52D}ZwNGoq zp)hEzbb?Mq8`YLm058-R>-M@M8mG>>i_XzEn=xiATAb(1U(Mgl-_7%83fi21m>11U z=4JDWnQHP(K02KOGu;%L8Rk{lO+Yrsi8R$2f+jDFS`*-_1I)oSOi|7zuMuRZb=GlC72nBXJ>-~%D zVmWDs75?R{>=&`3zsjz*AJ{eaL)P^_vFq$-tl%GD4gPCZ(UV*?*2c%O>iRqPSR^UJ zSIIoL6PPhHR~_lKk@SzMm*;@rE(x!)FZH(TP|<;1?ybnNOl z_f~Q4YvSCqWA5%6KTFC}(K86U+e4O1#|+G`IPa3loGV%@$f<4Kd}N zz@HqL(3+E>Myi+849@afp}yDE&}DYvyvMQnDb95~$~>WwX>Yo7=H6e?-)%OB(K@x@ zY2SsX?0ED(lR39;wcYGeScmWIhO$yUk(J-i+~G)_NQ=mYk&cmWk=~JqBhwam_bRODwb+ zgg1NzL0P+D7nna`PeV5lZP7VU`S3vs>71tOm=gI9BTI|E3ti}5@L@|!Z{eHh+%z4)SEk)9rn4S)DD%Fc#-^bk_&@_ZtkkdL-YN;Vlg4{evU zmh|Z~CoUoXO@#{iPwF1!zXD%(`FCQvQm1^hR+8>Nu=CJbNvSTup633F9j5DB#`B%D zr_aHU>oTr!ui~C>zVbP}?!!d+X}9;;>wVtzK4ERQh&wtQNiUwC4*T#&eE3=7>n34? zm4`j;gm#hs$VV$C<)@aEKL@+O2zO6&Q?SGGzXKJ|MS=Xc+UFr7wGa)IgpKxP#QRtA z{{IwzZsDRd?=s)Jld1o;l&xds?}WH{p`+24EMuzSw#0 z7VHA_jwgm3f=j-;2fNS>#m;kgVHdbu?DA>eflIy{j$P>P#LjbfV;8t#*k}up!7oDz zuV=7h<}98cXR`w@!j7P}$hUUv_W@UmJwBWQ#w_SGWl~u#gRrVDoaQ10Z zBsr2&`j#a-N#13j$12`0`;>*06#%~it{;p&_tfRMC6O-NrsX8|E-FV0$L7R^;T+|% zF!OwvYqgAj^ub(R;zBmZHl4Y5|m##A)Gep(Ljp)t{ltYNzF_XHAR6 zyAAhla&~kq9Oo!=aei}I8`NRn%sFy8a+ymCE-g$;Tnw$+BhIHDyL39SyQj0c+FWDq zFhk9q<}P!$$u+|WmCBy4X7p(rT0Wf1c4Z%y%v@t>v!lS=V}=_ko28e#n!DnBTX5Rk z&8ENUV{R}vn%<_bxyjsO`k5Z4i|J~*nH*mpC+j6nbV zgquh?V)>Dxn`lPh{zvzud&)g+hPY?kpGd1asdXjg!K$|DMVehm(UhimgXzyxtz0UU z<(9aT-9)KwhCAcWNR|_ls}oD+CZrcFF?mp4N*PxQDFY=fmxFuSJrfHszk&uaIA#wSxH^0m*~a#G%yXB(=|3t2onoYn_jq7{Rbz$ Xk2aT?%S{K<(OhA!BxPw6dg^}wc$YKS literal 0 HcmV?d00001 diff --git a/web/libs/fonts/Black/Roboto-Black.woff b/web/libs/fonts/Black/Roboto-Black.woff new file mode 100644 index 0000000000000000000000000000000000000000..1fe59bab318f977a05c3c03a074f1803a5a430ec GIT binary patch literal 90664 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�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

eFy{%(FQC2zbDWP9UX{UYG!*wV7$?*8UyzQI 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�_SD^G{wdrrHYdIGDG`C$ZMlG79#yZF z@)$jWQ>84Z$8gNcwsv;@$HT;|`s#@KV%dG1r`8(1K!sw`5&GOZaf6PGx}AB&PZi(Q z%`8ydj9-&S)j5){>uQW|GdETo+l>Zn@Kvs(d@j#D=2^3%( zW0sb)q!9=wX=<`@LgXqlbH#_96~)zS$jC5P9BWcPG+uo1+Vk&(D~%}MLG*}E7^3i( zmUU>$_fLHDZ_Yn>mMEeQdYeUow(Fv*-Yv7&4cfLj<$X{od{|>WE{O7|79skII<~lW zaqw;}izA0goR^;O#dBbENxl{4lSyp1B`-rBTatm{^tEQZ>*@0AsR}FuT-s^}|`clHscJ5te%5Llz~D4DL1Qk+ATALq5s**Mg1V z!HY%@S><5^!k;?y<34d~AzOEALjJZPOP{?{(Y`s|sVi3ocI`C~-IXiY9pk$W;QfnP z`K(Eba_3oCkEcZ^R9{dgfb^yDe(F}!s?=&?k24+8m&W@ko1|6wt~x_$t9^(p+Xz^g z1S1XKEfoQjCm_TbkC`Hhs(m)q}G^0w-ErP$Gt9lPP^ z_IeT7+}(Lw?Yi;WGROGwMdZ8gDMp}rfa*+Kn2XLer5>Fk7G9mBi=ctC`j!q35}ffL zKUeW^O=YTAUd4#bvo?d4PO4G7y)z4WXi2);l7}XxAen98ATsSZ4*Dd5A_WEP*v9Nm z5lO!!L@sh>_3gN-$Nt^Zpffl&Dpz@J49$&iQi4d0wa6K103uW%Q<9;pA)Cg@>TYb= zG%WF#_*CU;-%(52?cI)aCRX{pc!iZX{>JodM$;KosLwJMST z=ht_~{ijd7OQ++aa*cg~aRBF04+^1TMvNoR*hMUcH0x>Suyu(cZdc0MP8~xMoQ}tP zbneo*Td!A!?8C~RRil)hSd~n6AFv}`$fg@2cWtk7RauYk&SdvfSvXQq?j$Ozd`_I= zr&$=r9O0;kE>aR3%1!LoQHmIoF?NJXKwfT?M-X}0=7EG|xga%mjXD|P-ug~GBKfdx6W(TuEZ3+4gAv91jBy9mtsfC$hT*?IVrb?zm)LHd2 z49$w3OPYroB`z!xPc0EI)@99_vo6h9YbCqlT_qb{fVtuUT4N2Fn$s}~uVb+>3R}{E z?T2l_6gdhrPt){z$Z437a9iLLhOZ@dKq+M&6tvKLcND54mBRdwGF9;3OX30` zsoRJbvslxC;-xIsq&X|bEV9IffvDyh$#wzhzai`y%tloB-=G<7Wnc3Z%1_9%iPM7v z_}P?{NXP1fnja4$21m_CV_cA%nVF5ZIoC{^xP;+4;U50`jFtSJDZV?GbG~#uo!q?2 z-<7$nGfrp}Ku}P3z`l|6gV(j5Rj>Pu*Xi%dGoT~pn2BQh&y*Ti*92V)WF=|{t5i3H z#W*qna@dd(A;NijS}vc(5*xP5NNLG|ag<}@3gTeK%N;?s1D$EGC-W6~=k>8T33-V>Wu`+UX)>@Ti!xAa3%LZ7 zIdG>IJzO;Yo~3(HqWavLhue&!r&`F1qSyU9ovw&yRxH?z+f&}hl&O!3qgr;|DN(^% zy}G%bg4i)F*EiW4tktcDy9yT`>$eRG(dy*nb`0Vhv{BxVPP! zCzbl@p``{t1rrLET;xp|;O9XFt&O-@Iy-q*2l5pUnPXM?s}UD&e_?xgmO`In7a zw{6^`VLRN&?z)2U`JY-De808uOIyvX{VLTY42PB|y(?d<4AIU& z8g|#iEnUxTVJn*{CW*}pi9}padKKvguE9_nL`n>uQWwe_$k#!SZ25>4^XG;3`K*)X z9@3-b$S)q$$`-HWd)5gzJJNg!SBm|gDybM#r2ubz3nwJQu~DyWK@hX$t(k_djsP~D zoD8^Xs?%}6nrv7W;Pwt#b(|r~tZ+q9Olsb#h*Sz9qdQHS<+I>yh#E(g>owz~#|w&= zC9n>H_qhNh0EP!wmISsX*iS1~Rz6@e^OXjlsjX#J9d!9(y7_lLFO;{}R zy#B)Hs(415IX^zkpZt6H4fZaNozzVgzp^l1b7W6;X#Bwi<-e%vmoKudigJGP!SUE_ z5^h%)U-GmfKcu`;_4$q)?CMR|Gz zk`v2#wc=80*JCI@2YesJR;UPo7~oO{FxiY0SMV*oj_Pf^o-Ci&lF!pXZ%nR>3)wR= zgN9a3BF~Z7U~z>m4Y*d?i)RzhCZ9_@J4hVA@Yx`7aL}2=Gs$NY&-7&74>Q&i{m3qN zO5G~ftZ0>?{IKiLE`KF}H}MZo*+tQuU97mlkFZ0B5T50Zn93iW=dflesHuEZk2h*6 zu~w#s+(uE-S7gd*Ge=NTxoOpv!ZqZuc*r2bHH%XP6ozGqDA!Y?B}t~{Ssh9EaDelI zo-DQfSQm7DiH*yLr;K=~Hyx%4Wf;PF7-!(}Q9J=YQ=jvZC;Q`^)Tq=Vc7)jNXUpGWK|vh~S?9bXw^}qFvLZaBZ;v9Sm+<5?Rn*@SIX|?WCmbyU-bV%6 zcU4^FS^oEx1U&ujyYGaHPvtZ5PyD)f-yWpt@yb82Q3 zY`$M}59Eea?GAJTi(8Na9@b!x7G*$qX2v%|)zBPc~`vD9=pR1sz|eit}m{=Syz@3o6H#*h^v5V+g%C zE{SG95ADad`^Q|k0y)*D@<;Ur)ZV2zmfQSDHg0D!I6d4gQN0j9kz#T6 z8ZrTu24GdmfY(V*Nrvw%#qt3V7|v~!!~m;l>(QDDZvucp(NR1g1Ar|FH!1v4wUrE4 zDhh+{5bvej>OCsr{yDq;Ju~KT($3i3Nt=t&r^Y#EMW=4P#I0ehAW^&>i#p z!%C8huHRFujrbs#3fvUeE~S;Si^12qcNN>ErlzR7#8viAYAg1EGf{Dh0bu}N1zk1A zvaZ95)&PE9ltf`QxR=f0Ty;JYJT`nr+*dKaTckZIri%!TTOTbIho`sy-k&_`}BJY?YK| znV#;Al&Y&Yk9cTY-icmuBjK1Ti?<*-f~`W6g8digpqgUKbZn>3hd- z%i0>bwa(TYHX!Lh))P?&vi1*Jb27on|L*dlC_BO9_=2o!bw}YJQ+DQD%N>P(BFXvP zn?lAgt3dZ@*-Xk&?{Mrmh>Rk>^A!;3X^%~&b0U1D@^y|z@l%NEJ1a+ zaCS4OH98c199yNScq>^POuo?IjQ8aR=jU_x4OSx}LR|biQ>{_DA+_cjX+?$Pr4 z!Gi}`xYAN#5&Sq4e_nn|+zMeT?mMFWNvD?W(Y#J|91h@!)d06^j7sZ6y02MtQW}u+ z3VjvVy7F28tpi9a1=tywB~oECS0oJ{SP3tT87bwcKhwj5DV^a|z3}GYUBi`O6n5}b zTeM`$#8uzwH5789ovK}zcZC108P}Zz=!w~L#XuI1P{_F$y z?{hmt&S_ds))a3L4BfoWZvT6a!X#F67CplG{$*{}ALjg#g=|Ps)1I|^*3S^*)dNer zuimHf{r45GJSgtTY0uS_Z5p=*Vo$?A$?65A{Z{#%8jhNjnur}w(BqA+8qk+b7%QnY znCoi#jnL{!`Wu4s%G}i^AdwK%uA0>@6QVB22k02LHT$eh5pk!h5aP4|86@OM08x>% z9yxE)Dd6JwJTGo0hJ|Y#Cq{M|-zMeXXEq%VRwIP?%pIf#ZJxh1(WNBRXdNFHv};YP z9`RxP91msVzhX{Jy|^`dtN3MHoqXTiAa-Nb=Iq+_xpogpQw7@7TgdH0Hw&DsdX9a1 zn$as0I}F;w6+#VH)3TDTd~^17K~;6l8)*icH&ygTl$e3HWsuF~3h;e{1Z_$Q)T|hb zFh!>to8Gq4PjO;fGi_;OrXod=tF97t|&1C!o<|HHxxRI1DqZ>*9XmW?y_hkl`0?V!(d<$674b3~ zF7`mv+QCNqzhI~-8N~L_ra3GC<Y?#i-68v zt5H+3=`K|Mij3w*9X|FVJUz?t_y|jbKy>li3Mcj8fKen;zRVVKIm&Oz0zUjr;R&n{fxGIKz zrtRqd8gO@(Gx{BU=0l+{{wg-*u`$u&NFd)M@Y~xV9|q-H0Pl zw1B0mt;DHs#gwb!BnwyT;(IRKY~_3g+RvkS-V@&$iq3w7D?{{L6|R7=vwRzJ0F5$d zn_i&1WG`;~6r~OnqL7?^P}%DLH-9DP*5o9iOaHJ&C7$yrpT zmpY8}Qm{@-!4{2|DjHC-#TEAQU;d-wv(7){vJI?(x&$es-vFOPakK{t*;Sx+CP>f6 zB%K#FA-drn*p75<5a0l&xFZ863JzPN(;X~G8QUkFHx$mZDCuN};A>Q^HIXrd=zq55 z6^Z8ve$6Rul;Ed8bdnDM(E{25MQNqrb804AN9-1~SD-z3-zWK>iW_U+85@zrI$wR@ zO2estKCtHPG2tUA<{|+P0rfcpdlbcT37|nM)Al`8C z_5Mu=y0mtxk1l<9HRtR8-h5o#e2~4=e>6WnCw3UJ!+*`lZZKdJ<4;VBZLwky*Pa9w zsiKq8!C4bozpZUp6Vk_ECYH(#L14)iFM@otWJ)7{1By|auTa)6kR*$9Wu}wmGa>H# zbL~fE=G9r6cry0)=h}_0(l${ z6lInkVIWW<+CiE5P|!IxFOT*HaT{w^8<=xswdF-|Wq6Me6fsmfJi%r`le1v(;;&^H zmXFjTqn6@O`3+T+U{Hb^#eV>u=PJlANaj%RT;?llCa#&pTqDMg&8?9$zHRGiGp4m! zzr4Z3Wi2KxyJOM(ZV8j-wVpa-dYfgdl+6>@_0PJM+ajxV7Qg$zM3&yMPLqzeCdpfz zh<1*iYI}7HW{WT?Az)e5c~#n4E_sD?M5hCf<*zW{_+owW{1Uc<)qJhfGNhyPW4K%>o{LkBr-?ly3 z>NAcBj#}Wumx~-tA7d_dta7;jcgiszfie(%Bwf&^T;`a=KUJUzRmUgN@QDwqKCv$F z3GzjZq{~uVB&SDkLZNy+4N;CJfrvOc7?|}mnhHi08*oY_4BagsBxk|#XQ&@yw^-UI zqELMNF|YsY(4Uc%73*)tud(THK&?meJmg**M;ULTQA{oh{O24+10DO7E1R?ZIg(t(?l7 zEE(UJuCqXN-IaH$maD$2$fXQBvCg&21^Y}=G3g4BMy)v#efMrHiH~{v!)K24xp&eG zM&pLJR(lQI-ZLG=keV(>F&~9T?q~)c`T?@TW5C(&|A(#*QUch{tvMfCMw7&gH~&(_ z14)EbWe-7wFw6P4t@H;f8%|g>mIG^17$*ZI@RGTZjzS(E1y!D@tv77RTH~(AQoddC zVaKg=1}u(seOUMP4SWN&=k2QW`krOI`z2>h#P1Dw-vP<&`-)On^GwdYrZWzf@ibkv zRL)5yz?@sv_RyfE1!GR9%CB#m4io~5L?m(Rw#-iYHb7cr&IKkAm{vYuyHb_1fjPtG zMMhk%`x*w1X{`-SXHg#=V`GF6C$&&_kB*&a_%x?ZFTbgH|AyzC=HZ&)OoYtOcAU|p z4exq1*|vs)X4@;d?&joVyxyFTt;VhvERQNw$a=V`XTD9EE@m94HvUrEys3kmI&=e6 zEuUr5&^m70Rq#YBmZXn7k8+2R2OMDOv_8NHLXsYNMJ>w(ocMQc zAA>5CUe%Q0I)YP?<#=3=sap>c-B?L!ij4Xc)pNXmOpG|8;)!XRkClg4kFDVq?rn!LcKc@r3W6tGVXZU9?&yr(fsUZN++MqLsf? ze((GOC$p>LpdQ<;dlen!W9ReDRVlx2}GG`LS<(Zx~nSvZ(v9RgXLKFvuk zC{il{qz!UIvLGE%7sa!vXQvs5L?E&-U;WF~wy0zbx@0Lxt*YOqVM%br56KJ>gcXbL zTyXLa&KmNytp^52t~+~LQoSDR5WS}0S`zkMw#>`2y={ar}~b?e3t9Qg32Al`dL zo#pDL&i$7!Y4BlTiw>-LgHA1j2VA*hSG^3*U*E&lHmhG;tM1_ilXec`-kHtT?Nzlm zO9y{1Jo$~d`g@|nkMFL@ZhY^Et=uPOy4z?+VbRpY32)+P1#IqOM=x|=8UpmpG0-z6 z1LtWTP@k4KmN`~99(1flYwOZhhV9c0`_PSC5KD>?2wAuYm-)+ZS=t;)eBC^ygUd^Y zmzRz%FYQ#W2es-%)<6*I*2r~8J}l|Jhm+7y8TH;ZWnDz?yt3)l5e_}3_jLI=BxzQ^ zN}k0`%Pj&=@LtvhYNW^L=}^|#^4C_L(kbPoca)co1=44ESy8p~pmcnB>7??~spX|D z%L$0QEg-%a=|jS| zJf*A3OP6>`A1E(f>M31aUb@Uvx`yb=NdyGLqyXgNrG6eU(Nx@m9Aa?kQL~~!!4a+|P1^PkR^Axb z%ssx>;IX_!?Vr;k=f1hl~a%0Y=XdlI}yoOVgZ(U28INcl3 zleLdwo*4g~F)Sj+KQ)Gh$GjGO+Y4+!zr~*5HmRd3UZ0*bJ$hR9^kdU=rnwwbbEd{j z&7OJ;kL=xX3pl@f`1H|>)Vqg^fAI{a6t~T6ksMa!>s+F+ZmoNBUeak&yB?emC}O>s zxWD_b!9A2=P3u;?!kS;2G2@cB=uBZrvkxAeExtNfaq6JfzmrSpI$Qql?cBRwE^H4&HKI8eO`yXRwc z%_~htcT#|vX5^t31FvV88f~h_&_(qyhjb8x<>`o(5{aiEhy!0@BkWZ{<3_}Ju&c0* zKpe1)V&c+c05cbpPXE++<=Kih746lCh*pR$Smo9ZxRj#Ve zJ{1ir8YpkxXx1#=x1xCI<_DMLH!5m0qD7mb-G&_UcTsib)yNf!#OE@_GA540mzwT%6=oE&{T~fMqlEbhJhFD#Y{IV$ zul$AYz&eB>hS^zbjwjyEnR(d%OUnx_E4^D zK9TJMo+?sFxTA0756U0vE?6X~jv=~g^tK|(3yf68u}qL$(~oDhbal-u6GmXt$6BNy zVpJAg6xnd#K*W7WzQKV9s7fpzYY3=(B2H)QwLK>aMvvWpA~a~t)x{Uz^9E&#S>kAP zP1dZaX)H=en-2_jH~LW}!!dg^M>cQc*2D5K0m=H}c@GA~6q*n*>~J2Z;N zVKzWWq=qwG~)u4tmHfCqvoD5>&C1R8D1=w1Zs@KUDX?82qUxojTo)S_3{xmgRi1eCtD=57Q4X{=mvJenKxHZq zBnBV+u)~zqTaJae)?B;$gRi2Usq7dM7S8qS5~3-t$t_2OxLc?P#b4@-dj@g-?*6&Y z0{?}%E;oZ;a*BTpZ&j3*G-7z$7A>)Y9H-+OtY9+G7vfT=65CioxoH0}CqVC6SJ1`G zIa&I819pK(jHYIpsqT%XPO5rxqEXL7aiUc6T*<~<=$@oSlV=VJB6RC^|Ki_ei#lDp zjX!WU)V1pCWiwuy&RB8%q8aItuCOsfig*DpsW{`~7oS-0{1k-^-7$gZWzT5Ra8M)K zuP}IVURBp3OFjbr`Y*@PO1a?Txk+4d%&EWIa%VVERo~kgBa`QUw5KOERsTQUc2@UKkepNcNUWf339#i zC*|MjZpg!#db-S`F;-j>jyWpRszU=Kf_;zxj`c zrZYXojN`@Oyh2gWbZNwd44eqOOY8Q-LSoiF!p|_?z6Fr$yYq2d25|Mr=8~lil2#18WBV|LUpmMd zHE-W^CJsDij+z|hQ_Q>uWT$G$dXeUxIdfB1SfeF6uVKhej|B7p#+0QXy42%=8(fd8 z;lV;l*(o)+OdlAK1|vh3z(=4|W=zF`I_y$|nNdMG4V(805>ExQs27%99OrT^n)~2d z_WCz$a=mf=>o;YC+VNB0Lp|CzWBidm!|y=AmO1ue?8WLTaGEy%ozs|O2a<=L*O$l#Sc@k>`nNj(@@|(KH;ej`>vmR|k1mTl0BZAVC zaQj-3nQt{-DFR;oy8*e17K#RVm{Sd8M@zN2ke#{sUO2Cl{@??bmYo|Ik&z;Pj8N54 z_a$X&YW)T)5lzAU47nNWB#lJ0Dt~uWkKNhtt<~UW@+e_fYgTY5a zkkRc3rH(WPRoljEMlh|B7+nRn4$d8>U?uJl7X@R%oGJ8E*w_GTImn}PDw@^o%6t1i z$yMHfMaYH!SWk`L2TqM>fnyj(aU7#?Lo!HLiH(g(A!x2$)wy@Dz=?(!fKiWbV0F-7 zW2XQ64&qdI!TR&_?6rytF%KY4ml7CDqE5p4XR?v%fahz`Y{ z^&pgn$@v2R7XCa6bY<}DFWE}b37WvFxj$e47r$b6DD~KA=0E1Y$R>)-d;hhF)#-lNrp<-RvGV#(zMpevY+c9pht^;#;#+sp)1RQ*#woAPTX&-~iGd>E-fB znm=SCMw0+;t5QY=DFt3Bl@u`|B!{T07S|A;e6#Z1v0;l@@!gslJbO>d63i|3_3u?S z?ZQ(2gWsExf@th1{sZO;d_{FNM&bfKO7()8Lokm3Ie6{S6wo>rm9o;+5#rGu;^Ypo zCs*|-t19LxtDuN5hr9A8H4U0yCeTD8eRckMmy_UTNTTiINiv-QkteIxieu)fR$}$g zSmK(csI{qWd9@a=Z4e{u;DB-$gZfT#oxje-o}|)dM{V;V`e`qRCv4LGVZwfwG`7KtlEmkocUiL8o#9iX_;Q<*gcke$Im3G`zz)(6Y z1v&y#7QAddgc)T@x79_%%SXBIQXqO`QWZl^`h4u>igx1ECHBqB-?&}N?>ZoMeX_0n z*wCP+1!vj4VzIiKX~%Y~IM_fGdiZh%M46_|kwM0h{g|#URQDosmJU9y7*!MS-k4cE zGLgX?ze)2*F}b~GK`kpEr=A5aI76gbh)$CcoTIC&bb&ZyqCu?y>*}@h^5=<3n?Jt8 z!)vnPf4;-Gs=$x^rOKmLP1)cL>eFMw`q+V+cD~6O-@sgZqFEH{;Z(#|UpJ|bM3qTn zQ;)f{Bio1OkDiAJ>~?c6+DPsa0~Aoc;&7X*20yk7B81#XnheNExM` z7dQmQVl(mRXLm%X-Xko0_oh!Kg{$6!;+Ivfy;B~hWnQq!gAUTpy;%pC2i>MNh+BK= z91y>3VTzsREtdgpwg?_=z=seJ+%mevA<|MgI^_cN5FH5eXOfW_R~S=lZ9CBDor^so#Q3%&NSu&V;_4;+Uc*TkuV6 z%8t2(dUA+F6ib%bS(cg;JtCP(OZ1QkR2hIpSrHFVeU(%l3(-d;QLaFSp(O;8XdoMj zEl93V2&ZwX8c{569NqTW*hptB*5j9tehL#WyE^y1d$V)kpx&KRo=54=s;<2hzOM4| zhG^>AMpZAr!g_U6)UQ5I9Xdhb$?Lu_Rvp+M#24EB50tM58FCJZur`I__M{}KTqv-* zm2RRwt*26yBZ6xqgWp)siq}a~3##<0wX|8tOP#563tpFvf121oI!}>02Er4tz#@Vq z6g)HxZ?!Ig>;&6trci>Do?(0rctBazZv|Bw`bGg&!^6#wWtCo}6adCL;Q%F2=EhY@ z$6d2}T@MSo{#@?%yhqE>zrBN0JEl+S&XlmAug}%rUjN98Am6@&TJ~lg`(~si4*FvA zxrmQGPdK|Cnn%mr&S|x3j{5o06A@p29rOBLHn5oUCbR;`R8+@6k|ZI&r^fAwD<;XC zr&3SSWKB{M7QZ?RyOfGsEJ6>CIR`Bf^eie-KvtH8!1S1;1fspL7$Pi&q>JhzPvk8W zkd>Z6u22{LFjkzevw*M6t>}>H>XjC~KXsj_cIERIUUR}t*l%KTBpb%wV|Sx@JNihl z#A#jmQ*7LuU!tb-c~&@`D|=@#(A5>#vVVZC;*r1Lu+$~kY=SC=?g*7;fSXAtD5~U? z@`y zd{N5Aicj{Y{!j~PxT&JjCrXSf>RR9_&*#Jgd40UL^4t0Fz@(pZl~kWn^bLl8MIHpTdFnjRFS!Wn#8l9`S9}p zfoxzC(<`wMHoX$jemEKbM_s3ozb9VG zVa>!XQ|F1;wdvD|x9L2S)-N?3bSx`g%Q7qXd z3jFJsHc}X18)6fEuFK*yAIPS?Aa)OD5Pr92DV<}`qp0#KmiVOHOBdwVC|#A_35q?j zkLenO*b`|m|0u<*f(iMk^pmJQw;gU|SX*E!4C&>=kP7jS|4>npqo|GiM{;<-xfQo^ z;fGbK=cL~EK1Swp6jfzo(MW8<^&jOaS&x=%p=yz-hKW<##hpaYs?VfnR`~_! zc_4l%6evyEcl|j1~z&;wDcKzq@krFYJ}1+T+Mn9nF(fcT2sr^ z9Z|Ak9H<2@x8%7<%?*}}6}$!ki^OV4Vpf4zUXk2Kns+L=4+IJF52)=S>{&0RyQ1(* z`TFXu@ZgQ&r;`cdsTx;BcmzOl5ufSeU*stnG5kyZR@|9klUAy+-d~=5=)J|klcz-a zUwZrBVRM=P#OPU(?2VL@rD>vJj!Lbj`VZ}VO)`eDVlz0I!Q}0EK-x<1o6#H4Scz56n_4)h&Z7zkcAssO@hvY}b zDUlV5<|u9DylHSeP{EUxwa}nZu4p+QBnTlP5T}xb6c|0_gh05#s)_?KA(#Z2XTacd ztx!qjt)Hvq)VS8C>$2kJL9RJbSeS=TIkW8*rK*>Joa5432xg{luZ-6uEvN%=gydABr_EDSz-zEjMU9TEt);N$>}aRrld6 z-K%>ou^U@Nvr0}xs8WCP_Hpoao6$Fh1kNXY6NsSPSSE zG`gt7UkILK2Gp=6Xd0%sW^5*Ooj^<+s-1Q9XfZ8eKPXN1D7icZSR*)muy)cWVdiqN zgKTzhR@`#c=S&STR<65v&@laYzx>^O7htuz@U!eKxiqD;YKHOR-$Bb%(z!_&jm|fC zFP-lgU~juD9S&`)V_T3a+>QS@-w=Q};sMpS?6#hi7{76#iTP40lKnVO9r5KiaSg?Z z#-ZKEw3%!>%s;;ajJTF#2XTdtGVxpSq)NI#2>R|Qf%pk33I0PJ_(v^8zRe_c8@DA9 zkt$c&SZnA9`*eK#JDf9e)KBXMmovW<6S9|&Jl2gMKDE$?4wfm<#p?Xx#7bjJLF^6Nv`{_}J ze=;!U0N%b$?3%*WI%YChoaLBEsha=<>^lwcFFM+?BNTR8%fO*`IqOeckltBYx_$e2 zajL0*b~EwRc=iq(H~o4-R3Xmnl~ZhY#XNr*>ncw29;(Lg-#Yjvc6vAXR~bed<9I|z z)fY~%Az5t}woTQS3D%~}l|*dHB{Nnd3BV*`-4jF+wM97Q@h6sfHh2ht*3%9Twh2Ra zd5zXS00+RN;ary;u*eaPe+&;22L}6skVNfqoy1dJ`dk&G*+ZMylpe#nvM$|vea%*g z?fb?3Y_Awb|M)x}D|)kI^v_@J2U-YoI3cAX;GwDQxJyqqaiPJ4Y#tgjbrv?7ZT zw}CuD{RyfO?1gl?TY5R6N0g;m!rq=VshbmMQnlf&*GNmkw}H+eY$>sT;=x33@Gg+T z0fE(6leN(W)F&Z$Q*2O^7GskIj!^h(uYcC`a+yL=5Ax8c);pYe8NMRPB0fiK&} zR{0kK4K9LY{u><_Y>VWf$~~;*C2@L=VB&>Ktm7QAc=!(gXt@2x1@t$HhlN6a3$|$d zwu4|(|82i-o@^aHZ>%6p602392g{fshC{@HgCS{z{WQCabklX4d>yfo6)ypJ3^V~t zm#E<-|C6*XklyO67*_-$396;LsLkhT#twxFwnnCxaD40ArDK3)Lzhiq%(N z6{mK5)v2BEzt=Y|_vwZ;OQcn}O6(Eu`!%MyGFk9ec8X~9Zd7=FDLF`cUA1%swRuEr z2^!|f(;adttO*Gi3Vgs$r^_dS_*n*@#9~qT`{krp7S8atY2Gsfurq!5USz>PcKktv zaDuf4*-9Jo%zTStjRcG%#L-a?`oBtnD#mYN!E~Udc_4XKfcH21Y=L;CSw;IQ)(<5F z>?y$fnd1)Z&GY#6NJl4KwV<-u$i!x|n8v9d=9b3~!5)j>OPZxPW=1D9=z+vU{}!iu z4(V{SBEEoiZ4%mTY}?5r;GbU~$N5hmvfkpDl!**&jir6;JS|rPz341C1vWWuGnW5R zI$DQhu1jh__aO4se);=Pxg@cWhT9okjU_Q;qTCf{1;!EK7^H^;X!d`V%{FTJ?^{G( zK|;l2mW`r8Ig{jO-5hn>$gEoyklBu+$_i~V#z(QzQ2}cc$|~$EL$cUpwiGjDSvo?c z?-q4qklt1U9}Zj#a5o)0d&EvA)Tkt|v7Hc&KEVC@6y@A~mqtc;pAfg!iumnfNe+Lh z<+$Dx`H&vF8Y_9q7=8AVXD6a6j_jE|{&)G{_RYHh#rH#0_Ce=xHpBQcIm&Q>tNN8% zk7~)JF49Eqe>8f&RO=#2U|zPZui}_NPMLdy+V}WhZ7-pzX zQIos}M8z6t9js%^V;}$Ozt(F4A6mS-5!<`uxjRVjAl>sraSRh^*_mSkM5&dKacv>v zl7N(={5PMmS$kpi+~}?-N@-dIr&7GGxX63wYKIcM6U$s1=)ntTDeNz+wF2+4Ww-P& z`2~7X4f2VAj*ztg6d!M@w7-lbF9OkrtgG!~9fJ?{pX(Ff=P)H?&Ct6-#nX{HZa)0X zw^5;;_g&xkrl?fe=H03?YA*UcyyoOX`?LFBnW$>tonJ3Lcz-5Wzj|I;A(cNkN5exN zgQ}ecda_Xu54%OVjf0n$s%~*;%z1K$ak~A_ehCfE!&aRL>O37 z{=`YKE^}_Zx5vZ3T=v0ywy~RxW%XXro3(t8+?Kqi*q4it_(~&IJ8^wXq%x;`DHAyQ zB8OeSsd92#MvGtI$C@u=;(?H@cJdId0mOzn9loqEtil+iQAp@LXpiQZv++#x%*kn+ zjs?U^q(ck&30Al>79#T#Al+=nMmnR!;(TO7K{-Rhp)Z`r7PHY025a|RU(epXBu=%T zcZ+$}^38?iQa$Go2BG~MMK(~2c8`WRt&xhLcyjsO9z~}5H>2SntNCf z*(QXa-Y*oH7N2Ey9m0f7SqUM;RU-Qkw5Ec@KSQY;gdx}M^h`UlPLn<-X>kM|eH z%sCl4pPUJ7@me@1(a`=wEFJ%ByxZoou}wpoK2lmm60U~TEK(|*`k5^g%U>4vPO6u6pl(4=ViRrnm5)AR zy8mVWVYC;0vs@srkzp>f zw>z=Sy#dc{ga<#rMWI4JGh!Lo5~+9NkOgMdYA!VI?c(lS{#w^b*Ky`CrF)3Dq_`#Z z8i}FBfO+g4XKxiUt6clA41d5E_ z)qm_gdf<6+c|)YDCM&s>a>wduHFDGAgSc`9Tq(Nk<;=f2|C9JT{mOA2dM? z56VRm`vw+T*5(##Y2w; z;cn)VW>Pahbi2!}^o!@gv6kX&QvPQII>84Av_UIMh`D^WvuWB>axF~?HzA0ECGe20 z$ncVFzr$qLH#&ka!VPvqmbsZ3`p`FY43EyUCWiuU|0mtL7u48G%_`aZG4OzSEPfH&fwGAKlP2{DBQOU^Y8{&*At;DdL)$m92gm@S@QBgT z8Ky{&aoGoQpT8IjW26Nf%#9A@j~-6uNYs@NqrQ6q_Q+P>h!o}!HzQ$^NQ*=*!aCk= zA<2=L8!uPX^w3nfT2+UX_DBi0bXo;i16n#478qcrh6Ayb6_bH_zy%R`J2qggm2}lVgARxV00Re%~LLi`mBGMET>{t*8 zf~(T62zKnQu5HD(%B~$zR76xPWbXGn=bf25H{gH&UwfYKc|Nl{+?jjsne(>up8i_< z#86U^k}VbMd1&CY8LmGHYjA-w&o87T248!Yy`DqnpnH~2P9}>OC=~1w>|yAn>V8m1 zaBz?3$QYbjoJ?E-32Pq93N2eIpawr$Dmv6&s|BW!f@6z)8ZA`v>u4|Tw0`g1-PpN3 z@|>nmxL6GTY|AIp5;Gfcn1FhRzx}ko8&hv@DV~Y(MUIG7ubhAR^-;flE_$v>{~Rt{ zZGB+J=UlKo)C%uFiB#J0{3#0Sb+Q;uBV7(23{TNae^e|TNsb2cIjHQw z@z}#qutjfGXuV#$#`InH!<{dEC;DvcIx!+;)4fG!tn}9UOKG$w zxAjxZ&}-U>d$a-B`}-{`w`L8C(?!qU?MTi$tv|Om-({V?O*^1Xy+T}Hp4u=%yR1%~ zITIt804_ES?ao(D`BOdC&XARu4_7Hwzd%quvR(60xWThc?$1LEnhI$Xr)>H6(NSBI z%=sv%dFUWtN&$W0Srx7f>)jwm10b?Sqpd=10&kB0h;Y6~VuVD2=F$ z7#u-gm<#;-B7j@LWj~@9B0axNL~KtYVtcUKJuAXLiClt5Qr;r>U}cI}0GljE5I?4D zbokz~z+}s)FukuLwSq2o*ir||G(xCP0b-ZvPs=Sb^NMfCy~`K1Gw;SC>y>Bzse-u; zn^q7Zrsh~r`l7^;9BWlyb!~~j>AEeALwbCvc36D$ovy$Bq-Z%=+f;H`%goabYaQVh zMSnL5oYD<_C;Iz!zwq=o8}Uo4gjl2=zl9(_e=J=Bbg7KRLGF|2Q#LNt@)%Et zz=4e}^2%WQb^1{P{V0KO(B&<<;5-lDz=Z<55OyovzWA8yM~SI&Cb@}jow|_cbH<#pK>H%Y|C4j zd`s^2eMQr^XI;@|?$Ci#3qxpFKchBltrmErbmeovE-+V|M*7|CXJ@baD<_45J zwO@DbF+9N3rI-}ozw{kg!SPtP#mXUnHrFvBk3H&%;EDh?TodgbhJ^&Dp9?Ur3!Izg zxM}X(WaGv?`ZW(Xd9*#a7Y0anSfWFv?zUV_y0{QQ_hxOrrFPJ$Fe}tV1xj)xAvd5p0%ZZTW z5%NGf9-%~33y_JBzVdQx;?Ofvl6kTeVUcmElDt#H%6j;W=6Ss`zVJP?>ZbcOZOan% zl{;4KR6p>IDe2>jo-$c{q_!z)nxmJC*-KSz$z^pti&xaF)YKamBFLsq5!}(R6`jRp zrIvy-sbWwsB&Y&74R-`>zX)f&iBeF5B=0xhrtnGFAHpIzRVWNzP5R?7$^?hci%SgfogTuUdW)4_yr?zhn9>S%-MdQCkK((I2*qNo0hG$~*tVx7TLPJ*t}$0^Y0 zum}5ct`=vTS>Ix~eW;d~H1n8_d=U z(p7!dBvnN)nW{vhHEXmMur6dHGrOr=SqQRqst~Y1abBw+F9;8+NKFyks!e{Ri&ZDCrXX!_mzoy)^C z)z`Oml^LzIc+}`Osq2jkmu*M&pH1!F{L#9oWy{pWo|@XTc!-o9)E6;2rcx&737c`8 z1~{x?KP)tQkTxW;)3gDJIKT%bPSoAjrN5!b?7}Q{vAVvYPMxam2R~QPSGQsfQeU-% zT`56U9fm9h_JFHX^^3zsoL4%!jK5e$-xZf0ET!UrwTGgLGmBdl;|v>51qZ)c%)=g? zR!nmOQe3kjnY1hB1#dwvRS{sJ6H@{4c&I`JJG>0}rbJ}7hCsV=92c}|iYkWRI{t?H zH0`e8=)G%~-fLNAtL*$rpIMw;*uAB%Yj#_o*{o?!nR;=}#a$N+nB7U$u9$Z9fUc9~ zE>{;@1M;$(7v>hU7E`+vW;e}mLGz~sRV+4fpJXZ1{7s~cin2J7_U^`NyVDoJ+7Q83 zpf-ekI>n(VBhzSQphLX76gI6UL1?>3gbyAu$#hUeAuq3@f{H|7R{6|z^OqI(=s9n> zuD`Hy%^l%dto6sQ(XsEeY#FEOm94w@qPH5o2DjGL^^2D8QPoFoY4yQ3`~PS*qNuy3 z_G>$iY*ZW6_1a>td7ADQd{nqBeHz>r(DAf6j%c_jM}0+Ik257TEvHNiiYP#$lUJ=g4uaEl;Up3f@c)RuKYyhLPthq@{)$KU7h} zr0GB#Qm(0PKs~-^Pi|6Ij2^j2RhNw$e#H_aKfk!MX=D`?cQZc~AACP@(cD497R?xz z+wP*m+%jpo8-O(DLp5Eup}={MVOUL zHmRVTV1KrcVG%~!HV8r8-`R6p6nCI!@D{DhBQZ)ZDlEbTO--e(5_h?fs^qGzVEHP) zq9QdFUT<0c1Dw4m5ds%RO%9t+?=mwtTurn-`e*lnnOao;S9TsUjC-t4HeUbL98LQs z!!&yj7&%(k;)M2(7Oj_w1?TSS6d5&s`26c4V@G{7)a=u_Y6{L~uj~w za^6g^zOxP+^M}q&OjeD$%WJ~c@6^?QKiR!9UPOs9L*Kd)AveE@joUQs%0;U#fw=PF z^Jpikb?TNuCwS$OedNaTgX@cfxFl{2&SK$D>5)*m5@{vf6+ zKll{$1xvx;UDA%SN%a{tSEyxlUIPo3;?QL4;d5%U@6SeuZQM0|QcS$+L$TJq6{uQn zs1Yk3cy{WFsOVlpzflVtKElyapTAVQqi3Oi#iFcv<=LX!%Hp{#3$8RooR~U%&@`?t z0UNDitVcT$@gi3^FKVY@ypcIKAp_``W`m(F2g}S}jZU*ZFX9CXgve1Z``dIhLyW%c zO0hufZ}?NRw60XUH$00WE}AXA zK;q*@vD})fE^WZ88#b%c8uA-jIX!eOs>U{^h6!-`hDmdJ?JuY?B{lLN87aoppnrJ8 z#7%?IJF%kw9iof4(W(#y)<*G))yYgluENrWGYv-}uhU*pjJ*P9$Rd|Gkx$|zCEdyX z35g1FZB~eb_4skgC>3Jp&Eqs@stT3Js{rndjMv1VJfJ*nN$aFzPff0`eZ^~quNoD1DN0~Yc$kL=F##L=|ftt=*zl?iYjk135j zHC~%xhE7ZiT|{!2n&{*+l5xyTO(KS*=TPMKcPBGAaQo;v3f%U;pC2hUjHKyxF!NKF<3|TK zrfvo3ff!G~pf84%ObnAZ*ad?aQXmzhCUryXdPu18r6Sw~e5@!{vN4R{uCLDc!e8q% zMD+!flUEhoJ8rV3#th$i7my!G_&og!X;og(9eU(8C+0B?9nZ>N@-VaR<;^oQA5 zSvaVX#X3xtR7k4OJIlqohdMQn%s7#fZ-!kmXL9)s_k2bB>i7CH+N0)WkhEe^-5TDFDZO~2cCwC*}hTd_c!V=UNr2tC@6qdytA)m#D# zRx+w^HliTfp@7V%5KH0xM64XrHz3tTc?bbrv7TFmcHr<2P?rP?DeTaPVw19xN|RuU z;KKaf<6{%D5=s*)69y+tN|>LpJRxi%4AHQ^q$77Htxa1Eot9e&D?<@iDa3wQl$~q2gHy32Z9t;<)nVf7;Rnw z-7rEK$>9L-*f_(|SlafR93`8MK2O6sOGj1uG`|j`9z;d7I2_S|wuFBu)|cfgMe%PM z*;HSVuLyM&^3?2YKR&x?+i7jK5Z2ev8?VBKuyCOe*9+?hk}334+G8QKz&0rCW41wy zJ;<_)@m>JENb{51EgnUy@M=2cwN(fRK05OC)*tz(st&Pr#E{(2?9vnR>8dDy2L3@oM96#71JU@`)xrI$2 zjZD$9VHFVV3*`ic87Bp;wA3-mzGB5h0kLG+s@Q+ILv@rw;Faq_OJ ze5XID9WFAGJIxjFyNQWJg%Q zMcP5*we9yZJGvut?3uTpH~hqnuB#z#^ayfcX+*yRM|{Z_1&T5d1tYC)(7G@>MBe|v z9?$2EOd5C)SP%$g7F3xuIB8PS{G{bcVG|D|2FK4IjY^pi&C;Vuy7Q2ZGJA-2#|DPf zYpB>sKA-lI@sEe>IR!WEi!?R>4eI`?wbY}Ha- zIH2G7^fq(`zzX$xE#7z<-osaL+B2OJ!5bDwwUg8skJ&?T)#_b16?(6br$W1MEJG3; zp9&4QnW(H9X6yl64&#fH_*gIi9G~BFjk-?#-FO}_x!!lO6h^p`?RTzL@6sw5SDqIa z+tVCe3-La~cnY}k875u6j}2gi3kGwn0je-ht*A$9BN)2C`(_rt2)YTuVfVZwaKh+k z{2f~55PuYFl@cu*RSD4D)M@L6Re}a7j+40AiW=0VNLb4Z-trJa4_6?8BqYEWq`<+* z+9FJltUT(+llNL*?fTIArI`^mEiwi+TKxquF7m+GX*uWkXuM*FtUa$fecD6YKwc@ju0- zPw8+Kek(rqK|*`CyiL1F+eZka{l9;reGFPq7PO#9$fkW!lbB7}kiBAU*~^)FJZ4@= zKg!cEh>)HW8QZjN3z<=u<%04^*3T!^x3K7(IQX2%y)ju&{nWbe%|2WEyZ{X{DKc&| zbRl6Jw4Qk>?;qCT$MpJhbH&%MEuC=94aoiA*>pX|AOTj}zW!#?!i`v4ryd}?Hl49> zDWp@sLMaS6O5DI4BBvNSx*4iK;;bnyq>Vk9=TU-s<#Fqy^zaB@#`~h*Q@4MAdwl$q zPpuC{xOT;&{a^X?HggBhKUAA+h~__!9pCOHG2$6rTm8r8-PUI7i(9qX+mGPz*xlCe zQ+7?%^jkz4@xcB8%~)Z)hJG$UJ9HVT0{>s0-P0U-voqy&RHzsI}e^)t$k(a4|>@N-}UNj=IA-yw=)Ns;(^1A|{K-4+Ja*ls(i! z--bAlfqu;s2XevW3vciP>iDvZ7g{0{)rIIOy|V(5?&SBfi(|4ikBVbHd#-uDnsjOZ zu!zn*`%H+k4uy;Se;+*&;al$?bG`WKZLy-?)OPK=j;vB2`$E+YZl3$Z72T?_fG8er zo1TnbNJF-zQ%%Iqx?^nx{Sjieurrzrd6YH;Sfn3#HB=08YS3EkPfI1y47?AK`W&W0 z*2hA~S9Drcc>&V4dRTo+v`cfWJ8%12bkvj9b5A_<=CHN*-uREiX4ZFCXL1$a7p(fG zzumX}r`4J^{p@&k`-=B{Vh41uH?3oOoPIOQ3L`I2DVDb=XFwg@DE2qNMPX9vr2iro@-1KL)jwKBrjp@7m$V*8vyR2`v z{p;mr3+}l6kgoRa*{8o)t@%LxH!X3>`|ls#`}_5pzVw?Zs`~Wq!K1rim2^TMZ$c|f z)GZ_(fwn(h;50Ej30=FCfkZl5t{IVrgP!;^4$diI9vDLM9aeWQeDGeum}L z)N_jyud}h+JHj9;@3q63+#w2H3Ir@j$j0mU-}{*Ha<~3>GCjk&X8qRg0TFhy5OHv_ zSm)l0)6^|d>cd+nwz;IbIqyE!^IRz*9aWb{_><)6#-X-O$!$=scNuDj!rCljN${C?A> zkEbOh7rkJ8`_OfVN6i&t#;hDe>(hJuHPMEs?4(`48|9Wo{J(BF3khwL_50Sl&sz8G z(zI?rwia*b-5>8bYrd-1&nswg2||^HbsUvqSBqk-3Y_6cF^-Pyhz`c{)=Ng_2CEU6 zz_3ab;SlVU3d~=zx?TDY&t5#KxI^nDS!JTwdiAB_Y62qK)CWXIWH{{rpYKGwojM>N z@^Ii~aHsyXuGK-`)BV?2=f&WqraCa4@7apF4fmG>NPg~&@9)-c6efI#?%A9UBVgG+ z3t5=skVR#x)6dZ_x&%(>J&l*aIWqpF`@tB+S+IYa>TrYZo8$Y1khD$2zta7?*pE?k z7H{a^`+{$<8mzu*9?tMGmCf{v#`lQNP}F4oBGpt-1`2IjZB_GqA>HFwJHC#q88;-VefC)=?$Y&5BKB`F&x)d6C7SW=-+Nb1np~%w z58qw4aE)&4LnSFx$W!#|A<2izVvaz`+a+~Agp2=a%bDKL!Fo`At6^Ph?OFY#v*oCW znmM6&h6g7e8Ge`hby<}g(ks;*iI7W;)jP*k!78vb*iMh3@)9|0{E_?*>v}=PNV=XU zh?O=MmByG1x0>v}0-govN60;TS>l?bH*6o(#`*-lo_@1uU%&IoFoaS@Trs)dh{9=8 zmd1yxY1=Ieoa2ToOr_y7u!_%~>CtJsrVp|@^;5NbZ=Hr-%R;*nasF^q$eF^Ew-|Dy zn-c>P!WQ*-3TKD)D<1!#c$0T7Krjc51#l@KLl0U8k)ND{^bLx=PZ2-$9UCJ$L|p%$ z<+mQ48mlGLTX#Ku%z9(x6D`y*E#j0qtfzX#Q!_Mu!ylW{wh84%UH|r_hkT~>W?Y|k zJBgbYfa)iBG@8MqDNik+Q;o^ybx_bY5YFEBBRG$NnbSC01 zSNd~h++}W~SWBl-JjI+FCv=Wm$U7OBK3YgFjsqUy+8-NFkNnB0>>EOCoM#uKb{7U` zB*Y8K!AVK2Fflyu68@#N>%HwZHkDFFP`n*Ri&=c z2a4CKboKUC=$RCZ_;8$-8w(!W+n*w}U}QKeHiu$e%jpbfxI-N9P|`!v7X;@P$wQVC z6sq;jtz%6cW2!E{^`n_FYT{1ok9%)9HX}A&49zuEbzq+|MeXy&4X;Y&&|Us%=?cSvGE#s{L&(w2VF0WW6(18F7yqf0X1Na469E z(ZpYwV2Aqi`3MDmxSKhc3dWUfMv<<~$Qx;ZLG&cex_ksKVg%rED+`DV`!sGZ_{h4W zYc`C&%tF~d(KdeWZCjp+#Gz|FDyQ@xUO0WiH3@3QHtP?tk=d4Md?aREgOG#gx~HOT z`e3UIs&#L_kya#HI7c6f7B)i*Lsz6ry3@iChqtpFgB>AfZle}@g37&!;-LUnEa7_%>d_8+jx@1K2$zw4gbG8e8ou;o|EdD4&(yXq2 z{baW;<+H9(^+Dq0zN&i5T3VlZfs@e6M{w536@EqfF;ne93~80C`)A0y2pOdZvocCE zP)+Gb2K_%iF6-L$YU6)+cqKTaz|ls%c5 z)QH&4o02z^}tv+>YGIot_rf$9ePW}r+VHhhpFW@6#JrVx&g#CuLzKiIry@#<9Yo-d!P?3^OPMRCJp z!(KAW#r%hkpK$QQ||ldCDI;7IFXp=kQ;XMNtebFM=7yi}^MA=sqQbVCv<+P}M5UNtk zD{@TJmzq;#f}LUCm1Z6-rJ@L5o8sl?elU#3#+B!5zR$MSzvv5pVc7MRF(NT+%*ZX) zWBEH*`P$^R7^v&5b6Q`VQaFBf*r4$EB>it2?x|@vYDtgPyH)j}8}41u+COycf&-U4 z)v8LH-l|7pTI;fA1r-g8Ma)6Hw8L<+2mOZbnFT!|TIuGG^5mC?QmBF2r8(M%&>7=SD{KMA?ozh4$XAWlvrX z5Ahl6giwc8b?u=c(s5tlRsAdd1z3EuppjPl3#Haw$aXSZo%pkmGIfsN>`zZ%SviCM z!2o*0=lr=oN{RR9@jvjS9ViGc5n>n6Fi80*@)c1zNf~07ir7RXNMdit(Rwo0PS2W} zY9#KqPH)|L#k?agPD$0X#n|7rT`}*ubJs`fVrz@4>f4%%Mjb?zYVMtuRr=_o`?{AbahajXs8)nDc)$M8ap1)`}VNPTB_RaZNx`melR*?)DmHn zxy7$>wMlYFJKIJGe+V(}hFE0WP>*D3Hcc))7?+%#jCh#DWIn+o5q`u(&ClJU*UN){Jr)^DCe z529Z=uB!1uiPd(*;~=?#_5lds>MO))^{o0U^!<3~_oz67(Tau^NvAJu@W)F@4V7t~ zUC(hJhB9znRwUC)560?QIyU)A9U)0u^?mvg9jQC>ad$v?OxiLw1MY6h>p)Zqc)TZj zo*dp@rw7!`Oc)#K7;~_Vb~^buFui>!J(1cjfyxdRX=T~-9>{rMuGsyw$mm>_OHInRnRCil*RsZJF+g#V?uL8S0~svD6Nt_UYbVXW^~C&V|6$qq*{t!jpq#oJBYUy zB9Q5e7vj(dX(NBso67xA$86=lsh`Z3?Fxc@p zW|?-p;4~$_22R0-QvzhC+Mw-m9&Lu6{S?y;5sZ8+}WuHKug4 zdbFXndh`gwEn}_I)@g8u*8|_F9~l>8buITtcqDRVlRtGhTiOfYR^u#P2o46w*P zZHoVeQVc?k;7&jc52c2Y4g`}w!VQt;l3Gd~PM&&nI1A4{(irH>20rW;T!hNgm#Me@ zJ;T?eJh?~O@JsK$D<=D~oI~krny)Dp{Yv)~??KexpPO+yv@m~ef<&!NFCMw`TM=!Y z`Swn-8E(K_`qWph7Q)l12%l0V+KX~0HiG3IvPwlAdGzhqJ9eItZhG z=YiRxe0>3ou^#Aed>!5Q??mmNwl>CO56q9gco(KxIR}hU{MMvn=qa+t+AX=sg}Prn zC?2^Glu_yXLPDWVMv=HuXyC1VtwQ9({2kAWz*~LFm0~|zX<_`;-{RI3%l=Usg010w zj{3)LJ9bb-f5!t|Ctkb&bc}2lLwj*=fyc@1dlz?H2sHJAq@-GTN%jIbbMi3`enLCa zu`$eM3R+8g15X5T@)9C?4I3w*wGAHVI_26Rv>RM)dZ24P&|3m?gU~MSFc0)%9yLix zb>KQlFFZog$1uy;ijE#LHuLzmV|sUj%k%id)tY2u>L2*V=+5sgv*&21;CtB|L2b(U z3+Or=EzENiF@(8)0kcKQcqwBwUi5xMa&9 ztX}5A`jk2W{zK~?l^|MqU{YKdQ`rKs$Bx=^@p)krP0fAx&erE_Z0$YRGK`O1i0VSh zaiI5=9YZ=3^AOpB(Kd`vxnFrlx%xs-`Ydy=gd!fI+#h%oz17qz)c(+i;rYcj59z}^ zqyVRVAtM^k`^kP8CY*M-b{J=75^RwROZ}3Loe}+lnBP3v5?GW&@ohm$iQO+Qz5*|- zam;;pb%vn!09%V=`&-LVjseeTVtfY}_>E`}2-F6?b1?F{rjc`a2B5V*F0`rCb#!5w zV-T#(F#^8;wuKApQ?>*^`+1lhju8du65|z@j_SZgfCjZ*j)5nN-jzps829GR(LBLl#W{!AUPIU_>YDtI1Bi0WQE4*`*XY=4m)kyh|=&19;^$`K*i$muuNmhSM! z#?W%g?oWG_+kDuT0xoj#)r&&#H7TLk`!4Y*1bOQ&mii$M=V?upL%Y}j3-PCrL{lMS zFY+hL)G?gTM;jh}*;#9{_9blJB}zx7-k;v-BC-P_ zLZr-YSpj^#1vDAXYgq}tDPbcG+I)eQm9U;3Tk20wEMTOV^6;_-rC@(fI(^YSJSrzM zr&Ugu9As4>a-}o~0YVOx1#Qd-zXN!DL8^2bBzkZ-iWRzJipBiXzw) z%@l2nw~N+?&A;cSCvc2==CAXjbba)kbnB3@>WgcaK0YCGsG+9{OP@XE$ld)@-?X`-z1r`vIKv=~|Bad-|Js2nqtW1i+U%gQO~`At$-k8e`yKhlIuZ5~w< zy1f=;I;1yqxEP;~hN4?(!SA4Yg7M(+_z-qPheeUK8zCCsbXBh`ea5@oYOKU_+CNJh z)|9Jh>MU!g^^<2&Egfy_hj#r2#<0*@sprFIlc%&&?)4Y6E+M8-5Vzksb2db=4l!l$ zGDyYLhOG;=c;K7l@UW5jKQF`?9zuhJps&S#xhYQ{SQH%NUFa;{?=ASt`Z_yKsNOujSF182hl zj|YbER52fvcxnQ3f_%``$31epdd|4Z#aI)_hnL14zw@Mg!aa${PvWi*?3Uw)_xtRg zAsSPSA6<;~foYJ=C5<*r;C5@WI@{O}7~r9pv|q{Uv74I&2GF?-Qc6q zmharIjF+R0ciI*xB^`UhNpMGr&!?2AQ&`8+VadP3IE&|m(WeYjF~hC8qgp=gL#73|`JFATyFtr7%cqXvH47~C99@FvyoVMTeqd)>#1mSUe!CJU#alg9@@dWR}HU{;evkQCQh;?^O1G1+Tg0F#fn(4+!Y!_V{>g5|@=^;y_wDi=e$-&mM z-by23k27m|6Pa$(@<_j+zQo*up6A#{RIKXF+-6|pHt*(D{hj603-#f66MoCjvz#`A z0+Kl-1zSaWkOG%`I+jV&GZ(WALli)Ufx0H&Z|g~PR-<0%&`9r5D)i6MQnpv|x-4Vb zYm}2%m-sDgL5}>xIx1^2QVXnU&6*6fKu7wKRxgrDB$j$%OGv=tSOTP`OB_^Ush`U< z0*fQ{5teUR#f6OTz~|TJ&Cw)IgxDiz?>C4-48^lWc>#EAj^bJ)9lI1TYCpLcv1fko?wM{Nm5bRb z)W0`gM*Be_+o0j3WPZ|A`&b`>=b(z8M?!}UC|L~+UU=+kb9w&%(s0HLk9ql-wVL2T zxyAhm{Ak@)0FD^gO>&nNC4*18^xp4zM+Al5lkza^UH8#lAx8ga=}9=&V*#K{|V^`pb;Qw`mlrVZ2dQR;7h zzKnHpAC_<_bH&a)U$%%_f~(EtW{$)&2W#_l=4Rt1wDK`#gw6Y0`N+|k8+#8z{9EUF z4?-=9;FOUrX4jVC?EDflc`u}2!u5s0dXR98ebnHaAAs5>+_r8^ZJl@~*fv*kcO(u# z>n*u$a9c3%ZyIcyYjgEL12@U468Cm-x0F>8(rxcJ^nTrBYl_6UUHMIFCZRCELo$iP zvsLkMJC{aBCLxSf%$*4%xO0x%M=pg9>{g8IrM{lPyh64|{S>xsd#qeO>h!1?IAJbg znRSf~jdR(E^CPgJhrNU9fqLU=-?hNF(uPR`FSjwe+9KOAFtefq^YzZgpMbtWLW|~s zKOJaSUvzffd1x6T;nYHv8u?5B=r>4#NpC7Tq|dRe|{>C^hDL1S}Lzo z>)%I3bhv(wU34c41$Ef!9r%9*E1j&N-$b;E4{4{vSqpy#mSVoG#`6wlGqgcUY*os` zh%mPMhg(bmZ;ln3pqG1~>%@W}w3BJ%*q<2pWiB*U$Hen7+pCmeCz;tc9j3DL%5+;w zb0k-K_vOa#E<|;0Gve!z5 zPk0W4I?F606$5>A9B+>1c97icJQ2}9~u#PVbmH{o>g(cmx$ptXm+8<-iCt7Vfssn8RjWL2Q?0V0+hn}I47XM*t zcPi_kMWfXo?|CywNLTZF|1Dbt>E5N1IXqDr)JsC4>%?3@O9?Q?<^!P^(bHqk#~0L4 z2p>^MZ^vA$WBMR-JA8t%aZ4pc^*nATAetXT{Ych4iqVD}xDDWd;LTOat-&_%rb}&b zB(lcPbz-b+10*Gf--W`U4ZYnqxcD5|jA5BVdOQ4BDNS1Zr@)EDJAg$wZbQxm8@@-oCFr5%V1dTsm}{QP{OrHDg_6S&&m+;O!OZEV?J4aF{n7J+t#&e`Q zo^IxF&}%}ltzCTEMU~Ql<~Z;<^Oo@4Y!=us*lFi-p98|i!fUUXA!x?}#72gI1Y*Zn zNM6UIEcs1M8+Vj1$m{ZWlqGC^4Pygua`tn{If-h5AiGMoq#7_h^EsY%GS`hoRa}2; zW8VQyK$1)U&-f0`a~pWP2^JyT*S&O)Vv zwD$fnhkIT2)7F0OIJ&}<$dZWnp^h%P!q_2C&_Nz1^20Fnq4+@ePEnyUC^2v*bkd*EK4_TgWe`zY)!@0#li!65gllpOP+2`DITa?d$+opt-Ko4(5#I>ov!EgDDjJ-}M$Vgc{4cjc|h znjjW;mO5IJ-2Z4K<^ESQhCPDV{~UfL_gfl0f}pv1mmEF7IP(`UMyA=+g?T{v3NVlt z0F!f!%R&K>AvnbyRwK23wv0uz71~#K9(VSvxSIotYk{)J)mSVQ-YS- z(PhZJj|fWWG!ul++N?w?{vk2&2JXj2M#Zos&zK;|6Q*7+HSRb$mZrfL>Vk5=u+$Gb zR$J&e0Yyp@B;iQuVCNK;aM&s%-y-D)8es`hQ^g|;2**MsanJ}$`KDf3C3_Aqj{Qf% zKpwYY?gzarkB7)1fHJ~(9!N|NDE|Z|%mY9LZ8j~e`}K{G7qU>pGul)2E94v+&Teqb zG_ElNan>lwz+P1L7l$UhP+~J#i>X#aW3Q<^SUDA$oE3@s#%q%OxAzQsN{bG^kZ_IY zf3=}UEZUy697zu;n~%0E?dF(h26RgMdXIXtA+aE9hGrg8Pon3q0-K8Dx%3{-w=JkV z;*K9%BWSfc>nv14!_3QaVcnJOj5Wiu#7EK`B)<0ChauL5*C6qYCekH*NHp53S!tANQ z(~!i^Eku6+{!iffil29#CgC5T=Ygm3JfELGVZ+bl=bs0D(dx`k@ZJ}=NA_6_@Q0!j z4CY>wV-($vLf*}!cFApm71^XVVJzWgg)N)z3*CD}LEs*3!#k->P58c>^SyB&-?N`j z#y#^r+9vU_Z_Y$ffBWA2g};XkX@5_?Ba?hVxMy#R@ha}2yW8KR>Obz=^F3Ro`Mx9H zvjvLpyV8B&KG3&_@4N8#>DXcZd-U`F%8tbq1p4s!Kgs>NS2>IEgS?vV#>%2T{MUMK zhQSdD*dz8TCtMhtp9X$_mXOCV&)G0d63S7?k;SR(HjrEKNJ?JtyCTLg_M*C)Yfqfgmeh4@&LyjAG4w?NMU z{(dokKMe4L_=8*FVfNeD6EeX zgZw*keNcSsNt-vP9z$>;>$=pxJvnMp_HXqdwz;E!vyPzz5W`5nA8`cqB#$;Q+T-MC zM1&`WN1O1_jCfPR0Fx61-X`#+*^^=L9l;2|5*|cFJN_P8nQw6%zl=F?ZLMCr34W#M zkF%}y0As4dVYIGSjBQ_J{bvadc18A~880hEI0q-JIUT7q|cWmPFy@B66XW2`1tA%zfI8+Za;m; z(;w^B>&CTvGs49wb$Cy)gv!#0(jU`yG_2G0Z(bI=HPd?U^1=tH&FpPqUJF?V_d78U zdh`8+U?2U@c>~mM+TYn@f=@IDIcmUb?*PCoi^1o@I;z9LpR7WKzTq?xTS1 zzefL~Zdf06fN>UB2Jx@LLHDyv!)Zx_n2r&#X>d0RN0O30CW@Cpt7)YB^GHLkb8Ls$ z$3y&KRpLKEKIZy3oG7h1n5mz8{9#oBRte|r3{5@YKsUx+r|y*N5$|`_Y-5ad;#3gh zc^)>2k^Eqtx%Jg37|MQJV010{3ghG*WxRw^Fwbe<34xNb2t{{vWvD7|rkm@ni?HK7 z8hh)i#<=ZvC%qGSF-3b$Jb+#@QMr;vdZ_lQ`YC)(Caer}pA3C&BRt53iUGQOilH*k z<7liw#u-qwnJL;DXo)!|r4h%*bBFe|noqdJYR1L9hWTP!{!N{FI5>W8Oq}B>`30Ow zMl=q!q1S@~ya5uCZS6)bz}3tl9qIdTVw4h&6?4{FN0IcPt~Y*TDu0`wg0?V!6CluJNtj6(jTp-@}(^tnuYTkHKyRY`gVUpfg2o+U(A!R>d9L zpCsiIOa^@tMK6jB39n&wI`C|Nq_@LUn_|l|R^PBUz&}}@!3>zFU(WCCZ}km-1NDSC z28x~=@_CXKqrb_L)W^W@aqE0 z@Vu#&=FQl`^K5?JNV-?XSd;W&%pIR*x*F~2fqN7sY!B~((8Gq0& zT<+`3y9`F1UZoZRZz{!%$-vT-7o%KHhpAZVfcY~HbO z58htgiZk6Hhq(Ed_Bav0l;c#bPQy5X6MCiV0FM;&OF2^2!jF-XP}znR<@k=i z!)T5-_Sh>MzN1RltM0}CIIxnxP|(;WQjG>%#(R_Fw+ItOb#9XL7`R+0>J)Hgu%>S+3|yGY32M zO(8q=9{rTPQ{Us+seQ^GJq$JaoQeUDqo)h_eY=b@(V93itl4P=2aj5TSi^9KZk9o5{l+-EBk3c?9CLt;(^M;x-=)3;M$GTF za_op_f9k>65qXg=PFJ4>&O`|>=?voR80X+@>BVW>?R(y(vy<`#aN^BqZ`3gy4aBpC z_JJ2ncs01irrIg`f|x+vt1hOCn7RvKLO%HIE~bkvL=(o$trmf?=V}q?XC6C8UUB=- zk(ltfL9}D&^8_s&kU52Rx~1RPAx_R zIkkT|BCX|!Ak%FFWx9c8$jXrJ_UKP?WN?AC3YCdQ;CVm$`4Zk4q;x^+74X2mOFt*q zD=blt1SK=iS= zSXu_Ln323f84FhZ!QcvYCH;7Z84dOH|<5e$lc2af&C*F+KB9ch*57sRXn;Tr}I*AKn zLch_fmzXYM>Mn#S%e=$IbkT)qqLFvBB!yNzDxdIt!3=O#u-jh8^33zXsf|FZ-Vpp7 z%ns)_u4d!->>;TmAeQC~tZy_Qw8!L}fd|{sPh((-c+#8?80bh0Qxo`tMmq=tzx66l z9uwxOR>OPF8`cI-CKEj$&2zZFwaVxYe}bdA?9tEi9ByVUg|tI8B4D}YIe!($^2=-w zqEpIOyNr&KPI!uJ4kqpAChbMi>bpz7O72B0<4Rv6@R%3#tYkcxl|RbS1|Hbw?fL?c z(~KK<2O><^f!Z;T2cBlGjagDeb5UaI$h#Uw89f|3p3(-X5!!r<=6qBh zY8OX8CH^yBhDldTB;aH7vS0OIpeB6%rUE+Qgy(sD4 zWpoqm&!gL+MB=8oDe2y&ug2VLYzuUvWF&6#P)gi8krC0+fp+`E*(W4!%F`g+;Kd`) zIk0YzIBgtgtuiakWQm#A*ZaKgGB9$d^m`-@eqJNj9p(YD7x26j`hvz1URY=$t*{3n z_8-s3%IDfSelGJ$Xq^I{c!kjf>(p*1SAw9?bfPk3HU{+yun@GqMy^=!UfaF|&FX~t z1;=T_iU628H8mKg-2`a^O$eit&(#_$cO_>i-YcO+U+8c4``P0~^TY80HO5^fh6HiD z)H=|OaaSoTg1FsE&rY<5#QhT5Ch6W~3>KXnN?hCyG~;F-E9u^)Uke^<)9vC8=Dty% zRJpi!imc$gcKgIxVUliGn-CAd^JB=@4raGUoc`-?Ej7O|Rf(C#+t7F(w8lJGsgraF z96S#e@_652J%W>OX8t*L3pq(ia37>5OzNXKDDkhl1v4h z3L2791Z{Z>a-4liFJ)2SY6bByu{nr`*@}zYJ=R*Ay}4TIP!(@St17L{aB!&GO?)v`9$T;gh>-UE5 zp4ynRF-wfe{>*y3?|rlHjg7u^oZj_1p)Y!S?I+gHdelaIrii7}N6a8cN=QqW z{$FmXx1!?z*BU7TUtu&cdoXV=k(OuILQccS#(fN*KU$tY9vxb3QN}HyJ|v2IM}$PG zy2T+;(4tJ;^+m1q@PZq2ZmiaxE^WA{^Ox$)4K?b`N7N~(Td@~W3AaF^#9VDe1##W~ zr>HWLC-YWBP08LOvw=7@OOI`=phJ8EPq##Z!2l5&+_OHRRT zlwHDwGMpLhYH36n8wV-4n{L<&L!A{HPnC-i6MP`rQ%qC5jWVG8g<5(b+zfi#O+F}$ zDu$5WfU=ZjvM7<=hp5V1roOkUz;nR--|5i#BQNHlu*K-i-TJWi^REP?$Sh(x??Ag61;q z&8PLz){e2ea>kz$(>943vBHX(f>NKBp^78wDbcV_UEB}{+PX>)1|oHa`(wDyI1BPb zk-PiSjq9AD8@9Vpl^LADtKbl3teoH^@y8f^a3-CbT9D`C8Gtid%RnHLrX3FVVG+yxu+(qUzbmPZ|_H?nDDp#(R@$ch^jUq(nIanmkQNdz0G>rnjzlk zm(%x@SaOeOC+@U{^j6n5)QOW;vbz3=db!n1eBM9;kK-|KM6<&Om}ckZFKCXrAkFe= zkY>Ionknhip&8*yW!mV=AF>{+J!;)0It|PjyH#8=^%eB+N8`lnR(sLSdS1MKMC`*s zn9Z%B*uyzOjQu6>Z(y(7idA=j_u>}3w-yHCt!{uDsP;5|(E2NV#J-iv<(GFS*xvv< z4g0VO`x@?HRsBYv$M>mx-vr+eFb?ATG`?>MErz5flRR309|@b{c!STzN}_u@D{HMh z3ybfUB8Ged-#2CWN%;Lj`29>JiN9}Lr>0Id1pC*Jk8$DZG!zlj5$%%JTt~Zt1u3Kk zMU@x?Jhl|-+8Z4233*ybn&3JTkeaF#*s~pIx7J}uBn|mUZENyw{cpf+Td!SC3_Gi= ze9E@CFS)`C+2Y_+)&2?WHoR-Sg!R_;cS-9P;b-d$e6Te^{4VXV=+J^hAF~C^I0g8A zHvX5u2WcRU66pN7?wt13&B=m{YK@*>*9h?-A9VY^)oDSY5`B$ZDB2 zsmN*oq>GHTu|iUIsCNBxs9G#7vex^c`V)?&eR~?|5qle?XO(QD6c1Yn*`4(ZeHGc2 zP+MlH-kj%TAN%=dtiKTaDTZ%uKTl^nR5tY3`x%}z0Mc(4@Crk{m>@hw$ScpHt~y1? zH>C)9T%<;jjT#f4-y@PfjjKA~eH?%5#2D`}BAC9DphK=1me}#WE0_)%o&OTAMm0Q) zIJU_Q=Dw@L>9*xKzmBpQV~_bK#S&4N*{b!px9$$!@vMRxeCV6|y>)l+{AxrR68wgc zN;P}zz9ie-819=PvTtnL9pE`$tq^CWaGx2w?7rV%&STr%Q2Y6#vTe{EJ#}cHQySFA z&}z^Z!8$ZO#E3A#J@8GM|M zCP{HZu0;{Qu^eCs!1;o>!Dwu74q0Fa3>n z>@daj9bgNT zH0EFO^TGW5K5K)olvhz_dxUrM^EtrtIl~i=AgckduD}O)PEqNcz0Y{|6Ffb~8Hln`&8$N>ZFrCPOF)42qcv0_3M^+5(+sS+UE}HMbh4B4r zxF1LNl>b2RxIe=8U-JDq&`UXICS{k|-&1y&DZriOy>W`~KXL9kUpFUJA&X#X|qT4MPx*&)=@|CVb` zFikjvOI0v*J*0KcR>7)ts$TqW=7?Yl&}w_ky>sRSD0+r-6%h>p zit3eZ@*Ut0j{g@_pEoZ&^Xh{0;sbu?DfgY;dBWMls8(Q7hB4+zqj?hunJ18dt*sBoMrm#XfPx?T9 zus)?fF24|u_9Oo~{cT(iWChI@{>=O=sS(P9vwQ}hwu+Q1I$q19lQ{|tI~YkglNAT8 z;n?wrJ5$0tcA3`9!r>S(ufK8J7xCUDgEZs0{UfczW0qbgF244XN#Sv~-BX}GW?b2} ze1N+BfUa-5`H|{^H6vC(l4&IhtLWjY#I!!$Y7Frv1_mCn%?7gUpzY3Nu1xK9Q~2n{ zQEs^>SU@FBEmETsESI65(DcY&k*MvlGV(V36LR_w6i%4caIB8!3=-%-(9fX-arIIB zTc$r?9q#)50Bhv{>(FbW|4?!DP|0w=^}>eV=nHxn6e zI@7a+h zkQjv>qB~0j1(3YFF$y~r@L%9xjpwMu;O^u*1Z&J#Ei-v%N%LcCLwTl+cpEqFE)ammUPM?-tH*ebX+U!L$r)T%5nKBjs*x$6u4wBMl=AuP4 z7nPPSS+b;L3PY95ubokf77&hwr33o)>OE+9@7C=~+Q1VuU#Y=ds8wcRcFa^3VP>>L zP0+UZ%SILO`S_cG41(#n>!-|9reY>!W3J7?UpDG%I=@=Tchi-HfUH#(<2ntH!|?0N z@c%{le>P6No})|whS|Wh2%qNQ*HixzhTa#18pU`Q;urHl1<_an3d@uVtO1AOiV$3_ z|5xvu1&XslOE%tBi+`u#J#+DIEvU@KyDrDo+Y2QjccI@*1^is}=uGa-g}^uqn21{0 z57aY@@IGq60B-wqe4-wk2ADd0rnlGP^CIA&9`Av%p&pZWp}%Q`?=RFVZFm$H;g=VI zSClGC_^$+bg1AaRLoLR&)E)zej)izK0R7$zk!FJsJ<%JsAnNHhAw3%$$Ivl?@C+XK zGyY$VmR#sRR5ypUAKMh92KtQ18~RUej?ZIZH|vI{y`eJ+%t+!-EAS^!ZEPj3Yw#yf zOY8<**DH78dKaqK32@RKxIU=t!P^huPaq5FNnDTMPbkmfPaxm&1zcajpFq~?KX82m z*^mMUGXE3Tqxchu2>%bRpW#oScEVSq%5l6iNgBgc1-!5jdd=+zDp@h2xrn z3*Cyq7`@6+>|y zfmnATCJMA&TqZ8Vb(+AL=VGRqiEFi(gX=so57!z|gXly5dLi{3r!S$T56rlnDS4{=Ys!#Rd8m>m-8n4FVnxdxRnxUcxRu<3W;D0HE}qo2n0jQ$6%=XCV8;WMJq=QK}f zH&T$zE-+&r#NR{sdl-Ma@V6WBZ0K{G^HGHF4a}PzmjdGJRC_g$jGRLPS<&%*Hk z2)rvFes!Vzf%^zRC&SK{3JZ8Tur`6D*%ULHPFrXWi){;3FmHv`RDu;o+_fEeS9{a~ zC+gnjKLfLVB`QGNsBBbjQEtPW_A8Gl2bD*aL&{<0G39a0@OLn0 z-&NjI-d8?QK2-jN+50hK{Qr$P{E6}@=J4mr7s{8I#m6y=Paw{`6mzkIs6c*EXUxG~ zqPO@z+I#cxsEV}jzfM(!H6$Ty0TGeKT|__y3kiXH^4c3i}E6uS!us(vrkIN_JU*ywrrhG>JRX!`9le6UW@&);#oGo9H z|0DkT~#;LL-k~~?re3gI$zCF&#M>Ii)yxdN&Sy{S-qlORj;Yn)!)?{ z>L2Q#>P_WRZ>hJ{JL+9EM`fzd)aU98wMZ>iUn09-sjt;HYN=YLzE$6;@6~d(0$I*h zE7dBsP3=%S)h=W>qAGP${I@pxN#u7HlG)sm7b#i{{j-sBBSlta)4bG_)Rd;R&2Ae! zF0EN=m!Z7|Z*Mj&?NPqTX}6hlXs@&n;&ps}T5!mUAqP`?4=Nbiw$+Hii_@}G3tGLL zuqI(^nk&sUxF{_+9QyF)x} zKS*b$0K?y*+)tPRqV{$i+`x^8J9>Y1#dU_ut;X zcEB|QW)B`WxQJ4F27Hm0Jt!sBm)bjZHuW;!N1eP=zv%wdlmP{X7O5#i@40m9rHhA7 zO-&d&dgz3qQ^DxyH&qOsU}zNmrUkiwqi<@L!P}$XXo;iLi=92FVDRF`-_(NW*U&EZ z9pV|{;f{-ANbM5+8m^=k91rgAYIu^vuiN06;hNbWg^O#8Vn{1+zQHuULky!x!M`b51I0#;* zJvyks)Rg)a4cM)K|?_NJ-sY(mqJ-k`_$uJ$NzoMW%+pVd&(8G6&7qlsU>J z?%;Ch1Z515Q@faBF}}tqXlUkq;k==Oxo7IDoLz_d^BPh8H}{=@4~_W}t#t#%W0a4j zgNve6H$z+6(&(I{mmWibAqU}P zuX>5Sr%rnBLGM38+fA!s{=aI8mb6X0FP^W)-k(FOVC;NL?EJ;dr}XFB4(pyq+b~QF zr&YL0jAT4(G~cd_GmRy>@Ob)Y-SFkEqqVq!@0pBePo~wlmG5~(bezro{>ir&{>~ik zyNH&cH!Z<3+K=z}_7}_f4iMS+Is+YFCsnNFI|zSgD=kVs-{G_td+}Wh`5)Q@`koc| zL{Ptghl0TCJXY!$*`>oFEkLr)sX|!=KSzT${ z=2&N_d1{_@vHDPbXkCH__OaDpEmRAw0j6cM2AY=5N~L96W(}r2TW$@bHCtniQ0vrs z>uR-8ZL!9we6`KGhPG{obsg>7PV0KwIFEIM+NXThB;{9r>kd__%B(50dlBnSRjH0z z(^Rdpt#oZ^Z9S-4>Uir>ourejKk8(iV*N>X(Zj8$StB^YTA)Yj>#c?QR(-3rRHy4l ztY!K!{g{=dAJ<90ztCS;JN06{*!oc~(cfCT^!NIEtB{$6EX%7`>2+3- z-k>*F0li6Ywn}ua&b3PMFY~Q3{iFWT3hF(2k5#Tqbcq$xrMlD#>k3_AMRbj>VU(+- z=x?tP`M_?l2pzX5Hfma1|H@CW33hb*QkTn~;3|;|c zcD7Y+XUo3eFZK}mG?)pV0e=P0g6F{7_Fnl8co)n8?}52s9(dm_lOKS8fe*n)AOm~~ zGQnrybMS>-Di?vp;7hQ?E|Fh>uetv>+-E82GSY8Jzaw4FHCbRKSPj;K^#D4^%^(Nl zf*-&(umkJ_#ddFbnDi(h!WH`77Hw~fuD4a~>{8VMbOfh>Q^9GVJO8}uV`nSqp!$J} zz{TJa&>xHeSA(%&mOVth4Bq43A8;*oRv&Zy0`d#VXOJ$odn@XtR`Sf%_FlDy{p$dH zR2w;$@~eE(0#L+x`#HzYzA}EBIiLzqMg9=S4s+}X=}}VF&}s`v5DyYS5@^eco%SFF zoXYPm_7HuVy;pZ7J)N{0X?M~dq&-Q`AU%`xEYh<{&mldR^gPn@NqdoAK-!!1Lef5@ zeM$R~UPO8c&lmtM1%p5u7z!>2!@wvo28;#c0Wz-GTtd6!18?p}@vd=0L3AAwuv}XzOEqf0(Wgj+WA2wy5 z>S_m7H}>@aeMExd9Mun81Xu;3E&=_)7;rTh3tr;fx4|5+l6!399y`EJu#0`Awog^s zdvrQ@5IhVXv-jy)q*8jUlsu0TNTJ@+d_frO2Zcd6Z%+rO2Zc zTPa03r6^}5T`&F%Kfn&je}I32H-QTj+ac8!vRDyjf!9JB>pGvSTCD@h{Y)c8Yr3Bkj zf^8|mwv=F7O0X>@*p?D(O9{561lv-AZ7D(LOVH&K^w{WdiP|laln3ksetri*1z?s3 zJuN{`OVClHll=1@3;>scK_Cqb1($jWi?EnQSj!@;QxVpw z2sh-JZdY2*gBQSyb|vj)742meZB-TRO%?4(6>UfrZ3yoZvMZGb>;q-|uCl9W6RKzv zs%R6cu%T7h&?;6*AoDXJ$HvyDU zT%*cCgwnSJtw16e2}Xmf!8kAhTm!BHHvp)sCxKhQZD1}~O5KJ~dq1`JQ+q$P_fva6 zwf9qdKehLZrS>W)kOT$%P{0ob{7}FT1^iIJ4+Z>Czz+rdP{0ob{7}FT1^iIJ4+Z>C zzz+rdP{0ob{7}FT1^iIJ4+Z>Czz+rdP{0ob{7}FT1^iIJ4+Z>Czz+rdP{0ob{7}FT z1^iIJ4+Z>Czz+rdP{0ob{7}FT1^iIJ4+Z>Czz+rdP{0ob{7}FT1^iIJkAK>e_Au4X zrxn?T$LYi4^ocddeu15jSLw6*l3orb+xhY@wh!;nhj-}1JM`fl`tS~Yc!xe(n{9Y; zK0G)d-kXosW*e=|HasOCEzUN)B_FNMHua93Ps_7SajsfLei!*7@|C1D{60kf2<@G+ z^YLJOcrQLY7av}W50Ax%x8lQ7@!_TT@KAhsCq6tAA6|)%)@vKB*EU+OZM0n5@DhCV z(Ua`q=!+ ziE}7n4t<9;wCBsP=^kvl2b=D}rhCMAd!?8FCfaUs4e7P)yAiMDCfkdx@M7;h*n1Cs zg+17O54OWAvf$}T&Rs=6VYPigthFOz9dE(g!10Y>6aVPj%=ugJ7jo?FVynGd1UWwf zDnJ#e1~uR(&ObzY1RMpm_DZH#R??n$tT@sXTBA<3m$t=2+v2g#W#7dh6$}QKv2P;j zwcvU%3EalM+rhoIhqlK<+vB0_@zC~oXnQ`>%!yfFQ7dz;st@6-Td1$LVv{fG3Di3XyhqlT?Tjh~CAQ$`qwt*dBC#d8()KeY= zKY_zM<0$yWcGExDL;qk8{ewO95BAVM*hBwd4>s3J8|P8;?3MWZduUbM>SOjTAit1& z2I(4(ujTkUey=Bm?$~HA?V(3);r9<9pWg+*OWx1^0BIS&gXAkf6?vln(O!DA1kLP~ zx&?^0-L#z^okZH!_F~t)It6s**s1Kl1PlO|f+=sv;;0d1Z0`GFoJg)ta z;|s`VfUns94Oq>2Ymv_bY@d}Xk2+|%w+9ODN1nG_--C?BLgLHe4?g-KyLAt|8 zcLeG7Ak{&1B@u}(MWQ1}bOedsibRKz=l~KMK{_Kyr3a~uAe9lMFoM)Ykh%y`7ok-x zpj9oPRV|=ZEud8`KoTQJVgyNyAc+wqF@nT}k+?7t7e?a3NSp_W^B{2%Brbx)MUc1% z5*I<@B1l{WiHjg{5hN~x#6^&}2oe`T;vz^@7^w;)Rbiwmj8uh@sxVUJL8>B1R2XUU zAWdN;DU2jVke&$A<3V~NNR0=n@u=N)0j+!it$YE}6G3u3NKORFi6A*1BqxI8M39^a z(h@;ZJV=TMN%0^h5u_x7lthq{2vQP3N+L)}1SyFiB@rYcf+R$cga}d)K?=f1K^Q3r zBL!ikAdD1*k%BN%;6VyJNP!3LN8o%I&PU*U1de;)xQFpfR(asz$HVae91p*7z~ulO4mh0MK)Mn9WC!4604@gLVgN1%;9>wS2H;}AnkCHYm3ZrA@CqwX-nRpA zFaQSwa4-M|18^z;rvh*)0H*?QDgdVfa4G<&0&pq-rvh*)0H*?QDgdVfa3cWq15iHz zmXIe}F)ov(_mX-+ zDftTaSA!ZxI1ZB@q32<=pa|~>dWu^>2%r~MKjaGDSnN}0fV03kU=ujVwHj>XTvndZ zySfF0@c8luf!3$mxkD$ry766T z6{=|!s<{SS37skCoKSFpbEsE)Yh)XGFM}ntFffjSkh`NX*HI! z8p~NtNvfCuYQhTrmY_dA`anFkR7zrebv$}X7^Z}NO6M2(9CrhrB=%BsFRiVg^Fy3- z(B6n``N3*#7c)Y%o%Ydhb+z~7+nd^i88OPo?ySMjuZBKhMvwBXEAi~E0#E>Yg{?cd zZVK8y73(|Qo{EjyNL#vzwsaG1=_cCJO|+$(r0EANq4)9?y_5v}`6Q4GOn;y|`%Is} z^arASfof&?0w1&gD>%23^HxEjjr`t38%VE6nf^fm>27-y?cOHZy-kc(ZHJnEr_WGD zTH{bPOrN0|9)@)rxZjo@j)_{^p0opL3Ta2Y5YtC^fV}A^JVa{x3XhOJMt|c^95?-j zEb^=D?Qp`+sMmJ5;b$#Y6YOzI(4YUp4z$<9i3ps?z)#P|PtV6k&&T&>CXeIW`KOed zw19K=kl%}q_VVv|KYnzOb0VMuRDo(h&j&8$;BV*SZ|CE0=fkml{Ox>v?R>bl9{)NY zjz#dX^WoSWIJU*2=Z3FsdT0^2HV3X1!?j{-G$Wbg;MPQrT??)UlK`zcoZJc*ORZ@f zpKiYgC%0PnlYbVydJa4fUH~sLAMlo)flr=~znqW1oR7bpkH4G`XXn7#IdFCkoXvo% z8E`cNu4d4~i@?90Z?kM zhqD=QH3CN?_`mu1zxi;r6pog{%~Cj7jNh9NH#6X523*X5gLB|uG5%;iT+F~H&By=D z$N$WSqjTWsdbqisReVkCMR0NvoO};Xu7#78aB@DJ%!QM=a5C4?t(BztoZ|)soU@m9 zIjUEy;9{<$TM^Olc_1a8iSo9p1@A~?ASPA-Cz zi{RuUI5{6q=EBKbIGGD4bKztzoO};Xz6U4YgOl^&ix97vv`EYeUTz${c%O}`Bi{CENufR7Ti({+owQzMUDPOQLEhM~F_%NU00E1aAv3y~4#f#MoWAVcJ5%8G30zL6UO)u2+LQOB!T!F<4 zW9`Dk^X-q#8;FM*)r|m@bwgP*65&QSDxjzviu$3bzfLzo=tc?D^+R1Zx={gz-B8#K zh22ot4TarMI1dWDp|BeYyP>cf3cI1O8{G&%S$CaoR6t=j6!t^mtx(tvh23?!Q30j> z=tc?Ft^{hkp|%@pyP>wbPA@9ZivW62j$ZiDi#&8700-Pq-VNp5P~HvY-B8{S<^53J zj}8RTfdD!XKnDVKIuL*xZge07H~e)vV0!p&bf5x`xZ#K!9SFb;H{5U|_W|TSP$%~l zb#h+;cieEtjogQj`w(&;Lhei8kQ=!VAol^}J^+XO$bAW1awGQvIOT>@ZaC$JQ-0(= zfZPXIVcU}))|pWLT+kO>1p3>BaIX-nwom+(-+u=#u$XhdBwa$9&vE88Sap&F7YkXT z*p}6aNp%*kP?R(OQpxYbcA=%2M`;G7Tak7k?a01UNbw`B)4=Iq0Q-iL4kI<*!w6F2 zL5w6FLpqLY$AcTtk2}zrsrFuL8o%%1n(6$$4{LTmztfqoVJ<`t2O|I+mVaU2(_kie z2K*H;&m)=Vk+V4dDtHaN4yY^RtR--JpZuEROTo9`d-kmW*_Q}>5Q!*6BK9E>`xG&()GX$BW|O|e@0ZEHN&ao}a{$j$^i3F}EkQ~Ok%oP$ zh<&Bx%h?wJmFzpjz9S+@w}iW`Kq4@nNhiBdcVT~5&<*qeX8=Yp^*P`?&%a|wbp?$3mN4#H!nkh<61EQs+lPehL&6Fj%lc>1S^R#B z`_1K?r67xatH2uM;v#VhZC`)%c_6-J)c=eiJr#9cP6hg3UFYRgq5l;~N44Wy4j^xQ z%3-9&8!^7*9dLOnh;WF#qS<@~Jc zO+Z)t=tm;Dkx1?Q=tY8K-FBe|1?2aDz4W7_ejKZIxl1`C8 z4-(OXMD!pLJxD|k648T1^dJ!``jPiUdb`z1^L^x!;aN~g+@7q|rV*O|C!@LQym{@1!j0a9jB$^QO zy(Q>RRFr|V;DhW;xHK0I&8_px{K(!KIFSPvc0m1&P=2oCB@ZLL5{fb_<9Nn*&=+D3 z4(h&7yO4{&l!d>Pg};=Azm$c)lm#_2q2OF(axOAC7n#h2f|*b+6Ut>mwahx7XfD*s z#P`X<_sPQd$%1Mdp;{*L*x0x6Q{zl(oJox{sqtKD?5DPw)O0R#xsjS~WZijx#^_V& zD-2@xBA@(r@@~>y&a6x^npRAW4~R;9cn$g@M+4~1T~0cTbQ~%Db+oM*Ei0y$KD4aZ zN@whY84+r=pIYswR{N=yk6M`-8Z$fNL(__>nU9(sKo<6+X~ope%+C1GvSPHXm|7h` z%MPGr2dI&c8u_S^j~eZ#Ha=>zpH=t$DQ&7#gCc6+qXtFPz>BT$Q3Ib-gL2YJyeSR( zI%_bBsD+PO6j2KwwJ4(&K5AiV;G+ht!SecRG>)}cP-F9;62?D|;G3HsS6H_Ioxu~p1@Kt$J;HFd zVa~b+|GEbMx`tX*Q}Sv`Z04?OC}j<$Fn!7s&0*Kz|JLB+*5KdP;M3ON%huq_)}Td= z=df#3m0g1$TZ124gCARi4_ku|TZ0cgXzT*^dxxn@2kHiu&7 zPh#^Wu{n|yMie@u0VkU$+0C&%pcrqB)o4}?n$j9{0vCe!nb$ae&LqQLFFysD;4|#e9i8%(X|sFN_ADsf@;!F&bN@&{#%e%NUI_%h%+2SpJe+SIiKRpyUpUs>~HZx>9utXcMxI4v9 zj5a8s?VXlcMPudwSfzqAWLsSsak$8X-F0$q{BIkkk8K3$9{0n>tJ_09N2^5<*_=ani0ki?B%tk7+ z)KMHKtP+C9&vw|vt)U2X9 zc1|H%pN2%A4!VP$;7o8fI2W7`E&vw-#xYq#YBVZa4*-{fK_Cqb1($XiZW%P z{2s}?v+3c^AWttA`yRozNB9THm00u<;3_Z*(1Ku{L+EMvxWn|-JWK)_Do<=sF zMz-i)H!l>SG}V-b{vRHZ@$B#mDG~f(HC!j2Y`it&osH-BEk+tx(bjmbs9}D{%n6f16=nhqqXWcUXvbSO|qyL6udsKnLxq+|A5X z?5e+;xzh+{8jP>h=nhO8aopKX-F8wpS{d*hcpkg}UbJ^o zx1H2&Cw1G&4B3^eh#dj00;9l0J01OUiRt-+>KaBlyX7p-(Q_zI61- zg+95^B^UbRLVsN7kIPzVr=UA7bjOA6xX>LJy5pjaNyp1c$ID5_%Sp$}Nk^Yt=#vY5 za-mNy^vQ)jxzHyU`s6~NT=e}$GzTp~EAXD} z!ltL=wWOnWE^K)^y5~aoTq=j-e$pVQ0JiNy|6J&w3mct|4!W?>>FA&f9dx0CE`2E& z1k%7za5)$TMseO4Fcypl&=Aih9nU2l{dA$9F7(rdolQqaUFfF^yPB>SkY<1t(7KI< zrLcD3p}Og_{D|aKgF~!ZO#;bSkhXaDqp=i!Bz+q1btd^|(62nO7kB~l1lsuJ=5EE@ zt%AFiakm59?ErT>z}*gTw_@&gfV&lQw*%a*g1Z%Sw_@&A%-xE)TQPS#z}<@ZAAYo4 zA@f}RZtu3gv38@kmLnlru#Qp;MW1h{2Q`MCL3>tyZfCtwIjd4y3#1?KDIjvV(^ks1 z3oBhp5BwnVI1r3Pn?|E|W0>2yff1lP@Pww~Ma^KJ8P(iP$%`3#zL0B(M8L>dve;#R z#y`K4MH%-lXDlO(p8mopsDH(q0*Fe;GYYI*?d8_poI9Pe znN{JYe~?YC+`fzJOy9w8uV%lQdD>11w=(h(IBw))9;N$%|0rH(KP7HvrgBDYg?OU2 zTFk7?5VP!C#EbSQXz-xOvJ=H>yMRcPZ`g}?q+fm0<>X<-D@1dSoQO|fv zn`Vuvt$?1{yx2R8Ex8bDbSw34LA~QBeQ#*Hga1Lazy~(}BS@gW=D!3ZnQOhy{!!dO zYW_no14=)^sL)J%9yMD@&9bRkp7@^QSS4VIg^ykcau8COQVjOOS-SK5(*q*F-m1T&B! zMNLCIzm#ht*wyGtqNQ9{1cf79Q^7U-M-cg7MG-RcDw5wC51vs)N_R7~SV{>u*-PNw zPOe)o;)yJW6b&+Q)si@a!$zYcrG&LyrI@^+S&m_64LYFuQ-rRx9F@B(p_Zou!ES-VqU=itG5Br6$-o`AIH z9zkCHr$Tt5w!79-bBQijFI_0!1p zpOEXbkn1O*++U#FG$>bqT+c+VUC8y5Q1MIT+EDTdDDXDCpAJR+)a+iU`Z`q2M6Mr4 zt`ni|Yjrkb8FX9*9hX6y=>KF|Q>!8)(@=RAM|W{_7ql_GL9@bZJrZqJchSqE92L%W ziReT;*OhTyLLJYdmZ%BzDTWqC%6%LOa!1p*FXy_MNHwGFf<6=cD1{pPkmf>oR)_^- zY(QwZy`G%mb|qI4LxerV2Qh2Iglrja{(qhDX@|S`cVtigiQVu?j%Drr&^GpI9m~|E z=6C%*+eb-%^`R7XA3MX|LAjj|r4?vz{m1s&v)EH(_80uiW)%l?FCVu}3KPN9sjH-<Dy;bx~o`iH8TG$;y*pc(Q>W>!*vn|21$H~h{@h-Ip4ICGdIr_)3VU;H zt^E{|m}Gxn%TcVeu+RE6uTI46hUfFN>|Yhd*qKtJ+UF*I9I2^Ms}}uEJ~fOnwYTWgTE$NiC+J)@2{P1e^UR`$=D-~ z{ry|N8e6TCpBI%jWU>uUue6`DOYFY22h6YN$7o1xA;( zJ{v#zJo+rn`7h>R_6_47GaZRQbQN)kOa!7+o!CQNoY+IGOXu5NOd|eb4=3u-nNHN9 zvxqmeggcoSL+2A=XeDuoOeDn%Si`b|KK@R=eOSX%Knw&AYgqb;Li#qBh#=p=g1Kq_ z2mBM?A;cN_nf^kpu*GGTw&IBFYa$H|cOng4=|mbDVVM|1qnsE+S35C=#yc^FCO9#M zCOR>Oh%iKqAtDSBW9WJ(#?TE;jG-HeF*I9DvR>vtJ5!t(Lw7pSg{C>th5q0~7rNVt zE;QYVE;Pf5E|l&>7ka>nF7%)iUFabvy3iv|bfL$HF7%Q3qZ3hRE)j*+h>x7eL0>zO zgR-5-L93m}L2I1IL2I4JLFag+#=}6(SB1afO8K#1)cGTp^`QT%l%8T%nduT%mX;u28ZQSE!p4SE#!a zSEz>*SE#2GSLh7l3N5qFav}=#aUu#`L`0!2)}>A?q05|DLRUDkgoZh>goZn@gho2C zgvL3sgvJv~=%_W(i6V5X6GiBDCyLOWP86X(I8lV|a-s;`??e%DIZ=e(a-s-*qVLdm zSo8H%J(cJ}CZfy<tRPfJ39K=(b3P-9sN8V{p^XZnm4?3Cg#_f zwBBd&wa~S*v1z^dwm{1+;L6^7Q_wG>aiU-S7(p~~#S&N*GJtai@;#LZM3)kEGL>&K zk%$J-#~jSJH99|x5)9|t6z#u~2$e?vn>qTg(f3g^QOK_0921AEons4{V+$tH ztDDTXIac8oN^hc(HFfNQg5i2--LW#=9V^ocD^n_6mE=wAv^H|JTut6YP_wXu>nPQFxn49Q*5?ND8|6mwo8%_)o8@NmTjUnZ7kRyDz~sx2I=+QPA_ zEgY-b!m+9?9IM*Gv8tzFRlD(TxbCXEXintO9^`wfo}#rnL$QL^M06v6mSQEXiR(uG z9IS8>R=78F-xsP2MR(N)i=60KWd7lYMNTvpnWJ~AJ4F{YP2DYe7`x0K;=OVHEHz6= z;+j4$;)rWHn+T&X5k<^c>X+I7ih4zKCd%on9DPl_#uMLAZ!n+!rh1bzUCKpVvA5J) z)bwrjHdnpFO3Y+ppUxpaPt6nBSb1KHPV_ixMD#fFnJSaJeWpGW-H433ShO(FQMu<= ztlsQNj8vqEIH}+8+@)$M=PXmpIDff{#{SLXiP;KiA#UnQp1Vq|;)$!(YSBckQENmq zT8ni&WxZN2nyL+IgXlmkx{W-2liEZCtIY~&QCrj&(TNs?c|2mTZWE`Nc7-i5>e%j5 zyC{DF|59wOcC)siBW=t+wqE6>Mn$YBm1@7*&l7#hN7;yD$306`sW?>;_lm2CW=9Dt zR0U73BN`0n2ll)n% zftC7feKz@XSPLulx%yo4=dmVM>htya}3PMpo(z^@Zg7=sx88>b~Ur z>3-xd(if4xSYIsS^deyfRCqF#NC+)nmzz)8ojG z*W<}g&=bf{)U;6g8qM1b^tJk0^4DqJkf5*E*OMpKAX~fy(ZYBMY}1LT7q1^+%|>(m zAd&Ur^+WuBw7Gtm2z&AR5&lQo-1rczHhx?`F1qO_^b?|!6QNq`nR=$^s-Mx%u%8Hp ztU@M2Ay>JyOKANT>xZR&TjO`@cl0~dYp&*<33{HMM;VA&DB9s+d@MTaPxL3qz_Y(mvZJZy^JV;-|BC<1JMn+C(#Wl z?Ml6p+N~l!;Hi2wD^8m89^^IT*Xp(8*XecQ6chVUwALH-MplVz(wn&3W}%+Aylykn2Rd-04L9lwa*LJrh~C zH=1^@KgawwFwweN;K8)VTQP5)X#Al!M>)*bqzxa!MB}#*)0|z54d5>|XWNc#-S|wy zssD8@R_>GCK-QNinsfT0Cp-C9`+4!4Fw|-In7Wu+n2(Akb=zqDkG+3OuJ6e4mUZWt zu{pt>Zcf_O*&e?!cWnm@^-giNW{*Ygbmx~j-|UG#-Ez(``x+>5Y>CY=b5z53Ljkd_S*_t%^=~SOP*|q;0`;AQh-lqfoh8~=hOy7VJ4}AJ48eeDj z#XhY#7xei z^Y$LBd15`xog1jx3jQ~IPb59fkg4d`jAxl|hq^0b`^_C=pA+qi9cxIVXGMQ^L2Jx6 zD*v%(n6w8^GVjzfpP0qtokop*H*Te(b&ALKm|7c78QL0atwGeH$B{qQen&aet^WR; z$|rg+358oD7sfJ2Te;@gLh~zT7Zmq3RF1(=+gK^{FBk8d+G~rys z(G!>BL}#1G{r~>iEq?Rt#-)z?jZ&vf>GRMO@%of0Gtz}?>h!z+Am&ZI@BSI$<7p4x zK1F=Wj@!l8&h~p}yJ7l$kIWEx)9;@$UAUdC$JrLK`$181zd0I8H{0rmW=u&JM;>~J zcM%Gf|I!pQsiX`8IE|Gg~8-=B++vYh>Ka zV3@73r560{Brevyn}(XrYqlxzzQkt}KTBMd=ub*Y8l7}o(!)tFB)y%qBrme1O66)}ESXzjE1GN$LTcJq#cU5xSVwA|JXYrD0LH!YP|2joNY5&5uuR6Zyl zlM{IFQW+zCyyO>(Ymhw{Bim|~&nWPW#UZys(t!&4enL5Z6*-@S%JIgNeR5PB$Xi^{5SM^gD zsf*Pms=peb2C7R{sv4vQt28x44ON#h%5;SqriQC4)d+Q!8mUI9(Tq7=t;VWxYP_1D zCNlPPt-4NKuWnE`s+-i!YLc3)Zc(?Y+tlsq4mCyFYO1T&gidQv^5{-T~%Gu1Qduj*O#91f1@d2Z1; zdMka;JpBWG&ux0U-l5%kCq2+zxVt0&9O%%Z=n3O^-@1Z;;p-)p+ee=Oadf>oos}k=K~U zTQ(-u$ZaJOdqT}`Y$;A?1v+2>9{sQN{+a(o^Z!5V{jatDNA%lhc~rZjHlWne<_e?7 z*u#U3b>3+Fv3f02>NUH4Bi$~ejWTc3xsvyql;XOK~ya_PKdgBmp(20l&{z+9Oss;Z}X2$A}IE=k9Z_@e2%x}}} zvnJP<|vomY>M^ashKX8S+z^DL<2+%P-_2xmbQ_=60CVS;qX$a^`AQGB2~1nV5~t zy5um!@`KzacgUU0vh0?7JZ|#VC3+oxs@mQ;fR) zmABJ3i{oF(wA9jS-bVY) z8~f_+qqa8M-@DQNyhi&|8usg}94~3U_>@BRu+GHWskb+jv5CB$cO~8-r^wr!Hy1WB zGAX@KAdf3epTK!@V3R()8R|jtq4UdMy@3EgC>yqb6J zz03Svp{!)qsTZyNNLtxh%=^5}d+WBULfwLK_~H64MylUn1I zT^ILs+`_o+akWj(f}*^k1W$^0(uniq)6`*snR5`CMEsCqzV6C4gNPqeEYyr`Q#*@s z=#R-|njEu(Tmgs^$|$z9zGXz)96R>DFPO8KJ23Zsp6z_*4!AF~2Iju+u>BO6d;EiR zfpnWYIZrbCkH6dhm~-?9b40$xcD^3YHbWM$U8v`<{S+QGGc{v0-P|wWlpz#m0p>C>;l!oIlkN+2d4B1&Ymo1&+%niSy!finroR4F=aCJX7vQy z3|5kuGX0Bfj2mmnXEL8)Y8ZR+4t_0QE`cM=!*TU7R5CKcIt=INE_0Om6~pO=*&-iK z8O_Ru1wsWjJ*=zmC7J zsnY^xt;~HtU^|~#D^sdV*)G)YvW?!?p^PJUhMvw79KEiW8vQx@7swLlNsF9wV)9h# z90@u{zI2X6YwI=pnZq&nYb;Nd&iU2O`QMnM+QpVwd2AORM;F771@>5I7KZb3F zG}66L&tn@c|F`5COOb7UE6aI?(bP<4pv<{3UdB1cTRO*AnB%}mQKGXa+u3t0hfJLp z=qcuiH2O4O-^n&ZwPU+bf5T}F)aroNf&d_9?M2J?=`pE->^ z3-o<#Gxc3;=j(geX6Wf`kGqw;pe3_rwtPHRHZWo3#RY87ao_26#!S>)S>~qZ-c1 za?TlN8U1HInD59&XP(2+cxv61{o4ADbC_2qx00MGiMEVvo1A&l_2M7o>T742cRgEB ztnt44oqgur(GBPE9%a6~zqy_Uomn??o_RZR{hk)==_M~Fj@5zoK8&ugPipb^Uklbv1Vz#hvdIo#aIBIg&fdx;tJg zZ=qE?<}Q?Fa-)*nNU3f@Ivc+u*}NgSEms-eg!{!x%rhu2rEF9RQwBukx%Ol>kSUCwbpaj(_~L5+)%<)MpD;X>#VgL=_*ghr|T|z ja8AP+o#4V?@n_xu|FBGxL*!6-nY>(H!JQ3F;MD&Cq{6iB literal 0 HcmV?d00001 diff --git a/web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff b/web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..532150fce9cef55e6973a135bb5cce0a229c338b GIT binary patch literal 98296 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

}h{9G5mW0d>jVCJ6JM5$S%y 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

    n`#BaK^{j71T+CX6*c?aJXa5>a` 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 literal 0 HcmV?d00001 diff --git a/web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff2 b/web/libs/fonts/BlackItalic/Roboto-BlackItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..df3c3f4472aee1f12d289f0a69945ec4bbb974a4 GIT binary patch literal 72088 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 ajpj46m@Dv8cLwS%S54ry+Z-qa00007t>q{H literal 0 HcmV?d00001 diff --git a/web/libs/fonts/Bold/Roboto-Bold.ttf b/web/libs/fonts/Bold/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d3f01ad245b628f386ac95786f53167038720eb2 GIT binary patch literal 170760 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�!3r53e9=KAt@g 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