add dashboard, fix: ffplayout:#415
This commit is contained in:
parent
fead22adea
commit
4e742861df
@ -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)
|
||||
|
||||
|
@ -42,6 +42,7 @@ $log-server: #23cbdd;
|
||||
|
||||
$theme-colors: (
|
||||
'primary': $primary,
|
||||
'secondary': $secondary,
|
||||
);
|
||||
|
||||
$b-radius: 3px;
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
@ -25,11 +71,11 @@ 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 },
|
||||
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)
|
||||
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>
|
||||
|
@ -1,33 +1,29 @@
|
||||
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
|
||||
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>')
|
||||
@ -41,7 +37,6 @@ export const stringFormatter = () => {
|
||||
.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
BIN
docs/images/dasboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user