Migrated camera.android.ipcam UI

This commit is contained in:
Fabio Manganiello 2021-02-16 23:56:34 +01:00
parent e508d453ba
commit ca2fd60950
16 changed files with 456 additions and 163 deletions

View file

@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-12dc74e9.505ac0f9.css" rel="prefetch"><link href="/static/css/chunk-13b07ca5.029dd736.css" rel="prefetch"><link href="/static/css/chunk-1653b664.5b949e24.css" rel="prefetch"><link href="/static/css/chunk-233baa62.8a0131ad.css" rel="prefetch"><link href="/static/css/chunk-23726328.7e460329.css" rel="prefetch"><link href="/static/css/chunk-2ca39dde.efa1eae8.css" rel="prefetch"><link href="/static/css/chunk-2f304dee.a8a2d99a.css" rel="prefetch"><link href="/static/css/chunk-487896e7.b7730bd4.css" rel="prefetch"><link href="/static/css/chunk-49211740.43a25f0f.css" rel="prefetch"><link href="/static/css/chunk-4dae396b.92b3713e.css" rel="prefetch"><link href="/static/css/chunk-5145872a.197de139.css" rel="prefetch"><link href="/static/css/chunk-595ffc05.678c9c97.css" rel="prefetch"><link href="/static/css/chunk-64076603.e451beea.css" rel="prefetch"><link href="/static/css/chunk-675c7703.75b51be7.css" rel="prefetch"><link href="/static/css/chunk-792fd41e.4d467174.css" rel="prefetch"><link href="/static/css/chunk-7fae0422.c233115f.css" rel="prefetch"><link href="/static/css/chunk-d22da0c0.7c71cffb.css" rel="prefetch"><link href="/static/css/chunk-d28a86c4.cdd32c08.css" rel="prefetch"><link href="/static/css/chunk-da9476ec.f1965e2d.css" rel="prefetch"><link href="/static/css/chunk-ee62c128.44bbe779.css" rel="prefetch"><link href="/static/js/chunk-12dc74e9.a7799079.js" rel="prefetch"><link href="/static/js/chunk-13b07ca5.11833bcd.js" rel="prefetch"><link href="/static/js/chunk-1653b664.4bba37ff.js" rel="prefetch"><link href="/static/js/chunk-233baa62.348949bb.js" rel="prefetch"><link href="/static/js/chunk-23726328.7a638dfb.js" rel="prefetch"><link href="/static/js/chunk-2ca39dde.bfb67629.js" rel="prefetch"><link href="/static/js/chunk-2d0aa612.2338a00b.js" rel="prefetch"><link href="/static/js/chunk-2d0b270c.82d7f897.js" rel="prefetch"><link href="/static/js/chunk-2d0c1eb0.2fc91e77.js" rel="prefetch"><link href="/static/js/chunk-2d0c229a.c6f13c92.js" rel="prefetch"><link href="/static/js/chunk-2d0cc2be.71e3fcd8.js" rel="prefetch"><link href="/static/js/chunk-2d0d5f97.57d7afa3.js" rel="prefetch"><link href="/static/js/chunk-2d0da3df.99de332b.js" rel="prefetch"><link href="/static/js/chunk-2d208116.4f67ac14.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.90a98553.js" rel="prefetch"><link href="/static/js/chunk-2d21b0dc.465e6abf.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.707bd994.js" rel="prefetch"><link href="/static/js/chunk-2d231217.5ff519da.js" rel="prefetch"><link href="/static/js/chunk-2d237d41.b4b87abb.js" rel="prefetch"><link href="/static/js/chunk-2f304dee.649e4dc7.js" rel="prefetch"><link href="/static/js/chunk-487896e7.69cdcafb.js" rel="prefetch"><link href="/static/js/chunk-49211740.e4dea096.js" rel="prefetch"><link href="/static/js/chunk-4dae396b.0ee6bb40.js" rel="prefetch"><link href="/static/js/chunk-5145872a.f0bd0577.js" rel="prefetch"><link href="/static/js/chunk-595ffc05.8affd7fe.js" rel="prefetch"><link href="/static/js/chunk-64076603.2c344ed9.js" rel="prefetch"><link href="/static/js/chunk-675c7703.7c7378cd.js" rel="prefetch"><link href="/static/js/chunk-792fd41e.aca41198.js" rel="prefetch"><link href="/static/js/chunk-7fae0422.0d9be069.js" rel="prefetch"><link href="/static/js/chunk-d22da0c0.da01e99e.js" rel="prefetch"><link href="/static/js/chunk-d28a86c4.d0c1f74e.js" rel="prefetch"><link href="/static/js/chunk-da9476ec.f8c15985.js" rel="prefetch"><link href="/static/js/chunk-ee62c128.c11fb53e.js" rel="prefetch"><link href="/static/css/app.a835db3a.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.9940ca10.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.948dc2e5.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.a835db3a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.948dc2e5.js"></script><script src="/static/js/app.9940ca10.js"></script></body></html> <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-12dc74e9.505ac0f9.css" rel="prefetch"><link href="/static/css/chunk-13b07ca5.029dd736.css" rel="prefetch"><link href="/static/css/chunk-1653b664.5b949e24.css" rel="prefetch"><link href="/static/css/chunk-23726328.7e460329.css" rel="prefetch"><link href="/static/css/chunk-2ca39dde.efa1eae8.css" rel="prefetch"><link href="/static/css/chunk-2f304dee.a8a2d99a.css" rel="prefetch"><link href="/static/css/chunk-487896e7.b7730bd4.css" rel="prefetch"><link href="/static/css/chunk-49211740.43a25f0f.css" rel="prefetch"><link href="/static/css/chunk-49531d8d.fc36911d.css" rel="prefetch"><link href="/static/css/chunk-4dae396b.92b3713e.css" rel="prefetch"><link href="/static/css/chunk-5145872a.197de139.css" rel="prefetch"><link href="/static/css/chunk-595ffc05.678c9c97.css" rel="prefetch"><link href="/static/css/chunk-64076603.e451beea.css" rel="prefetch"><link href="/static/css/chunk-675c7703.75b51be7.css" rel="prefetch"><link href="/static/css/chunk-792fd41e.4d467174.css" rel="prefetch"><link href="/static/css/chunk-7fae0422.c233115f.css" rel="prefetch"><link href="/static/css/chunk-d22da0c0.7c71cffb.css" rel="prefetch"><link href="/static/css/chunk-d28a86c4.cdd32c08.css" rel="prefetch"><link href="/static/css/chunk-da9476ec.f1965e2d.css" rel="prefetch"><link href="/static/css/chunk-ee62c128.44bbe779.css" rel="prefetch"><link href="/static/css/chunk-f8a20d76.8a0131ad.css" rel="prefetch"><link href="/static/js/chunk-12dc74e9.a7799079.js" rel="prefetch"><link href="/static/js/chunk-13b07ca5.11833bcd.js" rel="prefetch"><link href="/static/js/chunk-1653b664.4bba37ff.js" rel="prefetch"><link href="/static/js/chunk-23726328.7a638dfb.js" rel="prefetch"><link href="/static/js/chunk-2ca39dde.bfb67629.js" rel="prefetch"><link href="/static/js/chunk-2d0aa612.2338a00b.js" rel="prefetch"><link href="/static/js/chunk-2d0b270c.82d7f897.js" rel="prefetch"><link href="/static/js/chunk-2d0c1eb0.2fc91e77.js" rel="prefetch"><link href="/static/js/chunk-2d0c229a.c6f13c92.js" rel="prefetch"><link href="/static/js/chunk-2d0cc2be.71e3fcd8.js" rel="prefetch"><link href="/static/js/chunk-2d0d5f97.57d7afa3.js" rel="prefetch"><link href="/static/js/chunk-2d0da3df.99de332b.js" rel="prefetch"><link href="/static/js/chunk-2d208116.4f67ac14.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.90a98553.js" rel="prefetch"><link href="/static/js/chunk-2d21b0dc.465e6abf.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.707bd994.js" rel="prefetch"><link href="/static/js/chunk-2d231217.5ff519da.js" rel="prefetch"><link href="/static/js/chunk-2d237d41.b4b87abb.js" rel="prefetch"><link href="/static/js/chunk-2f304dee.649e4dc7.js" rel="prefetch"><link href="/static/js/chunk-487896e7.69cdcafb.js" rel="prefetch"><link href="/static/js/chunk-49211740.e4dea096.js" rel="prefetch"><link href="/static/js/chunk-49531d8d.1ccab0d9.js" rel="prefetch"><link href="/static/js/chunk-4dae396b.0ee6bb40.js" rel="prefetch"><link href="/static/js/chunk-5145872a.f0bd0577.js" rel="prefetch"><link href="/static/js/chunk-595ffc05.8affd7fe.js" rel="prefetch"><link href="/static/js/chunk-64076603.2c344ed9.js" rel="prefetch"><link href="/static/js/chunk-675c7703.7c7378cd.js" rel="prefetch"><link href="/static/js/chunk-792fd41e.aca41198.js" rel="prefetch"><link href="/static/js/chunk-7fae0422.0d9be069.js" rel="prefetch"><link href="/static/js/chunk-d22da0c0.da01e99e.js" rel="prefetch"><link href="/static/js/chunk-d28a86c4.d0c1f74e.js" rel="prefetch"><link href="/static/js/chunk-da9476ec.f8c15985.js" rel="prefetch"><link href="/static/js/chunk-ee62c128.c11fb53e.js" rel="prefetch"><link href="/static/js/chunk-f8a20d76.70a12fd2.js" rel="prefetch"><link href="/static/css/app.a835db3a.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.fe071759.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.948dc2e5.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.a835db3a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.948dc2e5.js"></script><script src="/static/js/app.fe071759.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,8 @@
{ {
"icons": { "icons": {
"camera.android.ipcam": {
"class": "fab fa-android"
},
"camera.cv": { "camera.cv": {
"class": "fas fa-camera" "class": "fas fa-camera"
}, },

View file

@ -129,160 +129,5 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
$camera-background: #101520; @import "common";
.camera {
width: 100%;
height: 100%;
background: $background-color;
overflow: auto;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3em;
.camera-container {
display: flex;
flex-direction: column;
align-items: center;
background: $camera-background;
.frame-container {
position: relative;
}
.frame, .no-frame {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
.frame {
z-index: 1;
}
.no-frame {
display: flex;
color: white;
align-items: center;
justify-content: center;
z-index: 2;
background: black;
}
.controls {
width: 100%;
display: flex;
border-top: 1px solid #202530;
padding: .5em .25em;
.left,.right {
width: 50%;
}
.right {
text-align: right;
}
button {
background: none;
color: white;
border: none;
&:hover {
color: $default-hover-fg-2;
}
}
}
}
.url {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 80%;
}
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
margin: 1em;
.row {
width: 100%;
display: flex;
align-items: center;
}
.name {
width: 140px;
}
input {
width: 500px;
font-weight: normal;
}
}
.params {
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
flex-direction: column;
margin: -2em;
label {
font-weight: normal;
}
.head {
display: flex;
justify-content: center;
label {
width: 100%;
display: flex;
justify-content: right;
.name {
margin-right: 1em;
}
}
}
.row {
width: 100%;
display: flex;
align-items: center;
padding: 0.5em 1em;
.name {
width: 30%;
}
input {
width: 70%;
}
&:nth-child(even) {
background: $default-bg-4;
}
&:hover {
background: $hover-bg;
}
}
}
.modal {
.content {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 90% !important;
}
}
}
}
</style> </style>

View file

@ -0,0 +1,156 @@
$camera-background: #101520;
.camera {
width: 100%;
height: 100%;
background: $background-color;
overflow: auto;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3em;
.camera-container {
display: flex;
flex-direction: column;
align-items: center;
background: $camera-background;
.frame-container {
position: relative;
}
.frame, .no-frame {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
.frame {
z-index: 1;
}
.no-frame {
display: flex;
color: white;
align-items: center;
justify-content: center;
z-index: 2;
background: black;
}
.controls {
width: 100%;
display: flex;
border-top: 1px solid #202530;
padding: .5em .25em;
.left,.right {
width: 50%;
}
.right {
text-align: right;
}
button {
background: none;
color: white;
border: none;
&:hover {
color: $default-hover-fg-2;
}
}
}
}
.url {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 80%;
}
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
margin: 1em;
.row {
width: 100%;
display: flex;
align-items: center;
}
.name {
width: 140px;
}
input {
width: 500px;
font-weight: normal;
}
}
.params {
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
flex-direction: column;
margin: -2em;
label {
font-weight: normal;
}
.head {
display: flex;
justify-content: center;
label {
width: 100%;
display: flex;
justify-content: right;
.name {
margin-right: 1em;
}
}
}
.row {
width: 100%;
display: flex;
align-items: center;
padding: 0.5em 1em;
.name {
width: 30%;
}
input {
width: 70%;
}
&:nth-child(even) {
background: $default-bg-4;
}
&:hover {
background: $hover-bg;
}
}
}
.modal {
.content {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 90% !important;
}
}
}
}

View file

@ -0,0 +1,285 @@
<template>
<div class="camera">
<Loading v-if="loading" />
<div class="camera-selector">
<div class="left">
<label>
<select ref="cameraSelector" @change="onCameraSelected">
<option selected disabled v-if="!Object.keys(cameras).length">-- No cameras available</option>
<option v-for="name in Object.keys(cameras)" :key="name" :value="name" v-text="name" />
</select>
</label>
</div>
<div class="right">
<button type="button" @click="updateCameraStatus" :disabled="loading">
<i class="fas fa-sync-alt" title="Refresh cameras" />
</button>
</div>
</div>
<div class="camera-container">
<div class="frame-container" ref="frameContainer">
<div class="no-frame" v-if="!streaming && !capturing && !captured">The camera is not active</div>
<img class="frame" ref="frame" alt="" src="">
</div>
<div class="controls">
<div class="left">
<button type="button" @click="startStreaming" :disabled="capturing || loading" v-if="!streaming">
<i class="fa fa-play" title="Start video" />
</button>
<button type="button" @click="stopStreaming" :disabled="capturing || loading" v-else>
<i class="fa fa-stop" title="Stop video" />
</button>
<button type="button" @click="capture" :disabled="streaming || capturing || loading">
<i class="fas fa-camera" title="Take a picture" />
</button>
</div>
<div class="right">
<button type="button" @click="flipCamera" :disabled="loading">
<i class="fas fa-retweet" title="Flip camera" />
</button>
<button type="button" @click="recording = true" v-if="!recording" :disabled="loading">
<i class="fa fa-volume-up" title="Start audio" />
</button>
<button type="button" @click="recording = false" v-else :disabled="loading">
<i class="fa fa-volume-mute" title="Stop audio" />
</button>
</div>
</div>
</div>
<div class="sound-container">
<audio autoplay preload="none" ref="player" v-if="recording">
<source :src="cameras[selectedCamera].audio_url" type="audio/x-wav;codec=pcm">
Your browser does not support audio elements
</audio>
</div>
</div>
</template>
<script>
import Utils from "@/Utils";
import Loading from "@/components/Loading";
export default {
name: "CameraAndroidIpcam",
components: {Loading},
mixins: [Utils],
data() {
return {
loading: false,
streaming: false,
capturing: false,
recording: false,
captured: false,
cameras: {},
selectedCamera: undefined,
}
},
computed: {
config() {
return this.$root.config['camera.android.ipcam']
},
},
methods: {
startStreaming() {
if (this.streaming)
return
const cam = this.cameras[this.selectedCamera]
this.streaming = true
this.capturing = false
this.captured = false
this.$refs.frame.setAttribute('src', cam.stream_url)
},
stopStreaming() {
if (!this.streaming)
return
this.streaming = false
this.capturing = false
this.$refs.frame.removeAttribute('src')
},
capture() {
if (this.capturing)
return
const cam = this.cameras[this.selectedCamera]
this.streaming = false
this.capturing = true
this.captured = true
this.$refs.frame.setAttribute('src', cam.image_url + '?t=' + (new Date()).getTime())
},
onFrameLoaded() {
if (this.capturing)
this.capturing = false
},
onCameraSelected(event) {
this.selectedCamera = event.target.value
},
async flipCamera() {
const cam = this.cameras[this.selectedCamera]
this.loading = true
try {
const value = !cam.ffc
await this.request('camera.android.ipcam.set_front_facing_camera', {
activate: value, camera: cam.name
})
this.cameras[this.selectedCamera].ffc = value
} finally {
this.loading = false
}
},
async updateCameraStatus() {
this.loading = true
try {
const cameras = await this.request('camera.android.ipcam.status')
this.cameras = cameras.reduce((cameras, cam) => {
for (const attr of ['stream_url', 'image_url', 'audio_url']) {
if (cam[attr].startsWith('https://')) {
cam[attr] = cam[attr].replace('https://', 'http://')
}
if (cam.name in this.config.cameras && this.config.cameras[cam.name].username) {
cam[attr] = 'http://' + this.config.cameras[cam.name].username + ':' +
this.config.cameras[cam.name].password + '@' + cam[attr].substr(7)
}
}
cameras[cam.name] = cam
return cameras
}, {})
if (cameras.length)
this.selectedCamera = cameras[0].name
} finally {
this.loading = false
}
},
},
mounted() {
this.$refs.frame.addEventListener('load', this.onFrameLoaded)
this.updateCameraStatus()
},
}
</script>
<style lang="scss" scoped>
@import "../Camera/common";
$controls-height: 3.5em;
.camera {
.camera-selector {
width: 100%;
height: $controls-height;
margin-top: -3em;
box-shadow: $border-shadow-bottom;
display: flex;
align-items: center;
.left,
.right {
display: flex;
}
.left {
width: 90%;
}
.right {
width: 10%;
justify-content: right;
}
label {
width: 100%;
padding-left: 1em;
select {
width: 100%;
}
}
button {
background: none;
border: none;
&:hover {
color: $default-hover-fg;
}
}
}
.camera-container {
margin-top: 2em;
min-width: 640px;
min-height: calc(480px + #{$controls-height});
.frame-container {
min-width: 640px;
min-height: 480px;
}
.controls {
height: $controls-height;
}
}
}
//.camera {
// min-height: 90%;
// overflow: auto;
// display: flex;
// flex-direction: column;
// align-items: center;
//
// .camera-container {
// min-width: 640px;
// min-height: 480px;
// position: relative;
// background: black;
// margin-bottom: 1em;
//
// .frame, .no-frame {
// position: absolute;
// top: 0;
// width: 100%;
// height: 100%;
// }
//
// .frame {
// z-index: 1;
// }
//
// .no-frame {
// display: flex;
// background: rgba(0, 0, 0, 0.1);
// color: white;
// align-items: center;
// justify-content: center;
// z-index: 2;
// }
// }
//}
</style>