platypush/platypush/backend/http/webapp/src/components/panels/CameraAndroidIpcam/Index.vue

286 lines
6.7 KiB
Vue

<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-mute" title="Start audio" />
</button>
<button type="button" @click="recording = false" v-else :disabled="loading">
<i class="fa fa-volume-up" 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>