ArgusSite/static/templates/videos/playback.html
2023-09-19 14:11:08 +03:00

832 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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.0">
<title>Записи</title>
<link rel="stylesheet" href="../styles/main.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
</head>
<body>
<header>
<img src="../img/argus.png">
<h1>Аргус</h1>
<h2><span>/</span> {{Organisation}}</h2>
</header>
<section class="account-info">
<div id="account-main">
<img id="person" src="../img/person.svg">
<span>{{User}}</span>
<img id="down" src="../img/down.svg">
<img id="up" src="../img/up.svg">
</div>
<a href="/logout"><div id="account-additional" class="additional">Выйти</div></a>
</section>
<section class="navigation">
<a href="/">
<div><img src="../img/chart.svg">Главная</div>
</a>
<a href="/devices">
<div><img src="../img/cloud.svg">Устройства</div>
</a>
<a href="/reports">
<div><img src="../img/bubble.svg">Отчёты</div>
</a>
<a href="/live">
<div><img src="../img/waves.svg">Трансляция</div>
</a>
<a href="/videos">
<div class="selected"><img src="../img/play.svg">Записи</div>
</a>
{{#if isAdmin}}
<a class="admin-panel" href="/admin">
<div><img src="../img/keyboard.svg">Админка</div>
</a>
{{/if}}
<a class="settings" href="/settings">
<div><img src="../img/gear.svg">Настройки</div>
</a>
</section>
<section class="main">
<section style="display: none;" class="dberror" id="dataLoading" >
<div class="loader-container">
<div class="loader">
<div class="square" id="sq11"></div>
<div class="square" id="sq12"></div>
<div class="square" id="sq13"></div>
<div class="square" id="sq14"></div>
<div class="square" id="sq15"></div>
<div class="square" id="sq16"></div>
<div class="square" id="sq17"></div>
<div class="square" id="sq18"></div>
<div class="square" id="sq19"></div>
</div>
</div>
</section>
{{#if ifDBError}}
<section class="dberror">
<div class="erorr-container">
<img src="../img/warning.svg"> <br>
<h1>Ошибка </h1> <br>
<span>Не удалось получить данные из БД</span>
<button type="button" onclick="location.reload();">Повторить попытку</button>
</div>
</section>
{{/if}}
<nav>
<a class="selected" href="/videos">Воспроизведение</a>
<a href="/videos/export">Экспорт</a>
</nav>
<section class="bg">
<section class="content">
<section style="min-height: 800px;" class="for-table">
<section class="organisation">
<h1>Организация</h1>
<ul class="area">
{{#each Groups}}
<li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="{{name}}" class="checkbox-input" hidden checked><label for="{{name}}" class="checkbox-label">{{name}}</label>
<ul class="area-devices" id="devices-1">
{{#each devices}}
<li class="device">
<img>
<input type="number" id="channels-{{this.serial}}" value="{{this.channels}}" hidden>
<input type="radio" name="camera-serial" id="radio-{{this.serial}}" class="radio-input" value="{{this.serial}}" hidden>
<label for="radio-{{this.serial}}" class="radio-label active-{{this.status}}">
{{this.serial}}
</label>
</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</section>
<section class="table" style="position: relative;">
<div class="map">
<div id="map"></div>
</div>
<div style="background: white;" class="cameras">
<section style="border: 0;" class="whole-width">
<h1>Запустить запись с камеры</h1>
<h3>Выберите камеру, время и нажмите кнопку "Запустить запись"</h3>
<form id="edit-group-form">
<div class="parameters-input">
<label for="group-id">Камера</label>
<select name="group-id" id="group-id">
<option value="">Выберите номер камеры</option>
<option value="1">Камера 1</option>
<option value="2">Камера 2</option>
<option value="3">Камера 3</option>
<option value="4">Камера 4</option>
<option value="5">Камера 5</option>
<option value="6">Камера 6</option>
<option value="7">Камера 7</option>
<option value="8">Камера 8</option>
<option value="9">Камера 9</option>
<option value="10">Камера 10</option>
<option value="11">Камера 11</option>
<option value="12">Камера 12</option>
<option value="13">Камера 13</option>
</select>
</div>
</form>
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
<button style="margin-top: 15px;" id="group-edit" class="button-purple" type="button" onclick="playVideo();">Запустить запись</button>
</section>
</div>
<div class="calendar">
<div class="calendar-header">
<button id="prevMonth"></button>
<h2 id="monthYear"></h2>
<button id="nextMonth"></button>
</div>
<div class="daysOfWeek">
<div>Пн</div>
<div>Вт</div>
<div>Ср</div>
<div>Чт</div>
<div>Пт</div>
<div>Сб</div>
<div>Вс</div>
</div>
<div class="dates" id="dates">
</div>
</div>
<input type="hidden" id="selectedDate" name="selectedDate" hidden>
<div class="speedometr">
<div style="display: none;" id="speed-bg" class="speed-bg">
<h1>Данных для выбранного периода нет.</h1>
</div>
<h1>Скорость</h1>
<span>км/ч</span>
<span style="float: right; font-size: 18px;">1 ч</span>
<div>
<canvas id="speed"></canvas>
</div>
</div>
<div class="video-time">
<input name="videoTime" type="time" id="video-time" step="1">
</div>
</section>
</section>
</section>
</section>
</section>
<style>
.table {
background-color:#f7f7fa !important;
}
.speedometr {
position: absolute;
right: 0;
bottom: 62px;
width: calc(75% - 300px - 10px - 32px);
height: 220px;
border-radius: 15px;
background-color: white;
padding-right: 32px;
}
.speedometr div {
height: 190px;
}
</style>
<script src="../scripts/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
function updateCameraOptions() {
var selectedSerial = document.querySelector('input[name="camera-serial"]:checked').value;
var channelsInput = document.querySelector('#channels-' + selectedSerial);
var numChannels = parseInt(channelsInput.value, 10);
var select = document.querySelector('#group-id');
select.innerHTML = '';
var defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = 'Выберите номер камеры';
select.appendChild(defaultOption);
for (var i = 1; i <= numChannels; i++) {
var option = document.createElement('option');
option.value = i;
option.textContent = 'Камера ' + i;
select.appendChild(option);
}
}
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const areaNames = document.querySelectorAll('.area-name');
areaNames.forEach(function (areaName) {
const areaCheckbox = areaName.querySelector('.checkbox-input');
const deviceCheckboxes = areaName.querySelectorAll('.device .checkbox-input');
const deviceList = areaName.querySelector('.area-devices');
// Функция для скрытия/показа дочерних элементов
function toggleChildDevices(show) {
if (show) {
deviceList.style.display = 'block';
} else {
deviceList.style.display = 'none';
}
}
// Инициализация состояния чекбоксов и скрытия/показа дочерних элементов
toggleChildDevices(areaCheckbox.checked);
deviceCheckboxes.forEach(function (deviceCheckbox) {
deviceCheckbox.checked = areaCheckbox.checked;
});
areaCheckbox.addEventListener('change', function () {
const isChecked = areaCheckbox.checked;
deviceCheckboxes.forEach(function (deviceCheckbox) {
deviceCheckbox.checked = isChecked;
});
toggleChildDevices(isChecked);
});
deviceCheckboxes.forEach(function (deviceCheckbox) {
deviceCheckbox.addEventListener('change', function () {
const allUnchecked = Array.from(deviceCheckboxes).every(function (checkbox) {
return !checkbox.checked;
});
if (allUnchecked) {
areaCheckbox.checked = false;
toggleChildDevices(false);
} else {
areaCheckbox.checked = true;
toggleChildDevices(true);
}
});
});
});
});
</script>
<script>
function combineDateTime(dateString, timeString) {
const date = new Date(dateString);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
// Разбиение времени на часы, минуты и секунды
const timeParts = timeString.split(":");
const hours = timeParts[0].padStart(2, "0");
const minutes = timeParts[1].padStart(2, "0");
const seconds = timeParts[2].padStart(2, "0");
// Собираем дату и время в нужном формате
const combinedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return combinedDateTime;
}
let HasData;
var selectedChannel = 1;
async function sendPostRequest() {
updateCameraOptions();
document.getElementById("dataLoading").style.display = 'flex';
// Получение данных из полей ввода
const selectedDate = document.getElementById("selectedDate").value;
const videoTime = document.getElementById("video-time").value;
const selectedSerial = document.querySelector('input[name="camera-serial"]:checked').value;
// Объединяем дату и время и преобразуем в нужный формат
const combinedDateTime = combineDateTime(selectedDate, videoTime);
const response = await fetch(`/getData?serial=${selectedSerial}&selectedDate=${formatDate(selectedDate)}&selectedTime=${formatTime(videoTime)}&selectedChannel=${selectedChannel}`);
const data = await response.json();
HasData = data.success;
if (data.success) {
console.log(`Данные доступны. DATAID: ${data.dataId}`)
const requestData = {
serial: selectedSerial,
datetime: combinedDateTime,
};
fetch("/getspeedarchive", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestData),
})
.then((response) => response.json())
.then((data) => {
document.getElementById("dataLoading").style.display = 'none';
const existingChart = Chart.getChart("speed");
if (existingChart) {
existingChart.destroy();
}
const numberOfLabels = data.speeds.length;
// const labels = Array.from({ length: numberOfLabels }, () => "");
// Обновление данных графика
const chart = new Chart("speed", {
type: "line",
data: {
labels: data.names,
datasets: [
{
label: "Скорость",
borderColor: "#8086F9",
fill: false,
data: data.speeds,
pointStyle: false,
pointRadius: 25,
pointHoverRadius: 25,
tension: 0.1,
},
],
},
options: {
plugins: {
legend: {
display: false,
},
},
labelStep: "3",
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
stacked: true,
grid: {
display: true,
color: "#D9D9D9",
},
ticks: {
stepSize: 10,
},
},
x: {
ticks: {
autoSkip: false,
callback: function(value, index, values) {
const data = this.chart.data.labels;
if (index === 0 || index === values.length - 1) {
return data[index];
} else {
return '';
}
},
position: 'bottom',
maxRotation: 0,
},
grid: {
display: false,
},
},
},
},
});
const geoData = data.geo;
// Очищаем все слои на карте
map.eachLayer(layer => {
if (layer !== map) {
map.removeLayer(layer);
}
});
// Добавляем слой с плитками OpenStreetMap
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}').addTo(map);
// Создаем слой для маршрута
const routeLayer = L.layerGroup().addTo(map);
// Создаем слой для маркеров
const markerLayer = L.layerGroup().addTo(map);
// Преобразуем координаты точек маршрута
const routePoints = geoData.map(point => [point.latitude, point.longitude]);
// Создаем линию маршрута
const route = L.polyline(routePoints, { color: 'red',weight: 5 }).addTo(routeLayer);
})
.catch((error) => {
document.getElementById("dataLoading").style.display = 'none';
var h1Element = document.querySelector('.speedometr h1');
h1Element.textContent = 'Ошибка отправки запроса.';
console.error("Ошибка при отправке запроса:", error);
});
} else {
const existingChart = Chart.getChart("speed");
if (existingChart) {
existingChart.destroy();
}
const chart = new Chart("speed", {
type: "line",
data: {
labels: "",
datasets: [
{
label: "Скорость",
borderColor: "#8086F9",
fill: false,
data: "",
pointStyle: false,
pointRadius: 25,
pointHoverRadius: 25,
tension: 0.1,
},
],
},
options: {
plugins: {
legend: {
display: false,
},
},
labelStep: "3",
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
stacked: true,
grid: {
display: true,
color: "#D9D9D9",
},
ticks: {
stepSize: 10,
},
},
x: {
grid: {
display: false,
},
},
},
},
});
const speedBG = document.getElementById("speed-bg");
speedBG.style.display = 'flex';
}
}
const radioInputs = document.querySelectorAll(".radio-input");
radioInputs.forEach((input) => {
input.addEventListener("change", sendPostRequest);
});
const selectedDateInput = document.getElementById("selectedDate");
selectedDateInput.addEventListener("change", sendPostRequest);
const videoTimeInput = document.getElementById("video-time");
videoTimeInput.addEventListener("blur", sendPostRequest);
</script>
<script>
var now = new Date();
now.setHours(now.getHours() - 1);
var formattedTime = now.toISOString().substr(11, 8);
document.getElementById("video-time").value = formattedTime;
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
const prevMonthBtn = document.getElementById("prevMonth");
const nextMonthBtn = document.getElementById("nextMonth");
const monthYear = document.getElementById("monthYear");
const datesContainer = document.getElementById("dates");
const dateForm = document.getElementById("dateForm");
const selectedDateInput = document.getElementById("selectedDate");
let currentDate = new Date();
let selectedDate = currentDate;
selectedDateInput.value = selectedDate.toISOString();
function renderCalendar() {
// Очистить предыдущий календарь
datesContainer.innerHTML = "";
// Установить заголовок с месяцем и годом
const options = { year: "numeric", month: "long" };
const formattedDate = currentDate.toLocaleDateString("ru-RU", options);
const formattedMonthYear = formattedDate.charAt(0).toUpperCase() + formattedDate.slice(1);
monthYear.textContent = formattedMonthYear.replace('г.', '');
// Найти первый день текущего месяца
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
// Определить день недели, с которого начнется месяц
const startingDay = firstDayOfMonth.getDay() - 1;
// Определить количество дней в текущем месяце
const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate();
// Создать дни месяца
for (let i = 0; i < startingDay; i++) {
const emptyDay = document.createElement("div");
emptyDay.classList.add("empty-day");
datesContainer.appendChild(emptyDay);
}
for (let day = 1; day <= daysInMonth; day++) {
const dateCell = document.createElement("div");
dateCell.textContent = day;
dateCell.classList.add("date");
if (selectedDate.getDate() === day && selectedDate.getMonth() === currentDate.getMonth() && selectedDate.getFullYear() === currentDate.getFullYear()) {
dateCell.classList.add("selected");
}
dateCell.addEventListener("click", () => {
selectedDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), day);
renderCalendar();
selectedDateInput.value = selectedDate.toISOString();
sendPostRequest();
});
datesContainer.appendChild(dateCell);
}
}
// Перейти на предыдущий месяц
prevMonthBtn.addEventListener("click", () => {
currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
renderCalendar();
});
// Перейти на следующий месяц
nextMonthBtn.addEventListener("click", () => {
currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1);
renderCalendar();
});
// Инициализировать календарь
renderCalendar();
});
</script>
<script>
function formatDate(selectedDate) {
const date = new Date(selectedDate);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}${month}${day}`;
}
function formatTime(videoTime) {
const parts = videoTime.split(':');
const hours = parts[0].toString().padStart(2, '0');
const minutes = parts[1].toString().padStart(2, '0');
const seconds = parts[2].toString().padStart(2, '0');
return `${hours}${minutes}${seconds}`;
}
function addOneHourToTime(videoTime) {
const parts = videoTime.split(':');
let hours = parseInt(parts[0], 10);
hours += 1;
const minutes = parts[1].toString().padStart(2, '0');
const seconds = parts[2].toString().padStart(2, '0');
return formatTime(`${hours}:${minutes}:${seconds}`);
}
function playVideo() {
const channel = document.getElementById("group-id").value;
const selectedDevice = document.querySelector('input[name="camera-serial"]:checked');
if (!selectedDevice) {
alert('Пожалуйста, выберите устройство из списка.');
return;
}
if (channel === "") {
alert('Пожалуйста, выберите камеру из списка.');
return;
}
if (!HasData) {
alert('Пожалуйста, выберите другой временной период.');
return;
}
const startTimeInput = document.getElementById('video-time');
const selectedDateInput = document.getElementById('selectedDate');
const startTime = formatTime(startTimeInput.value);
const selectedDate = formatDate(selectedDateInput.value);
const endTime = addOneHourToTime(startTimeInput.value);
const serial = selectedDevice.value;
selectedChannel = channel;
const url = `http://localhost:8081/playback?url=http%3A%2F%2Fkrbl.ru%3A8080%2Fhttp%2Fplayback.flv%3Fserial%3D${serial}%26channel%3D${channel}%26quality%3D1%26queryTime%3D${selectedDate}%26startTime%3D${startTime}%26endTime%3D${endTime}`;
window.open(url, '_blank');
}
</script>
<script>
window.addEventListener('DOMContentLoaded', function() {
var mapContainer = document.querySelector('.map');
var mapArea = document.getElementById('map');
mapArea.style.height = (mapContainer.clientHeight) + 'px';
mapArea.style.width = (mapContainer.clientWidth) + 'px';
});
window.addEventListener("resize", function (event) {
var mapContainer = document.querySelector('.map');
var mapArea = document.getElementById('map');
mapArea.style.height = (mapContainer.clientHeight) + 'px';
mapArea.style.width = (mapContainer.clientWidth) + 'px';
});
var startPoint = [0, 0];
var endPoint = [0, 0];
let map;
// Создаем карту Leaflet
map = L.map('map').setView([59.855198, 30.282995], 10);
// Добавляем базовый слой OpenStreetMap
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}').addTo(map);
// Создаем маркеры
var startMarker = L.marker(startPoint, {
icon: L.divIcon({
className: 'custom-icon',
html: '<div style="background-color: red; width: 14px; height: 14px; border: 2px solid white; border-radius: 50%;"></div>'
})
}).addTo(map);
var endMarker = L.marker(endPoint, {
icon: L.divIcon({
className: 'custom-icon',
html: '<div style="background-color: red; width: 14px; height: 14px; border: 2px solid white; border-radius: 50%;"></div>'
})
}).addTo(map);
// Создаем линию маршрута
var route = L.polyline([startPoint, endPoint], { color: 'red', weight: 6 }).addTo(map);
// Скрыть кнопки приближения/отдаления
map.zoomControl.remove();
// Скрыть информационную панель
map.attributionControl.remove();
</script>
<script>
// Скрытие/Показ дополнительных меню аккаунта
const accountMain = document.getElementById('account-main');
const accountAdditional = document.getElementById('account-additional');
const accountUp = document.getElementById('up');
const accountDown = document.getElementById('down');
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountMain.addEventListener('click', () => {
if (accountAdditional.style.display === 'none') {
accountAdditional.style.display = 'flex';
accountUp.style.display = 'unset';
accountDown.style.display = 'none';
} else {
accountAdditional.style.display = 'none';
accountUp.style.display = 'none';
accountDown.style.display = 'unset';
}
});
</script>
<script>
Chart.defaults.color = "rgba(0, 0, 0, 0.4)";
Chart.defaults.font.size = 15;
Chart.defaults.font.weight = 400;
var speedData = {
labels: [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
],
datasets: [
{
label: "Скорость",
borderColor: "#8086F9",
fill: false,
data: "",
pointStyle: false,
pointRadius: 25,
pointHoverRadius: 25,
tension: 0.1,
},
],
};
var speedOptions = {
plugins: {
legend: {
display: false,
},
},
labelStep: "3",
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
stacked: true,
grid: {
display: true,
color: "#D9D9D9",
},
ticks: {
stepSize: 10,
},
},
x: {
grid: {
display: false,
},
},
},
};
new Chart("speed", {
type: "line",
data: speedData,
options: speedOptions,
});
</script>
</body>
</html>