add dashboard, fix: ffplayout:#415

This commit is contained in:
jb-alvarado 2023-11-16 15:45:46 +01:00
parent fead22adea
commit 4e742861df
7 changed files with 146 additions and 77 deletions

View File

@ -12,6 +12,9 @@ Read the [install.md](docs/INSTALL.md) for manual installation.
### Login
![login](/docs/images/login.png)
### Login
![login](/docs/images/dasboard.png)
### Landing Page
![landing](/docs/images/landing.png)

View File

@ -42,6 +42,7 @@ $log-server: #23cbdd;
$theme-colors: (
'primary': $primary,
'secondary': $secondary,
);
$b-radius: 3px;

View File

@ -24,6 +24,9 @@
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item">
<NuxtLink class="btn btn-primary btn-sm" to="/">Home</NuxtLink>
</li>
<li class="nav-item">
<NuxtLink class="btn btn-primary btn-sm" to="/player">Player</NuxtLink>
</li>

View File

@ -1,14 +1,60 @@
<template>
<div class="row sys-container text-start">
<div class="col-4 h-100 bg-warning">
{{ sysStat.system.name }} {{ sysStat.system.version }} {{ sysStat.system.kernel }}
<div class="row sys-container text-start bg-secondary border border-3 rounded">
<div class="col-6 bg-primary p-2 border-top border-start border-end fs-1">
{{ sysStat.system.name }} {{ sysStat.system.version }}
</div>
<div class="col-6 p-2 border">
<div class="fs-4">CPU</div>
<div class="row">
<div class="col"><strong>Cores:</strong> {{ sysStat.cpu.cores }}</div>
<div class="col"><strong>Usage:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
</div>
</div>
<div v-if="sysStat.system.kernel" class="col-6 bg-primary border-start border-end border-bottom">
{{ sysStat.system.kernel }}
</div>
<div class="col-6 p-2 border">
<div class="fs-4">Load</div>
<div class="row">
<div class="col">{{ sysStat.load.one }}</div>
<div class="col">{{ sysStat.load.five }}</div>
<div class="col">{{ sysStat.load.fifteen }}</div>
</div>
</div>
<div class="col-6 border">
<div class="fs-4">Memory</div>
<div class="row">
<div class="col"><strong>Total:</strong> {{ fileSize(sysStat.memory.total) }}</div>
<div class="col"><strong>Usage:</strong> {{ fileSize(sysStat.memory.used) }}</div>
</div>
</div>
<div class="col-6 p-2 border">
<div class="fs-4">Swap</div>
<div class="row">
<div class="col"><strong>Total:</strong> {{ fileSize(sysStat.swap.total) }}</div>
<div class="col"><strong>Usage:</strong> {{ fileSize(sysStat.swap.used) }}</div>
</div>
</div>
<div class="col-6 p-2 border">
<div class="fs-4">
Network <span v-if="sysStat.network" class="fs-6">{{ sysStat.network?.name }}</span>
</div>
<div class="row">
<div class="col-6"><strong>In:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
<div class="col-6"><strong>Out:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
<div class="col-6">{{ fileSize(sysStat.network?.total_in) }}</div>
<div class="col-6">{{ fileSize(sysStat.network?.total_out) }}</div>
</div>
</div>
<div class="col-6 p-2 border">
<div class="fs-4">Storage</div>
<div v-if="sysStat.storage"><strong>Device:</strong> {{ sysStat.storage?.path }}</div>
<div class="row" v-if="sysStat.storage">
<div class="col"><strong>Size:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
<div class="col"><strong>Used:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
</div>
</div>
<div class="col-5">CPU Cores: {{ sysStat.cpu.cores }} Used {{ sysStat.cpu.usage }}</div>
<div class="col-5">Load: {{ sysStat.load.one }} | {{ sysStat.load.five }} | {{ sysStat.load.fifteen }}</div>
<div class="col-5">Memory Total: {{ fileSize(sysStat.memory.total) }} Used: {{ fileSize(sysStat.memory.used) }}</div>
<div class="col-5">Network Name: {{ sysStat.network?.name }} In: {{ fileSize(sysStat.network?.current_in) }}</div>
<div class="col-5">Storage Path: {{ sysStat.storage?.path }} Size: {{ fileSize(sysStat.storage?.total) }} Used: {{fileSize(sysStat.storage?.used)}}</div>
<div class="col-5">Swap Total: {{ fileSize(sysStat.swap.total) }} Used: {{ fileSize(sysStat.swap.used) }}</div>
</div>
</template>
<script setup lang="ts">
@ -22,14 +68,14 @@ const configStore = useConfig()
const contentType = { 'content-type': 'application/json,charset=UTF-8' }
const timer = ref()
const sysStat = ref({
cpu: { cores: 0.0, usage: 0.0 },
load: { one: 0.0, five: 0.0, fifteen: 0.0 },
memory: { total: 0.0, used: 0.0, free: 0.0 },
network: { name: "", current_in: 0.0, current_out: 0.0, total_in: 0.0, total_out: 0.0 },
storage: { path: "", total: 0.0, used: 0.0 },
swap: { total: 0.0, used: 0.0, free: 0.0 },
system: { name: "", kernel: "", version: "" },
} as SystemStatistics)
cpu: { cores: 0.0, usage: 0.0 },
load: { one: 0.0, five: 0.0, fifteen: 0.0 },
memory: { total: 0.0, used: 0.0, free: 0.0 },
network: { name: '', current_in: 0.0, current_out: 0.0, total_in: 0.0, total_out: 0.0 },
storage: { path: '', total: 0.0, used: 0.0 },
swap: { total: 0.0, used: 0.0, free: 0.0 },
system: { name: '', kernel: '', version: '' },
} as SystemStatistics)
onMounted(() => {
status()
@ -58,17 +104,17 @@ async function status() {
async function systemStatus() {
const channel = configStore.configGui[configStore.configID].id
await $fetch(`/api/system/${channel}`, {
await $fetch<SystemStatistics>(`/api/system/${channel}`, {
method: 'GET',
headers: { ...contentType, ...authStore.authHeader },
}).then((stat: SystemStatistics) => {
console.log(stat)
sysStat.value = stat
})
}
</script>
<style>
.sys-container {
min-height: 500px,
max-width: 640px;
min-height: 300px;
}
</style>

View File

@ -1,47 +1,42 @@
export const stringFormatter = () => {
function fileSize(bytes: number | undefined, si=false, dp=1) {
function fileSize(bytes: number | undefined, dp = 2) {
if (!bytes) {
return 0.0
}
const thresh = si ? 1000 : 1024;
const thresh = 1024
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
return bytes + ' B'
}
const units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10**dp;
const units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
let u = -1
const r = 10 ** dp
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
bytes /= thresh
++u
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1)
return bytes.toFixed(dp) + ' ' + units[u];
return bytes.toFixed(dp) + ' ' + units[u]
}
function formatLog(text: string) {
return (
text
.replace(/\x1B\[33m(.*?)\x1B\[0m/g, '<span class="log-number">$1</span>')
.replace(/\x1B\[1m\x1B\[35m(.*?)\x1B\[0m\x1B\[22m/g, '<span class="log-addr">$1</span>')
.replace(/\x1B\[94m(.*?)\x1B\[0m/g, '<span class="log-cmd">$1</span>')
.replace(/\x1B\[90m(.*?)\x1B\[0m/g, '<span class="log-debug">$1</span>')
.replace(/(\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.[\d]+\])/g, '<span class="log-time">$1</span>')
.replace(/\[ INFO\]/g, '<span class="log-info">[ INFO]</span>')
.replace(/\[ WARN\]/g, '<span class="log-warning">[ WARN]</span>')
.replace(/\[ERROR\]/g, '<span class="log-error">[ERROR]</span>')
.replace(/\[DEBUG\]/g, '<span class="log-debug">[DEBUG]</span>')
.replace(/\[Decoder\]/g, '<span class="log-decoder">[Decoder]</span>')
.replace(/\[Encoder\]/g, '<span class="log-encoder">[Encoder]</span>')
.replace(/\[Server\]/g, '<span class="log-server">[Server]</span>')
.replace(/\[Validator\]/g, '<span class="log-server">[Validator]</span>')
)
return text
.replace(/\x1B\[33m(.*?)\x1B\[0m/g, '<span class="log-number">$1</span>')
.replace(/\x1B\[1m\x1B\[35m(.*?)\x1B\[0m\x1B\[22m/g, '<span class="log-addr">$1</span>')
.replace(/\x1B\[94m(.*?)\x1B\[0m/g, '<span class="log-cmd">$1</span>')
.replace(/\x1B\[90m(.*?)\x1B\[0m/g, '<span class="log-debug">$1</span>')
.replace(/(\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.[\d]+\])/g, '<span class="log-time">$1</span>')
.replace(/\[ INFO\]/g, '<span class="log-info">[ INFO]</span>')
.replace(/\[ WARN\]/g, '<span class="log-warning">[ WARN]</span>')
.replace(/\[ERROR\]/g, '<span class="log-error">[ERROR]</span>')
.replace(/\[DEBUG\]/g, '<span class="log-debug">[DEBUG]</span>')
.replace(/\[Decoder\]/g, '<span class="log-decoder">[Decoder]</span>')
.replace(/\[Encoder\]/g, '<span class="log-encoder">[Encoder]</span>')
.replace(/\[Server\]/g, '<span class="log-server">[Server]</span>')
.replace(/\[Validator\]/g, '<span class="log-server">[Validator]</span>')
}
function timeToSeconds(time: string) {
@ -99,9 +94,40 @@ export const stringFormatter = () => {
}
function mediaType(path: string) {
const videoType = ['avi', 'flv', 'm2v', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mts', 'mxf', 'ts', 'vob','ogv', 'webm', 'wmv']
const videoType = [
'avi',
'flv',
'm2v',
'm4v',
'mkv',
'mov',
'mp4',
'mpeg',
'mpg',
'mts',
'mxf',
'ts',
'vob',
'ogv',
'webm',
'wmv',
]
const audioType = ['aac', 'aiff', 'flac', 'm4a', 'mp2', 'mp3', 'ogg', 'opus', 'wav', 'wma']
const imageType = ['apng', 'avif', 'bmp', 'exr', 'gif', 'jpeg', 'jpg', 'png', 'psd', 'tga', 'tif', 'tiff', 'webp']
const imageType = [
'apng',
'avif',
'bmp',
'exr',
'gif',
'jpeg',
'jpg',
'png',
'psd',
'tga',
'tif',
'tiff',
'webp',
]
const ext = path.split('.').pop()
if (ext) {
@ -117,15 +143,23 @@ export const stringFormatter = () => {
return null
}
return { fileSize, formatLog, timeToSeconds, secToHMS, numberToHex, hexToNumber, filename, toMin, secondsToTime, mediaType }
return {
fileSize,
formatLog,
timeToSeconds,
secToHMS,
numberToHex,
hexToNumber,
filename,
toMin,
secondsToTime,
mediaType,
}
}
export const playlistOperations = () => {
function genUID() {
return String(
Date.now().toString(32) +
Math.random().toString(16)
).replace(/\./g, '')
return String(Date.now().toString(32) + Math.random().toString(16)).replace(/\./g, '')
}
function processPlaylist(dayStart: number, length: number, list: PlaylistItem[], forSave: boolean) {

BIN
docs/images/dasboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -3,10 +3,8 @@
<div v-if="authStore.isLogin">
<div class="container login-container">
<div>
<div class="logo-div">
<SystemStats />
</div>
<div class="actions">
<SystemStats class="mx-auto" />
<div class="text-center mt-5">
<div class="btn-group actions-grp btn-group-lg" role="group">
<NuxtLink to="/player" class="btn btn-primary">Player</NuxtLink>
<NuxtLink to="/media" class="btn btn-primary">Media</NuxtLink>
@ -23,7 +21,7 @@
<div class="logout-div" />
<div class="container login-container">
<div>
<div class="header">
<div class="text-center mb-5">
<h1>ffplayout</h1>
</div>
@ -127,17 +125,6 @@ async function logout() {
height: 100vh;
}
.header {
text-align: center;
margin-bottom: 3em;
}
.logo-div {
width: 100%;
text-align: center;
margin-bottom: 5em;
}
.login-form {
min-width: 300px;
}
@ -151,11 +138,6 @@ async function logout() {
}
}
.actions {
text-align: center;
margin-top: 1em;
}
@media (max-width: 380px) {
.actions-grp {
display: flex;