Auto-fix ESlint rules.

This commit is contained in:
Buster Neece 2022-12-29 15:15:05 -06:00
parent 04fc47ee1e
commit e3b877cc9c
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
214 changed files with 8394 additions and 3132 deletions

View File

@ -5,52 +5,100 @@
</h2>
<b-row>
<b-col sm="12" md="6" lg="5">
<section class="card mb-3" role="region">
<b-col
sm="12"
md="6"
lg="5"
>
<section
class="card mb-3"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Profile') }}</h2>
<h2 class="card-title">
{{ $gettext('Profile') }}
</h2>
</b-card-header>
<b-overlay variant="card" :show="userLoading">
<b-overlay
variant="card"
:show="userLoading"
>
<b-card-body body-class="card-padding-sm">
<b-media right-align vertical-align="center">
<template v-if="user.avatar.url" #aside>
<avatar :url="user.avatar.url" :service="user.avatar.service"
:service-url="user.avatar.serviceUrl"></avatar>
<b-media
right-align
vertical-align="center"
>
<template
v-if="user.avatar.url"
#aside
>
<avatar
:url="user.avatar.url"
:service="user.avatar.service"
:service-url="user.avatar.serviceUrl"
/>
</template>
<h2 v-if="user.name" class="card-title">{{ user.name }}</h2>
<h2 v-else class="card-title">
<h2
v-if="user.name"
class="card-title"
>
{{ user.name }}
</h2>
<h2
v-else
class="card-title"
>
{{ $gettext('AzuraCast User') }}
</h2>
<h3 class="card-subtitle">{{ user.email }}</h3>
<h3 class="card-subtitle">
{{ user.email }}
</h3>
<div v-if="user.roles.length > 0" class="mt-2">
<span v-for="role in user.roles" :key="role.id"
class="badge badge-secondary mr-2">{{ role.name }}</span>
<div
v-if="user.roles.length > 0"
class="mt-2"
>
<span
v-for="role in user.roles"
:key="role.id"
class="badge badge-secondary mr-2"
>{{ role.name }}</span>
</div>
</b-media>
</b-card-body>
</b-overlay>
<div class="card-actions">
<b-button variant="outline-primary" @click.prevent="doEditProfile">
<icon icon="edit"></icon>
<b-button
variant="outline-primary"
@click.prevent="doEditProfile"
>
<icon icon="edit" />
{{ $gettext('Edit Profile') }}
</b-button>
</div>
</section>
<section class="card" role="region">
<section
class="card"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Security') }}</h2>
<h2 class="card-title">
{{ $gettext('Security') }}
</h2>
</b-card-header>
<b-overlay variant="card" :show="securityLoading">
<b-overlay
variant="card"
:show="securityLoading"
>
<b-card-body>
<h5>
{{ $gettext('Two-Factor Authentication') }}
<enabled-badge :enabled="security.twoFactorEnabled"></enabled-badge>
<enabled-badge :enabled="security.twoFactorEnabled" />
</h5>
<p class="card-text mt-2">
@ -62,49 +110,80 @@
</b-overlay>
<div class="card-actions">
<b-button variant="outline-primary" @click.prevent="doChangePassword">
<icon icon="vpn_key"></icon>
<b-button
variant="outline-primary"
@click.prevent="doChangePassword"
>
<icon icon="vpn_key" />
{{ $gettext('Change Password') }}
</b-button>
<b-button v-if="security.twoFactorEnabled" variant="outline-danger"
@click.prevent="disableTwoFactor">
<icon icon="lock_open"></icon>
<b-button
v-if="security.twoFactorEnabled"
variant="outline-danger"
@click.prevent="disableTwoFactor"
>
<icon icon="lock_open" />
{{ $gettext('Disable Two-Factor') }}
</b-button>
<b-button v-else variant="outline-success" @click.prevent="enableTwoFactor">
<icon icon="lock"></icon>
<b-button
v-else
variant="outline-success"
@click.prevent="enableTwoFactor"
>
<icon icon="lock" />
{{ $gettext('Enable Two-Factor') }}
</b-button>
</div>
</section>
</b-col>
<b-col sm="12" md="6" lg="7">
<b-col
sm="12"
md="6"
lg="7"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('API Keys') }}</h2>
<h2 class="card-title">
{{ $gettext('API Keys') }}
</h2>
</b-card-header>
<info-card>
{{
$gettext('Use API keys to authenticate with the AzuraCast API using the same permissions as your user account.')
}}
<a href="/api" target="_blank">
<a
href="/api"
target="_blank"
>
{{ $gettext('API Documentation') }}
</a>
</info-card>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="createApiKey">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="createApiKey"
>
<icon icon="add" />
{{ $gettext('Add API Key') }}
</b-button>
</b-card-body>
<data-table ref="$dataTable" id="account_api_keys" :show-toolbar="false" :fields="apiKeyFields"
:api-url="apiKeysApiUrl">
<data-table
id="account_api_keys"
ref="$dataTable"
:show-toolbar="false"
:fields="apiKeyFields"
:api-url="apiKeysApiUrl"
>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="danger" @click.prevent="deleteApiKey(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="deleteApiKey(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -114,16 +193,30 @@
</b-col>
</b-row>
<account-edit-modal ref="$editModal" :user-url="userUrl" :supported-locales="supportedLocales"
@reload="reload"></account-edit-modal>
<account-edit-modal
ref="$editModal"
:user-url="userUrl"
:supported-locales="supportedLocales"
@reload="reload"
/>
<account-change-password-modal ref="$changePasswordModal" :change-password-url="changePasswordUrl"
@relist="relist"></account-change-password-modal>
<account-change-password-modal
ref="$changePasswordModal"
:change-password-url="changePasswordUrl"
@relist="relist"
/>
<account-two-factor-modal ref="$twoFactorModal" :two-factor-url="twoFactorUrl"
@relist="relist"></account-two-factor-modal>
<account-two-factor-modal
ref="$twoFactorModal"
:two-factor-url="twoFactorUrl"
@relist="relist"
/>
<account-api-key-modal ref="$apiKeyModal" :create-url="apiKeysApiUrl" @relist="relist"></account-api-key-modal>
<account-api-key-modal
ref="$apiKeyModal"
:create-url="apiKeysApiUrl"
@relist="relist"
/>
</div>
</template>

View File

@ -1,34 +1,64 @@
<template>
<b-modal size="md" centered id="api_keys_modal" ref="$modal" :title="$gettext('Add API Key')"
@hidden="clearContents"
no-enforce-focus>
<b-modal
id="api_keys_modal"
ref="$modal"
size="md"
centered
:title="$gettext('Add API Key')"
no-enforce-focus
@hidden="clearContents"
>
<template #default="slotProps">
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-form v-if="newKey === null" class="form vue-form" @submit.prevent="doSubmit">
<b-form
v-if="newKey === null"
class="form vue-form"
@submit.prevent="doSubmit"
>
<b-form-fieldset>
<b-wrapped-form-group id="form_comments" :field="v$.comment" autofocus>
<b-wrapped-form-group
id="form_comments"
:field="v$.comment"
autofocus
>
<template #label>
{{ $gettext('API Key Description/Comments') }}
</template>
</b-wrapped-form-group>
</b-form-fieldset>
<invisible-submit-button/>
<invisible-submit-button />
</b-form>
<div v-else>
<account-api-key-new-key :new-key="newKey"></account-api-key-new-key>
<account-api-key-new-key :new-key="newKey" />
</div>
</template>
<template #modal-footer="slotProps">
<slot name="modal-footer" v-bind="slotProps">
<b-button variant="default" type="button" @click="close">
<slot
name="modal-footer"
v-bind="slotProps"
>
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button v-if="newKey === null" :variant="(v$.$invalid) ? 'danger' : 'primary'" type="submit"
@click="doSubmit">
<b-button
v-if="newKey === null"
:variant="(v$.$invalid) ? 'danger' : 'primary'"
type="submit"
@click="doSubmit"
>
{{ $gettext('Create New Key') }}
</b-button>
</slot>

View File

@ -1,5 +1,7 @@
<template>
<h3 class="card-subtitle">{{ $gettext('New Key Generated') }}</h3>
<h3 class="card-subtitle">
{{ $gettext('New Key Generated') }}
</h3>
<p class="card-text">
<b>{{ $gettext('Important: copy the key below before continuing!') }}</b>
@ -13,7 +15,7 @@
<div class="px-2">
<code id="api_key">{{ newKey }}</code>
<div class="buttons pt-2">
<copy-to-clipboard-button :text="newKey"></copy-to-clipboard-button>
<copy-to-clipboard-button :text="newKey" />
</div>
</div>

View File

@ -1,21 +1,40 @@
<template>
<modal-form ref="$modal" size="md" centered :title="$gettext('Change Password')" :disable-save-button="v$.$invalid"
@submit="onSubmit" @hidden="clearContents">
<modal-form
ref="$modal"
size="md"
centered
:title="$gettext('Change Password')"
:disable-save-button="v$.$invalid"
@submit="onSubmit"
@hidden="clearContents"
>
<b-form-fieldset>
<b-wrapped-form-group id="form_current_password" :field="v$.current_password"
input-type="password" autofocus>
<b-wrapped-form-group
id="form_current_password"
:field="v$.current_password"
input-type="password"
autofocus
>
<template #label>
{{ $gettext('Current Password') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="form_new_password" :field="v$.new_password" input-type="password">
<b-wrapped-form-group
id="form_new_password"
:field="v$.new_password"
input-type="password"
>
<template #label>
{{ $gettext('New Password') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="form_current_password" :field="v$.new_password2" input-type="password">
<b-wrapped-form-group
id="form_current_password"
:field="v$.new_password2"
input-type="password"
>
<template #label>
{{ $gettext('Confirm New Password') }}
</template>

View File

@ -1,13 +1,21 @@
<template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="form_name" :field="form.name">
<b-wrapped-form-group
id="form_name"
class="col-md-6"
:field="form.name"
>
<template #label>
{{ $gettext('Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_email" :field="form.email">
<b-wrapped-form-group
id="form_email"
class="col-md-6"
:field="form.email"
>
<template #label>
{{ $gettext('E-mail Address') }}
</template>
@ -22,40 +30,55 @@
<div class="form-row">
<b-col md="6">
<b-wrapped-form-group id="edit_form_locale"
:field="form.locale">
<b-wrapped-form-group
id="edit_form_locale"
:field="form.locale"
>
<template #label>
{{ $gettext('Language') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="localeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="localeOptions"
/>
</template>
</b-wrapped-form-group>
</b-col>
<b-col md="6">
<b-wrapped-form-group id="edit_form_theme"
:field="form.theme">
<b-wrapped-form-group
id="edit_form_theme"
:field="form.theme"
>
<template #label>
{{ $gettext('Site Theme') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="themeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="themeOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="edit_form_show_24_hour_time"
:field="form.show_24_hour_time">
<b-wrapped-form-group
id="edit_form_show_24_hour_time"
:field="form.show_24_hour_time"
>
<template #label>
{{ $gettext('Time Display') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="show24hourOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="show24hourOptions"
/>
</template>
</b-wrapped-form-group>
</b-col>

View File

@ -1,10 +1,17 @@
<template>
<modal-form ref="$modal" :loading="loading" :title="$gettext('Edit Profile')" :error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents">
<account-edit-form :form="v$" :supported-locales="supportedLocales"></account-edit-form>
<modal-form
ref="$modal"
:loading="loading"
:title="$gettext('Edit Profile')"
:error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<account-edit-form
:form="v$"
:supported-locales="supportedLocales"
/>
</modal-form>
</template>

View File

@ -1,11 +1,19 @@
<template>
<modal-form ref="$modal" :loading="loading" :title="$gettext('Enable Two-Factor Authentication')"
:error="error" :disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents" no-enforce-focus>
<modal-form
ref="$modal"
:loading="loading"
:title="$gettext('Enable Two-Factor Authentication')"
:error="error"
:disable-save-button="v$.$invalid"
no-enforce-focus
@submit="doSubmit"
@hidden="clearContents"
>
<b-row>
<b-col md="7">
<h5 class="mt-2">{{ $gettext('Step 1: Scan QR Code') }}</h5>
<h5 class="mt-2">
{{ $gettext('Step 1: Scan QR Code') }}
</h5>
<p class="card-text">
{{
@ -24,7 +32,11 @@
</p>
<b-form-fieldset>
<b-wrapped-form-group id="form_otp" :field="v$.otp" autofocus>
<b-wrapped-form-group
id="form_otp"
:field="v$.otp"
autofocus
>
<template #label>
{{ $gettext('Code from Authenticator App') }}
</template>
@ -37,18 +49,27 @@
</b-form-fieldset>
</b-col>
<b-col md="5">
<b-img :src="totp.qr_code"></b-img>
<b-img :src="totp.qr_code" />
<div v-if="totp.totp_uri" class="mt-2">
<code id="totp_uri" class="d-inline-block text-truncate" style="width: 100%;">
<div
v-if="totp.totp_uri"
class="mt-2"
>
<code
id="totp_uri"
class="d-inline-block text-truncate"
style="width: 100%;"
>
{{ totp.totp_uri }}
</code>
<copy-to-clipboard-button :text="totp.totp_uri"></copy-to-clipboard-button>
<copy-to-clipboard-button :text="totp.totp_uri" />
</div>
</b-col>
</b-row>
<template #save-button-name>{{ $gettext('Submit Code') }}</template>
<template #save-button-name>
{{ $gettext('Submit Code') }}
</template>
</modal-form>
</template>

View File

@ -1,16 +1,30 @@
<template>
<section class="card" role="region">
<section
class="card"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('API Keys') }}</h2>
<h2 class="card-title">
{{ $gettext('API Keys') }}
</h2>
</b-card-header>
<data-table ref="$dataTable" id="api_keys" :fields="fields" :api-url="apiUrl">
<data-table
id="api_keys"
ref="$dataTable"
:fields="fields"
:api-url="apiUrl"
>
<template #cell(owner)="row">
{{ row.item.user.email }}
</template>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>

View File

@ -6,26 +6,53 @@
{{ $gettext('Audit Log') }}
</h2>
<div class="flex-shrink">
<date-range-dropdown v-model="dateRange" @update="relist"></date-range-dropdown>
<date-range-dropdown
v-model="dateRange"
@update="relist"
/>
</div>
</div>
</div>
<data-table ref="$dataTable" responsive paginated
:fields="fields" :apiUrl="apiUrl">
<data-table
ref="$dataTable"
responsive
paginated
:fields="fields"
:api-url="apiUrl"
>
<template #cell(date_time)="row">
{{ formatTimestamp(row.item.timestamp) }}
</template>
<template #cell(operation)="row">
<span class="text-success" v-if="row.item.operation_text === 'insert'"
:title="$gettext('Insert')">
<icon class="lg inline" icon="add_circle"></icon>
<span
v-if="row.item.operation_text === 'insert'"
class="text-success"
:title="$gettext('Insert')"
>
<icon
class="lg inline"
icon="add_circle"
/>
</span>
<span class="text-danger" v-else-if="row.item.operation_text === 'delete'"
:title="$gettext('Delete')">
<icon class="lg inline" icon="remove_circle"></icon>
<span
v-else-if="row.item.operation_text === 'delete'"
class="text-danger"
:title="$gettext('Delete')"
>
<icon
class="lg inline"
icon="remove_circle"
/>
</span>
<span class="text-primary" v-else :title="$gettext('Update')">
<icon class="lg inline" icon="swap_horizontal_circle"></icon>
<span
v-else
class="text-primary"
:title="$gettext('Update')"
>
<icon
class="lg inline"
icon="swap_horizontal_circle"
/>
</span>
</template>
<template #cell(identifier)="row">
@ -37,11 +64,17 @@
<small>{{ row.item.target_class }}</small><br>
{{ row.item.target }}
</template>
<template v-else>{{ $gettext('N/A') }}</template>
<template v-else>
{{ $gettext('N/A') }}
</template>
</template>
<template #cell(actions)="row">
<template v-if="row.item.changes.length > 0">
<b-button size="sm" variant="primary" @click="row.toggleDetails">
<b-button
size="sm"
variant="primary"
@click="row.toggleDetails"
>
{{ $gettext('Changes') }}
</b-button>
</template>
@ -54,22 +87,25 @@
<col width="35%">
</colgroup>
<thead>
<tr>
<th>{{ $gettext('Field Name') }}</th>
<th>{{ $gettext('Previous') }}</th>
<th>{{ $gettext('Updated') }}</th>
</tr>
<tr>
<th>{{ $gettext('Field Name') }}</th>
<th>{{ $gettext('Previous') }}</th>
<th>{{ $gettext('Updated') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="change in row.item.changes" :key="change.field">
<td>{{ change.field }}</td>
<td>
<pre class="changes">{{ change.from }}</pre>
</td>
<td>
<pre class="changes">{{ change.to }}</pre>
</td>
</tr>
<tr
v-for="change in row.item.changes"
:key="change.field"
>
<td>{{ change.field }}</td>
<td>
<pre class="changes">{{ change.from }}</pre>
</td>
<td>
<pre class="changes">{{ change.to }}</pre>
</td>
</tr>
</tbody>
</table>
</template>
@ -77,13 +113,6 @@
</div>
</template>
<style lang="scss">
pre.changes {
max-width: 250px;
margin-bottom: 0;
}
</style>
<script setup>
import {DateTime} from "luxon";
import {computed, ref} from "vue";
@ -137,3 +166,10 @@ const formatTimestamp = (unix_timestamp) => {
);
}
</script>
<style lang="scss">
pre.changes {
max-width: 250px;
margin-bottom: 0;
}
</style>

View File

@ -1,42 +1,68 @@
<template>
<div>
<h2 class="outside-card-header mb-1">{{ $gettext('Backups') }}</h2>
<h2 class="outside-card-header mb-1">
{{ $gettext('Backups') }}
</h2>
<div class="card-deck">
<section class="card mb-3" role="region">
<section
class="card mb-3"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">
{{ $gettext('Automatic Backups') }}
<enabled-badge :enabled="settings.backupEnabled"></enabled-badge>
<enabled-badge :enabled="settings.backupEnabled" />
</h2>
</b-card-header>
<b-overlay variant="card" :show="settingsLoading">
<div v-if="settings.backupEnabled" class="card-body">
<p v-if="settings.backupLastRun > 0" class="card-text">
<b-overlay
variant="card"
:show="settingsLoading"
>
<div
v-if="settings.backupEnabled"
class="card-body"
>
<p
v-if="settings.backupLastRun > 0"
class="card-text"
>
{{ $gettext('Last run:') }}
{{ toRelativeTime(settings.backupLastRun) }}
</p>
<p v-else class="card-text">
<p
v-else
class="card-text"
>
{{ $gettext('Never run') }}
</p>
</div>
</b-overlay>
<div class="card-actions">
<b-button variant="outline-primary" @click.prevent="doConfigure">
<icon icon="settings"></icon>
<b-button
variant="outline-primary"
@click.prevent="doConfigure"
>
<icon icon="settings" />
{{ $gettext('Configure') }}
</b-button>
<b-button v-if="settings.backupEnabled && settings.backupLastOutput !== ''"
variant="outline-secondary" @click.prevent="showLastOutput">
<icon icon="assignment"></icon>
<b-button
v-if="settings.backupEnabled && settings.backupLastOutput !== ''"
variant="outline-secondary"
@click.prevent="showLastOutput"
>
<icon icon="assignment" />
{{ $gettext('Most Recent Backup Log') }}
</b-button>
</div>
</section>
<section class="card mb-3" role="region">
<section
class="card mb-3"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">
{{ $gettext('Restoring Backups') }}
@ -60,7 +86,10 @@
</section>
</div>
<section class="card mb-3" role="region">
<section
class="card mb-3"
role="region"
>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">
{{ $gettext('Backups') }}
@ -68,13 +97,21 @@
</b-card-header>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doRunBackup">
<icon icon="send"></icon>
<b-button
variant="outline-primary"
@click.prevent="doRunBackup"
>
<icon icon="send" />
{{ $gettext('Run Manual Backup') }}
</b-button>
</b-card-body>
<data-table ref="$dataTable" id="api_keys" :fields="fields" :api-url="listUrl">
<data-table
id="api_keys"
ref="$dataTable"
:fields="fields"
:api-url="listUrl"
>
<template #cell(timestamp)="row">
{{ toLocaleTime(row.item.timestamp) }}
</template>
@ -83,10 +120,19 @@
</template>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="primary" :href="row.item.links.download" target="_blank">
<b-button
size="sm"
variant="primary"
:href="row.item.links.download"
target="_blank"
>
{{ $gettext('Download') }}
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.delete)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.delete)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -94,16 +140,24 @@
</data-table>
</section>
<admin-backups-configure-modal ref="$configureModal" :settings-url="settingsUrl"
:storage-locations="storageLocations"
@relist="relist"></admin-backups-configure-modal>
<admin-backups-configure-modal
ref="$configureModal"
:settings-url="settingsUrl"
:storage-locations="storageLocations"
@relist="relist"
/>
<admin-backups-run-backup-modal ref="$runBackupModal" :run-backup-url="runBackupUrl"
:storage-locations="storageLocations"
@relist="relist"></admin-backups-run-backup-modal>
<admin-backups-run-backup-modal
ref="$runBackupModal"
:run-backup-url="runBackupUrl"
:storage-locations="storageLocations"
@relist="relist"
/>
<admin-backups-last-output-modal ref="$lastOutputModal"
:last-output="settings.backupLastOutput"></admin-backups-last-output-modal>
<admin-backups-last-output-modal
ref="$lastOutputModal"
:last-output="settings.backupLastOutput"
/>
</div>
</template>

View File

@ -1,10 +1,20 @@
<template>
<modal-form ref="$modal" size="lg" :title="$gettext('Configure Backups')" :loading="loading"
:disable-save-button="v$.$invalid" @submit="submit" @hidden="resetForm">
<modal-form
ref="$modal"
size="lg"
:title="$gettext('Configure Backups')"
:loading="loading"
:disable-save-button="v$.$invalid"
@submit="submit"
@hidden="resetForm"
>
<b-form-fieldset>
<div class="form-row mb-3">
<b-wrapped-form-checkbox class="col-md-12" id="form_edit_backup_enabled"
:field="v$.backup_enabled">
<b-wrapped-form-checkbox
id="form_edit_backup_enabled"
class="col-md-12"
:field="v$.backup_enabled"
>
<template #label>
{{ $gettext('Run Automatic Nightly Backups') }}
</template>
@ -16,8 +26,15 @@
</b-wrapped-form-checkbox>
</div>
<div class="form-row" v-if="v$.backup_enabled.$model">
<b-wrapped-form-group class="col-md-6" id="form_backup_time_code" :field="v$.backup_time_code">
<div
v-if="v$.backup_enabled.$model"
class="form-row"
>
<b-wrapped-form-group
id="form_backup_time_code"
class="col-md-6"
:field="v$.backup_time_code"
>
<template #label>
{{ $gettext('Scheduled Backup Time') }}
</template>
@ -25,12 +42,19 @@
{{ $gettext('If the end time is before the start time, the playlist will play overnight.') }}
</template>
<template #default="props">
<time-code :id="props.id" v-model="props.field.$model" :state="props.state"></time-code>
<time-code
:id="props.id"
v-model="props.field.$model"
:state="props.state"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-6" id="form_edit_exclude_media"
:field="v$.backup_exclude_media">
<b-wrapped-form-checkbox
id="form_edit_exclude_media"
class="col-md-6"
:field="v$.backup_exclude_media"
>
<template #label>
{{ $gettext('Exclude Media from Backup') }}
</template>
@ -41,8 +65,13 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-6" id="form_backup_keep_copies" :field="v$.backup_keep_copies"
input-type="number" :input-attrs="{min: '0', max: '365'}">
<b-wrapped-form-group
id="form_backup_keep_copies"
class="col-md-6"
:field="v$.backup_keep_copies"
input-type="number"
:input-attrs="{min: '0', max: '365'}"
>
<template #label>
{{ $gettext('Number of Backup Copies to Keep') }}
</template>
@ -53,24 +82,38 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backup_storage_location"
:field="v$.backup_storage_location">
<b-wrapped-form-group
id="edit_form_backup_storage_location"
class="col-md-6"
:field="v$.backup_storage_location"
>
<template #label>
{{ $gettext('Storage Location') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="storageLocationOptions"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="storageLocationOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backup_format" :field="v$.backup_format">
<b-wrapped-form-group
id="edit_form_backup_format"
class="col-md-6"
:field="v$.backup_format"
>
<template #label>
{{ $gettext('Backup Format') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" v-model="props.field.$model"
:options="formatOptions"></b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="formatOptions"
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,7 +1,15 @@
<template>
<b-modal size="md" id="log_view_modal" ref="$modal" :title="$gettext('Log Viewer')">
<pre id="modal-log-view-contents" class="form-control log-viewer"
style="height: 300px; overflow-y: auto;">{{ lastOutput }}</pre>
<b-modal
id="log_view_modal"
ref="$modal"
size="md"
:title="$gettext('Log Viewer')"
>
<pre
id="modal-log-view-contents"
class="form-control log-viewer"
style="height: 300px; overflow-y: auto;"
>{{ lastOutput }}</pre>
</b-modal>
</template>

View File

@ -1,25 +1,49 @@
<template>
<b-modal size="md" centered id="run_backup_modal" ref="$modal" :title="$gettext('Run Manual Backup')"
@hidden="clearContents">
<b-modal
id="run_backup_modal"
ref="$modal"
size="md"
centered
:title="$gettext('Run Manual Backup')"
@hidden="clearContents"
>
<template #default="slotProps">
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-form v-if="logUrl === null" class="form vue-form" @submit.prevent="submit">
<b-form
v-if="logUrl === null"
class="form vue-form"
@submit.prevent="submit"
>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_storage_location"
:field="v$.storage_location">
<b-wrapped-form-group
id="edit_form_storage_location"
class="col-md-12"
:field="v$.storage_location"
>
<template #label>
{{ $gettext('Storage Location') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="storageLocationOptions"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="storageLocationOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_path"
:field="v$.path">
<b-wrapped-form-group
id="edit_form_path"
class="col-md-12"
:field="v$.path"
>
<template #label>
{{ $gettext('File Name') }}
</template>
@ -35,7 +59,8 @@
<ul class="m-0">
<li>.zip</li>
<li>.tar.gz</li>
<li>.tzst (
<li>
.tzst (
{{ $gettext('ZStandard compression') }}
)
</li>
@ -43,8 +68,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_exclude_media"
:field="v$.exclude_media">
<b-wrapped-form-checkbox
id="edit_form_exclude_media"
class="col-md-12"
:field="v$.exclude_media"
>
<template #label>
{{ $gettext('Exclude Media from Backup') }}
</template>
@ -57,21 +85,32 @@
</div>
</b-form-fieldset>
<invisible-submit-button/>
<invisible-submit-button />
</b-form>
<div v-else>
<streaming-log-view :log-url="logUrl"></streaming-log-view>
<streaming-log-view :log-url="logUrl" />
</div>
</template>
<template #modal-footer="slotProps">
<slot name="modal-footer" v-bind="slotProps">
<b-button variant="default" type="button" @click="close">
<slot
name="modal-footer"
v-bind="slotProps"
>
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button v-if="logUrl === null" :variant="(v$.$invalid) ? 'danger' : 'primary'" type="submit"
@click="submit">
<b-button
v-if="logUrl === null"
:variant="(v$.$invalid) ? 'danger' : 'primary'"
type="submit"
@click="submit"
>
{{ $gettext('Run Manual Backup') }}
</b-button>
</slot>

View File

@ -4,7 +4,10 @@
{{ $gettext('Custom Branding') }}
</h2>
<section class="card mb-3" role="region">
<section
class="card mb-3"
role="region"
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Upload Custom Assets') }}
@ -12,17 +15,28 @@
</div>
<div class="card-body">
<ul class="list-unstyled">
<custom-asset-form id="asset_background" class="mb-3" :api-url="backgroundApiUrl"
:caption="$gettext('Public Page Background')"></custom-asset-form>
<custom-asset-form id="asset_album_art" class="mb-3" :api-url="albumArtApiUrl"
:caption="$gettext('Default Album Art')"></custom-asset-form>
<custom-asset-form id="asset_browser_icon" :api-url="browserIconApiUrl"
:caption="$gettext('Browser Icon')"></custom-asset-form>
<custom-asset-form
id="asset_background"
class="mb-3"
:api-url="backgroundApiUrl"
:caption="$gettext('Public Page Background')"
/>
<custom-asset-form
id="asset_album_art"
class="mb-3"
:api-url="albumArtApiUrl"
:caption="$gettext('Default Album Art')"
/>
<custom-asset-form
id="asset_browser_icon"
:api-url="browserIconApiUrl"
:caption="$gettext('Browser Icon')"
/>
</ul>
</div>
</section>
<branding-form :api-url="settingsApiUrl"></branding-form>
<branding-form :api-url="settingsApiUrl" />
</div>
</template>

View File

@ -1,20 +1,37 @@
<template>
<form class="form vue-form" @submit.prevent="submit">
<section class="card mb-3" role="region">
<form
class="form vue-form"
@submit.prevent="submit"
>
<section
class="card mb-3"
role="region"
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Branding Settings') }}
</h2>
</div>
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<div class="card-body">
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_public_theme"
:field="v$.public_theme">
<b-wrapped-form-group
id="edit_form_public_theme"
class="col-md-6"
:field="v$.public_theme"
>
<template #label>
{{ $gettext('Base Theme for Public Pages') }}
</template>
@ -24,15 +41,21 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="publicThemeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="publicThemeOptions"
/>
</template>
</b-wrapped-form-group>
<b-col md="6">
<b-wrapped-form-checkbox class="mb-2" id="form_edit_hide_album_art"
:field="v$.hide_album_art">
<b-wrapped-form-checkbox
id="form_edit_hide_album_art"
class="mb-2"
:field="v$.hide_album_art"
>
<template #label>
{{ $gettext('Hide Album Art on Public Pages') }}
</template>
@ -43,8 +66,10 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox id="form_edit_hide_product_name"
:field="v$.hide_product_name">
<b-wrapped-form-checkbox
id="form_edit_hide_product_name"
:field="v$.hide_product_name"
>
<template #label>
{{ $gettext('Hide AzuraCast Branding on Public Pages') }}
</template>
@ -56,8 +81,11 @@
</b-wrapped-form-checkbox>
</b-col>
<b-wrapped-form-group class="col-md-6" id="form_edit_homepage_redirect_url"
:field="v$.homepage_redirect_url">
<b-wrapped-form-group
id="form_edit_homepage_redirect_url"
class="col-md-6"
:field="v$.homepage_redirect_url"
>
<template #label>
{{ $gettext('Homepage Redirect URL') }}
</template>
@ -68,8 +96,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_default_album_art_url"
:field="v$.default_album_art_url">
<b-wrapped-form-group
id="form_edit_default_album_art_url"
class="col-md-6"
:field="v$.default_album_art_url"
>
<template #label>
{{ $gettext('Default Album Art URL') }}
</template>
@ -80,8 +111,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_public_custom_css"
:field="v$.public_custom_css">
<b-wrapped-form-group
id="edit_form_public_custom_css"
class="col-md-12"
:field="v$.public_custom_css"
>
<template #label>
{{ $gettext('Custom CSS for Public Pages') }}
</template>
@ -91,13 +125,19 @@
}}
</template>
<template #default="props">
<codemirror-textarea :id="props.id" mode="css"
v-model="props.field.$model"></codemirror-textarea>
<codemirror-textarea
:id="props.id"
v-model="props.field.$model"
mode="css"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_public_custom_js"
:field="v$.public_custom_js">
<b-wrapped-form-group
id="edit_form_public_custom_js"
class="col-md-12"
:field="v$.public_custom_js"
>
<template #label>
{{ $gettext('Custom JS for Public Pages') }}
</template>
@ -107,13 +147,19 @@
}}
</template>
<template #default="props">
<codemirror-textarea :id="props.id" mode="javascript"
v-model="props.field.$model"></codemirror-textarea>
<codemirror-textarea
:id="props.id"
v-model="props.field.$model"
mode="javascript"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_internal_custom_css"
:field="v$.internal_custom_css">
<b-wrapped-form-group
id="edit_form_internal_custom_css"
class="col-md-12"
:field="v$.internal_custom_css"
>
<template #label>
{{ $gettext('Custom CSS for Internal Pages') }}
</template>
@ -123,13 +169,21 @@
}}
</template>
<template #default="props">
<codemirror-textarea :id="props.id" mode="css"
v-model="props.field.$model"></codemirror-textarea>
<codemirror-textarea
:id="props.id"
v-model="props.field.$model"
mode="css"
/>
</template>
</b-wrapped-form-group>
</div>
<b-button size="lg" type="submit" class="mt-3" variant="primary">
<b-button
size="lg"
type="submit"
class="mt-3"
variant="primary"
>
{{ $gettext('Save Changes') }}
</b-button>
</b-form-group>

View File

@ -1,16 +1,37 @@
<template>
<b-media tag="li">
<template #aside>
<a :href="url" data-fancybox target="_blank">
<b-img :src="url" width="125" :alt="caption"></b-img>
<a
:href="url"
data-fancybox
target="_blank"
>
<b-img
:src="url"
width="125"
:alt="caption"
/>
</a>
</template>
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<b-form-group :label-for="id">
<template #label>{{ caption }}</template>
<b-form-file :id="id" v-model="file" accept="image/*"></b-form-file>
<template #label>
{{ caption }}
</template>
<b-form-file
:id="id"
v-model="file"
accept="image/*"
/>
</b-form-group>
<b-button v-if="isUploaded" variant="outline-danger" @click.prevent="clear()">
<b-button
v-if="isUploaded"
variant="outline-danger"
@click.prevent="clear()"
>
{{ $gettext('Clear Image') }}
</b-button>
</b-overlay>
@ -34,9 +55,6 @@ export default {
file: null,
};
},
mounted() {
this.relist();
},
watch: {
file(newFile) {
if (null === newFile) {
@ -53,6 +71,9 @@ export default {
});
}
},
mounted() {
this.relist();
},
methods: {
relist() {
this.file = null;

View File

@ -1,7 +1,9 @@
<template>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Custom Fields') }}</h2>
<h2 class="card-title">
{{ $gettext('Custom Fields') }}
</h2>
</b-card-header>
<info-card>
@ -13,13 +15,22 @@
</info-card>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doCreate">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="doCreate"
>
<icon icon="add" />
{{ $gettext('Add Custom Field') }}
</b-button>
</b-card-body>
<data-table ref="$dataTable" id="custom_fields" :fields="fields" :show-toolbar="false" :api-url="listUrl">
<data-table
id="custom_fields"
ref="$dataTable"
:fields="fields"
:show-toolbar="false"
:api-url="listUrl"
>
<template #cell(name)="row">
{{ row.item.name }} <code>{{ row.item.short_name }}</code>
</template>
@ -28,10 +39,18 @@
</template>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
<b-button
size="sm"
variant="primary"
@click.prevent="doEdit(row.item.links.self)"
>
{{ $gettext('Edit') }}
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -39,8 +58,12 @@
</data-table>
</b-card>
<edit-modal ref="$editModal" :create-url="listUrl" :auto-assign-types="autoAssignTypes"
@relist="relist"></edit-modal>
<edit-modal
ref="$editModal"
:create-url="listUrl"
:auto-assign-types="autoAssignTypes"
@relist="relist"
/>
</template>
<script setup>

View File

@ -1,10 +1,17 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-custom-fields-form :form="v$" :auto-assign-types="autoAssignTypes">
</admin-custom-fields-form>
<modal-form
ref="modal"
:loading="loading"
:title="langTitle"
:error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<admin-custom-fields-form
:form="v$"
:auto-assign-types="autoAssignTypes"
/>
</modal-form>
</template>
@ -17,8 +24,11 @@ import {defineComponent} from "vue";
export default defineComponent({
name: 'AdminCustomFieldsEditModal',
mixins: [BaseEditModal],
components: {AdminCustomFieldsForm},
mixins: [BaseEditModal],
props: {
autoAssignTypes: Object
},
setup() {
const {form, resetForm, v$} = useVuelidateOnForm(
{
@ -39,9 +49,6 @@ export default defineComponent({
v$
};
},
props: {
autoAssignTypes: Object
},
computed: {
langTitle() {
return this.isEditMode

View File

@ -1,7 +1,11 @@
<template>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_name" :field="form.name">
<b-wrapped-form-group
id="edit_form_name"
class="col-md-6"
:field="form.name"
>
<template #label>
{{ $gettext('Field Name') }}
</template>
@ -12,7 +16,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_short_name" :field="form.short_name">
<b-wrapped-form-group
id="edit_form_short_name"
class="col-md-6"
:field="form.short_name"
>
<template #label>
{{ $gettext('Programmatic Name') }}
</template>
@ -23,7 +31,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_auto_assign" :field="form.auto_assign">
<b-wrapped-form-group
id="edit_form_auto_assign"
class="col-md-6"
:field="form.auto_assign"
>
<template #label>
{{ $gettext('Automatically Set from ID3v2 Value') }}
</template>
@ -33,8 +45,11 @@
}}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="autoAssignOptions"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="autoAssignOptions"
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -13,7 +13,10 @@
</info-card>
<div class="card-body">
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<div class="form-row">
<div class="col-md-7">
<fieldset>
@ -31,7 +34,10 @@
<li>
{{ $gettext('Create an account on the MaxMind developer site.') }}
<br>
<a href="https://www.maxmind.com/en/geolite2/signup" target="_blank">
<a
href="https://www.maxmind.com/en/geolite2/signup"
target="_blank"
>
{{ $gettext('MaxMind Developer Site') }}
</a>
</li>
@ -53,17 +59,26 @@
{{ $gettext('Current Installed Version') }}
</legend>
<p v-if="version" class="text-success card-text">
<p
v-if="version"
class="text-success card-text"
>
{{ langInstalledVersion }}
</p>
<p v-else class="text-danger card-text">
<p
v-else
class="text-danger card-text"
>
{{ $gettext('GeoLite is not currently installed on this installation.') }}
</p>
</fieldset>
<form @submit.prevent="doUpdate">
<fieldset>
<b-wrapped-form-group id="edit_form_key" :field="v$.key">
<b-wrapped-form-group
id="edit_form_key"
:field="v$.key"
>
<template #label>
{{ $gettext('MaxMind License Key') }}
</template>
@ -71,10 +86,17 @@
</fieldset>
<div class="buttons">
<b-button variant="primary" type="submit">
<b-button
variant="primary"
type="submit"
>
{{ $gettext('Save Changes') }}
</b-button>
<b-button variant="danger" type="button" @click.prevent="doDelete">
<b-button
variant="danger"
type="button"
@click.prevent="doDelete"
>
{{ $gettext('Remove Key') }}
</b-button>
</div>

View File

@ -5,19 +5,38 @@
</h2>
<b-row>
<b-col v-for="(panel, key) in adminPanels" :key="key" sm="12" lg="4" class="mb-4">
<b-col
v-for="(panel, key) in adminPanels"
:key="key"
sm="12"
lg="4"
class="mb-4"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark" class="d-flex align-items-center">
<b-card-header
header-bg-variant="primary-dark"
class="d-flex align-items-center"
>
<div class="flex-fill">
<h2 class="card-title">{{ panel.label }}</h2>
<h2 class="card-title">
{{ panel.label }}
</h2>
</div>
<div class="flex-shrink-0 pt-1">
<icon class="lg" :icon="panel.icon"></icon>
<icon
class="lg"
:icon="panel.icon"
/>
</div>
</b-card-header>
<b-list-group>
<b-list-group-item v-for="(item, key) in panel.items" :key="key" :href="item.url">{{
<b-list-group-item
v-for="(item, key) in panel.items"
:key="key"
:href="item.url"
>
{{
item.label
}}
</b-list-group-item>
@ -31,9 +50,17 @@
</h2>
<b-row>
<b-col sm="12" lg="6" xl="6" class="mb-4">
<b-col
sm="12"
lg="6"
xl="6"
class="mb-4"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark" class="d-flex align-items-center">
<b-card-header
header-bg-variant="primary-dark"
class="d-flex align-items-center"
>
<div class="flex-fill">
<h2 class="card-title">
{{ $gettext('Memory') }}
@ -41,9 +68,13 @@
</div>
<div class="flex-shrink-0">
<b-button variant="outline-light" size="sm" class="py-2"
@click.prevent="showMemoryStatsHelpModal">
<icon icon="help_outline"></icon>
<b-button
variant="outline-light"
size="sm"
class="py-2"
@click.prevent="showMemoryStatsHelpModal"
>
<icon icon="help_outline" />
</b-button>
</div>
</b-card-header>
@ -55,21 +86,39 @@
{{ stats.memory.readable.total }}
</h6>
<b-progress :max="stats.memory.bytes.total" :label="stats.memory.readable.used"
class="h-20 mb-3 mt-2">
<b-progress-bar variant="primary" :value="stats.memory.bytes.used"></b-progress-bar>
<b-progress-bar variant="warning"
:value="stats.memory.bytes.cached"></b-progress-bar>
<b-progress
:max="stats.memory.bytes.total"
:label="stats.memory.readable.used"
class="h-20 mb-3 mt-2"
>
<b-progress-bar
variant="primary"
:value="stats.memory.bytes.used"
/>
<b-progress-bar
variant="warning"
:value="stats.memory.bytes.cached"
/>
</b-progress>
<b-row>
<b-col>
<b-badge pill variant="primary">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="primary"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Used') }}
: {{ stats.memory.readable.used }}
</b-col>
<b-col>
<b-badge pill variant="warning">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="warning"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Cached') }}
: {{ stats.memory.readable.cached }}
</b-col>
@ -78,7 +127,12 @@
</b-card>
</b-col>
<b-col sm="12" lg="6" xl="6" class="mb-4">
<b-col
sm="12"
lg="6"
xl="6"
class="mb-4"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">
@ -93,14 +147,25 @@
{{ stats.disk.readable.total }}
</h6>
<b-progress :max="stats.disk.bytes.total" :label="stats.disk.readable.used"
class="h-20 mb-3 mt-2">
<b-progress-bar variant="primary" :value="stats.disk.bytes.used"></b-progress-bar>
<b-progress
:max="stats.disk.bytes.total"
:label="stats.disk.readable.used"
class="h-20 mb-3 mt-2"
>
<b-progress-bar
variant="primary"
:value="stats.disk.bytes.used"
/>
</b-progress>
<b-row>
<b-col>
<b-badge pill variant="primary">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="primary"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Used') }}
:
{{ stats.disk.readable.used }}
@ -112,9 +177,17 @@
</b-row>
<b-row>
<b-col sm="12" lg="8" xl="6" class="mb-4">
<b-col
sm="12"
lg="8"
xl="6"
class="mb-4"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark" class="d-flex align-items-center">
<b-card-header
header-bg-variant="primary-dark"
class="d-flex align-items-center"
>
<div class="flex-fill">
<h2 class="card-title">
{{ $gettext('CPU Load') }}
@ -122,36 +195,69 @@
</div>
<div class="flex-shrink-0">
<b-button variant="outline-light" size="sm" class="py-2"
@click.prevent="showCpuStatsHelpModal">
<icon icon="help_outline"></icon>
<b-button
variant="outline-light"
size="sm"
class="py-2"
@click.prevent="showCpuStatsHelpModal"
>
<icon icon="help_outline" />
</b-button>
</div>
</b-card-header>
<b-card-body>
<h5 class="mb-1 text-center">{{ formatCpuName(stats.cpu.total.name) }}</h5>
<h5 class="mb-1 text-center">
{{ formatCpuName(stats.cpu.total.name) }}
</h5>
<b-progress max="100" :label="formatPercentageString(stats.cpu.total.usage)"
class="h-20 mb-3 mt-2">
<b-progress-bar variant="danger" :value="stats.cpu.total.steal"></b-progress-bar>
<b-progress-bar variant="warning" :value="stats.cpu.total.io_wait"></b-progress-bar>
<b-progress-bar variant="primary" :value="stats.cpu.total.usage"></b-progress-bar>
<b-progress
max="100"
:label="formatPercentageString(stats.cpu.total.usage)"
class="h-20 mb-3 mt-2"
>
<b-progress-bar
variant="danger"
:value="stats.cpu.total.steal"
/>
<b-progress-bar
variant="warning"
:value="stats.cpu.total.io_wait"
/>
<b-progress-bar
variant="primary"
:value="stats.cpu.total.usage"
/>
</b-progress>
<b-row>
<b-col>
<b-badge pill variant="danger">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="danger"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Steal') }}
: {{ stats.cpu.total.steal }}%
</b-col>
<b-col>
<b-badge pill variant="warning">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="warning"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Wait') }}
: {{ stats.cpu.total.io_wait }}%
</b-col>
<b-col>
<b-badge pill variant="primary">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="primary"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Use') }}
: {{ stats.cpu.total.usage }}%
</b-col>
@ -160,16 +266,38 @@
<hr>
<b-row>
<b-col v-for="core in stats.cpu.cores" :key="core.name" lg="6">
<h6 class="mb-1 text-center">{{ formatCpuName(core.name) }}</h6>
<b-col
v-for="core in stats.cpu.cores"
:key="core.name"
lg="6"
>
<h6 class="mb-1 text-center">
{{ formatCpuName(core.name) }}
</h6>
<b-progress max="100" :label="formatPercentageString(core.usage)" class="h-20">
<b-progress-bar variant="danger" :value="core.steal"></b-progress-bar>
<b-progress-bar variant="warning" :value="core.io_wait"></b-progress-bar>
<b-progress-bar variant="primary" :value="core.usage"></b-progress-bar>
<b-progress
max="100"
:label="formatPercentageString(core.usage)"
class="h-20"
>
<b-progress-bar
variant="danger"
:value="core.steal"
/>
<b-progress-bar
variant="warning"
:value="core.io_wait"
/>
<b-progress-bar
variant="primary"
:value="core.usage"
/>
</b-progress>
<b-row no-gutters class="mb-2 mt-1">
<b-row
no-gutters
class="mb-2 mt-1"
>
<b-col>
St: {{ core.steal }}%
</b-col>
@ -188,7 +316,10 @@
<h6 class="mb-1 text-center">
{{ $gettext('Load Average') }}
</h6>
<b-row class="text-center" no-gutters>
<b-row
class="text-center"
no-gutters
>
<b-col>
<h6>1-Min</h6>
{{ stats.cpu.load[0].toFixed(2) }}
@ -206,9 +337,17 @@
</b-card>
</b-col>
<b-col sm="12" lg="4" xl="6" class="mb-4">
<b-col
sm="12"
lg="4"
xl="6"
class="mb-4"
>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark" class="d-flex align-items-center">
<b-card-header
header-bg-variant="primary-dark"
class="d-flex align-items-center"
>
<div class="flex-fill">
<h2 class="card-title">
{{ $gettext('Services') }}
@ -223,36 +362,54 @@
<col style="width: 20%;">
</colgroup>
<tbody>
<tr class="align-middle" v-for="service in services" :key="service.name">
<td class="text-center pr-2">
<template v-if="service.running">
<b-badge pill variant="success" :title="langServiceRunning">
<tr
v-for="service in services"
:key="service.name"
class="align-middle"
>
<td class="text-center pr-2">
<template v-if="service.running">
<b-badge
pill
variant="success"
:title="langServiceRunning"
>
&nbsp;&nbsp;
<span class="sr-only">{{ langServiceRunning }}</span>
</b-badge>
</template>
<template v-else>
<b-badge pill variant="danger" :title="langServiceStopped">
<span class="sr-only">{{ langServiceRunning }}</span>
</b-badge>
</template>
<template v-else>
<b-badge
pill
variant="danger"
:title="langServiceStopped"
>
&nbsp;&nbsp;
<span class="sr-only">{{ langServiceStopped }}</span>
</b-badge>
</template>
</td>
<td class="pl-2">
<h6 class="mb-0">
{{ service.name }}<br>
<small>{{ service.description }}</small>
</h6>
</td>
<td>
<b-button-group size="sm" v-if="service.links.restart">
<b-button size="sm" :variant="service.running ? 'bg' : 'danger'"
@click.prevent="doRestart(service.links.restart)">
{{ $gettext('Restart') }}
</b-button>
</b-button-group>
</td>
</tr>
<span class="sr-only">{{ langServiceStopped }}</span>
</b-badge>
</template>
</td>
<td class="pl-2">
<h6 class="mb-0">
{{ service.name }}<br>
<small>{{ service.description }}</small>
</h6>
</td>
<td>
<b-button-group
v-if="service.links.restart"
size="sm"
>
<b-button
size="sm"
:variant="service.running ? 'bg' : 'danger'"
@click.prevent="doRestart(service.links.restart)"
>
{{ $gettext('Restart') }}
</b-button>
</b-button-group>
</td>
</tr>
</tbody>
</table>
</b-card>
@ -268,27 +425,38 @@
</h2>
</b-card-header>
<b-tabs content-class="mt-3" pills card>
<b-tab v-for="netInterface in stats.network" :key="netInterface.interface_name"
:title="netInterface.interface_name">
<b-tabs
content-class="mt-3"
pills
card
>
<b-tab
v-for="netInterface in stats.network"
:key="netInterface.interface_name"
:title="netInterface.interface_name"
>
<b-row class="mb-3">
<b-col class="mb-3">
<h5 class="mb-1 text-center">
{{ $gettext('Received') }}
</h5>
<b-table striped responsive
:items="getNetworkInterfaceTableItems(netInterface.received)"
:fields="getNetworkInterfaceTableFields(netInterface.received)">
</b-table>
<b-table
striped
responsive
:items="getNetworkInterfaceTableItems(netInterface.received)"
:fields="getNetworkInterfaceTableFields(netInterface.received)"
/>
</b-col>
<b-col>
<h5 class="mb-1 text-center">
{{ $gettext('Transmitted') }}
</h5>
<b-table striped responsive
:items="getNetworkInterfaceTableItems(netInterface.transmitted)"
:fields="getNetworkInterfaceTableFields(netInterface.transmitted)">
</b-table>
<b-table
striped
responsive
:items="getNetworkInterfaceTableItems(netInterface.transmitted)"
:fields="getNetworkInterfaceTableFields(netInterface.transmitted)"
/>
</b-col>
</b-row>
</b-tab>
@ -297,8 +465,8 @@
</b-col>
</b-row>
<cpu-stats-help-modal ref="cpuStatsHelpModal"></cpu-stats-help-modal>
<memory-stats-help-modal ref="memoryStatsHelpModal"></memory-stats-help-modal>
<cpu-stats-help-modal ref="cpuStatsHelpModal" />
<memory-stats-help-modal ref="memoryStatsHelpModal" />
</div>
</template>
@ -361,10 +529,6 @@ export default {
services: []
};
},
created() {
this.updateStats();
this.updateServices();
},
computed: {
langServiceRunning() {
return this.$gettext('Service Running');
@ -373,6 +537,10 @@ export default {
return this.$gettext('Service Stopped');
}
},
created() {
this.updateStats();
this.updateServices();
},
methods: {
formatCpuName(cpuName) {
return _.upperFirst(cpuName);

View File

@ -1,8 +1,19 @@
<template>
<b-modal size="lg" centered id="cpu_stats_help_modal" ref="$modal" :title="$gettext('CPU Stats Help')">
<b-modal
id="cpu_stats_help_modal"
ref="$modal"
size="lg"
centered
:title="$gettext('CPU Stats Help')"
>
<div class="mb-2">
<h6>
<b-badge pill variant="danger">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="danger"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Steal (St)') }}:
{{ $gettext('Time stolen by other virtual machines on the same physical server.') }}
</h6>
@ -26,7 +37,12 @@
</div>
<div class="mb-2">
<h6>
<b-badge pill variant="warning">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="warning"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Wait (Wa)') }}:
{{ $gettext('Time spent waiting for disk I/O to be completed.') }}
</h6>
@ -50,7 +66,12 @@
</div>
<div class="mb-1">
<h6>
<b-badge pill variant="primary">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="primary"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Use (Us)') }}:
{{ $gettext('The current CPU usage including I/O Wait and Steal.') }}
</h6>
@ -58,7 +79,11 @@
<template #modal-footer>
<slot name="modal-footer">
<b-button variant="default" type="button" @click="close">
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
</slot>

View File

@ -1,8 +1,19 @@
<template>
<b-modal size="lg" centered id="cpu_stats_help_modal" ref="$modal" :title="$gettext('Memory Stats Help')">
<b-modal
id="cpu_stats_help_modal"
ref="$modal"
size="lg"
centered
:title="$gettext('Memory Stats Help')"
>
<div class="mb-2">
<h6>
<b-badge pill variant="danger">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="danger"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Cached') }}:
{{ $gettext('The amount of memory Linux is using for disk caching.') }}
</h6>
@ -22,7 +33,12 @@
<div class="mb-2">
<h6>
<b-badge pill variant="primary">&nbsp;&nbsp;</b-badge>&nbsp;
<b-badge
pill
variant="primary"
>
&nbsp;&nbsp;
</b-badge>&nbsp;
{{ $gettext('Used') }}:
{{ $gettext('The current Memory usage excluding cached memory.') }}
</h6>
@ -30,7 +46,11 @@
<template #modal-footer>
<slot name="modal-footer">
<b-button variant="default" type="button" @click="close">
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
</slot>

View File

@ -6,24 +6,42 @@
</h2>
</div>
<log-list :url="systemLogsUrl" @view="viewLog"></log-list>
<log-list
:url="systemLogsUrl"
@view="viewLog"
/>
</div>
<div class="card" v-if="stationLogs.length > 0">
<div
v-if="stationLogs.length > 0"
class="card"
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Logs by Station') }}
</h2>
</div>
<b-tabs pills lazy nav-class="card-header-pills" nav-wrapper-class="card-header">
<b-tab v-for="row in stationLogs" :key="row.id" :title="row.name">
<log-list :url="row.url" @view="viewLog"></log-list>
<b-tabs
pills
lazy
nav-class="card-header-pills"
nav-wrapper-class="card-header"
>
<b-tab
v-for="row in stationLogs"
:key="row.id"
:title="row.name"
>
<log-list
:url="row.url"
@view="viewLog"
/>
</b-tab>
</b-tabs>
</div>
<streaming-log-modal ref="$modal"></streaming-log-modal>
<streaming-log-modal ref="$modal" />
</template>
<script setup>

View File

@ -1,7 +1,9 @@
<template>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Roles & Permissions') }}</h2>
<h2 class="card-title">
{{ $gettext('Roles & Permissions') }}
</h2>
</b-card-header>
<info-card>
@ -13,31 +15,54 @@
</info-card>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doCreate">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="doCreate"
>
<icon icon="add" />
{{ $gettext('Add Role') }}
</b-button>
</b-card-body>
<data-table ref="datatable" id="permissions" paginated :fields="fields" :api-url="listUrl">
<data-table
id="permissions"
ref="datatable"
paginated
:fields="fields"
:api-url="listUrl"
>
<template #cell(permissions)="row">
<div v-if="row.item.permissions.global.length > 0">
{{ $gettext('Global') }}
:
{{ getGlobalPermissionNames(row.item.permissions.global).join(', ') }}
</div>
<div v-for="(permissions, stationId) in row.item.permissions.station" :key="stationId">
<div
v-for="(permissions, stationId) in row.item.permissions.station"
:key="stationId"
>
<b>{{ getStationName(stationId) }}</b>:
{{ getStationPermissionNames(permissions).join(', ') }}
</div>
</template>
<template #cell(actions)="row">
<b-button-group size="sm" v-if="!row.item.is_super_admin">
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
<b-button-group
v-if="!row.item.is_super_admin"
size="sm"
>
<b-button
size="sm"
variant="primary"
@click.prevent="doEdit(row.item.links.self)"
>
{{ $gettext('Edit') }}
</b-button>
<b-button v-if="row.item.id !== 1" size="sm" variant="danger"
@click.prevent="doDelete(row.item.links.self)">
<b-button
v-if="row.item.id !== 1"
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -45,8 +70,14 @@
</data-table>
</b-card>
<edit-modal ref="editModal" :create-url="listUrl" :station-permissions="stationPermissions" :stations="stations"
:global-permissions="globalPermissions" @relist="relist"></edit-modal>
<edit-modal
ref="editModal"
:create-url="listUrl"
:station-permissions="stationPermissions"
:stations="stations"
:global-permissions="globalPermissions"
@relist="relist"
/>
</template>
<script>

View File

@ -1,16 +1,28 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents">
<modal-form
ref="modal"
:loading="loading"
:title="langTitle"
:error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<b-tabs
content-class="mt-3"
pills
>
<admin-permissions-global-form
:form="v$"
:global-permissions="globalPermissions"
/>
<b-tabs content-class="mt-3" pills>
<admin-permissions-global-form :form="v$" :global-permissions="globalPermissions">
</admin-permissions-global-form>
<admin-permissions-station-form :form="v$" :stations="stations"
:station-permissions="stationPermissions">
</admin-permissions-station-form>
<admin-permissions-station-form
:form="v$"
:stations="stations"
:station-permissions="stationPermissions"
/>
</b-tabs>
</modal-form>
</template>

View File

@ -1,15 +1,25 @@
<template>
<b-tab :title="$gettext('Global Permissions')" active>
<b-tab
:title="$gettext('Global Permissions')"
active
>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_name" :field="form.name">
<b-wrapped-form-group
id="edit_form_name"
class="col-md-12"
:field="form.name"
>
<template #label>
{{ $gettext('Role Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_global_permissions"
:field="form.permissions.global">
<b-wrapped-form-group
id="edit_form_global_permissions"
class="col-md-12"
:field="form.permissions.global"
>
<template #label>
{{ $gettext('Global Permissions') }}
</template>
@ -19,9 +29,12 @@
}}
</template>
<template #default="props">
<b-form-checkbox-group :id="props.id" :options="globalPermissionOptions"
v-model="props.field.$model" stacked>
</b-form-checkbox-group>
<b-form-checkbox-group
:id="props.id"
v-model="props.field.$model"
:options="globalPermissionOptions"
stacked
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,19 +1,29 @@
<template>
<b-tab :title="$gettext('Station Permissions')">
<permissions-form-station-row
v-for="(row, index) in form.permissions.$model.station" :key="index"
:stations="stations" :station-permissions="stationPermissions"
v-model:row="form.permissions.$model.station[index]" @remove="remove(index)"
></permissions-form-station-row>
v-for="(row, index) in form.permissions.$model.station"
:key="index"
v-model:row="form.permissions.$model.station[index]"
:stations="stations"
:station-permissions="stationPermissions"
@remove="remove(index)"
/>
<b-button-group v-if="hasRemainingStations">
<b-dropdown size="sm" variant="outline-primary">
<b-dropdown
size="sm"
variant="outline-primary"
>
<template #button-content>
{{ $gettext('Add Station') }}
</template>
<div style="max-height: 300px; overflow-y: auto;">
<b-dropdown-item-button v-for="(stationName, stationId) in remainingStations" :key="stationId"
@click="add(stationId)">{{ stationName }}
<b-dropdown-item-button
v-for="(stationName, stationId) in remainingStations"
:key="stationId"
@click="add(stationId)"
>
{{ stationName }}
</b-dropdown-item-button>
</div>
</b-dropdown>

View File

@ -1,5 +1,8 @@
<template>
<b-card class="mb-3" no-body>
<b-card
class="mb-3"
no-body
>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
@ -7,8 +10,13 @@
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="$emit('remove')">
<icon icon="remove"></icon>
<b-button
size="sm"
variant="outline-light"
class="py-2 pr-0"
@click.prevent="$emit('remove')"
>
<icon icon="remove" />
{{ $gettext('Remove') }}
</b-button>
</div>
@ -16,9 +24,11 @@
<b-card-body>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-12"
:id="'edit_form_station_permissions_'+row.station_id"
:field="v$.permissions">
<b-wrapped-form-group
:id="'edit_form_station_permissions_'+row.station_id"
class="col-md-12"
:field="v$.permissions"
>
<template #label>
{{ $gettext('Station Permissions') }}
</template>
@ -26,9 +36,12 @@
{{ $gettext('Users with this role will have these permissions for this single station.') }}
</template>
<template #default="props">
<b-form-checkbox-group :id="props.id" :options="stationPermissionOptions"
v-model="props.field.$model" stacked>
</b-form-checkbox-group>
<b-form-checkbox-group
:id="props.id"
v-model="props.field.$model"
:options="stationPermissionOptions"
stacked
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,6 +1,9 @@
<template>
<form class="form vue-form" @submit.prevent="submit">
<slot name="preCard"></slot>
<form
class="form vue-form"
@submit.prevent="submit"
>
<slot name="preCard" />
<b-card no-body>
<div class="card-header bg-primary-dark">
@ -11,18 +14,30 @@
</h2>
</div>
<slot name="cardUpper"></slot>
<slot name="cardUpper" />
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-overlay variant="card" :show="loading">
<b-tabs pills card lazy>
<b-overlay
variant="card"
:show="loading"
>
<b-tabs
pills
card
lazy
>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.generalTab)">
<template #title>
{{ $gettext('Settings') }}
</template>
<settings-general-tab :form="v$"></settings-general-tab>
<settings-general-tab :form="v$" />
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.securityPrivacyTab)">
@ -30,7 +45,7 @@
{{ $gettext('Security & Privacy') }}
</template>
<settings-security-privacy-tab :form="v$"></settings-security-privacy-tab>
<settings-security-privacy-tab :form="v$" />
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.servicesTab)">
@ -38,16 +53,22 @@
{{ $gettext('Services') }}
</template>
<settings-services-tab :form="v$"
:release-channel="releaseChannel"
:test-message-url="testMessageUrl"
:acme-url="acmeUrl"></settings-services-tab>
<settings-services-tab
:form="v$"
:release-channel="releaseChannel"
:test-message-url="testMessageUrl"
:acme-url="acmeUrl"
/>
</b-tab>
</b-tabs>
</b-overlay>
<b-card-body body-class="card-padding-sm">
<b-button size="lg" type="submit" :variant="(v$.$invalid) ? 'danger' : 'primary'">
<b-button
size="lg"
type="submit"
:variant="(v$.$invalid) ? 'danger' : 'primary'"
>
<slot name="submitButtonName">
{{ $gettext('Save Changes') }}
</slot>

View File

@ -1,8 +1,12 @@
<template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_base_url" :field="form.base_url" input-type="url">
<b-wrapped-form-group
id="edit_form_base_url"
class="col-md-6"
:field="form.base_url"
input-type="url"
>
<template #label>
{{ $gettext('Site Base URL') }}
</template>
@ -13,7 +17,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_instance_name" :field="form.instance_name">
<b-wrapped-form-group
id="edit_form_instance_name"
class="col-md-6"
:field="form.instance_name"
>
<template #label>
{{ $gettext('AzuraCast Instance Name') }}
</template>
@ -24,8 +32,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_prefer_browser_url"
:field="form.prefer_browser_url">
<b-wrapped-form-checkbox
id="edit_form_prefer_browser_url"
class="col-md-6"
:field="form.prefer_browser_url"
>
<template #label>
{{ $gettext('Prefer Browser URL (If Available)') }}
</template>
@ -36,8 +47,11 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_use_radio_proxy"
:field="form.use_radio_proxy">
<b-wrapped-form-checkbox
id="edit_form_use_radio_proxy"
class="col-md-6"
:field="form.use_radio_proxy"
>
<template #label>
{{ $gettext('Use Web Proxy for Radio') }}
</template>
@ -48,7 +62,11 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-6" id="edit_form_history_keep_days" :field="form.history_keep_days">
<b-wrapped-form-group
id="edit_form_history_keep_days"
class="col-md-6"
:field="form.history_keep_days"
>
<template #label>
{{ $gettext('Days of Playback History to Keep') }}
</template>
@ -58,13 +76,20 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" v-model="props.field.$model"
:options="historyKeepDaysOptions"></b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="historyKeepDaysOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_enable_static_nowplaying"
:field="form.enable_static_nowplaying">
<b-wrapped-form-checkbox
id="edit_form_enable_static_nowplaying"
class="col-md-6"
:field="form.enable_static_nowplaying"
>
<template #label>
{{ $gettext('Use High-Performance Now Playing Updates') }}
</template>
@ -75,8 +100,11 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_enable_advanced_features"
:field="form.enable_advanced_features">
<b-wrapped-form-checkbox
id="edit_form_enable_advanced_features"
class="col-md-6"
:field="form.enable_advanced_features"
>
<template #label>
{{ $gettext('Enable Advanced Features') }}
</template>
@ -86,7 +114,6 @@
}}
</template>
</b-wrapped-form-checkbox>
</div>
</b-form-fieldset>
</template>

View File

@ -5,7 +5,11 @@
</template>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_analytics" :field="form.analytics">
<b-wrapped-form-group
id="edit_form_analytics"
class="col-md-12"
:field="form.analytics"
>
<template #label>
{{ $gettext('Listener Analytics Collection') }}
</template>
@ -15,7 +19,11 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" v-model="props.field.$model">
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
>
<b-form-radio value="all">
<b>
{{ $gettext('Full:') }}
@ -46,8 +54,11 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_always_use_ssl"
:field="form.always_use_ssl">
<b-wrapped-form-checkbox
id="edit_form_always_use_ssl"
class="col-md-12"
:field="form.always_use_ssl"
>
<template #label>
{{ $gettext('Always Use HTTPS') }}
</template>
@ -58,8 +69,11 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-12" id="edit_form_api_access_control"
:field="form.api_access_control">
<b-wrapped-form-group
id="edit_form_api_access_control"
class="col-md-12"
:field="form.api_access_control"
>
<template #label>
{{ $gettext('API "Access-Control-Allow-Origin" Header') }}
</template>
@ -68,8 +82,10 @@
$gettext('Set to * to allow all sources, or specify a list of origins separated by a comma (,).')
}}
<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin"
target="_blank">
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin"
target="_blank"
>
{{ $gettext('Learn more about this header.') }}
</a>
</template>

View File

@ -5,13 +5,18 @@
</template>
<div class="form-row">
<b-form-markup class="col-md-6" id="form_release_channel">
<b-form-markup
id="form_release_channel"
class="col-md-6"
>
<template #label>
{{ $gettext('Release Channel') }}
</template>
<template #description>
<a href="https://docs.azuracast.com/en/getting-started/updates/release-channels"
target="_blank">
<a
href="https://docs.azuracast.com/en/getting-started/updates/release-channels"
target="_blank"
>
{{ $gettext('Learn more about release channels in the AzuraCast docs.') }}
</a>
</template>
@ -21,8 +26,11 @@
</p>
</b-form-markup>
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_check_for_updates"
:field="form.check_for_updates">
<b-wrapped-form-checkbox
id="edit_form_check_for_updates"
class="col-md-6"
:field="form.check_for_updates"
>
<template #label>
{{ $gettext('Show Update Announcements') }}
</template>
@ -44,8 +52,11 @@
</template>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_acme_domains"
:field="form.acme_domains">
<b-wrapped-form-group
id="edit_form_acme_domains"
class="col-md-6"
:field="form.acme_domains"
>
<template #label>
{{ $gettext('Domain Name(s)') }}
</template>
@ -56,8 +67,12 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_acme_email"
:field="form.acme_email" input-type="email">
<b-wrapped-form-group
id="edit_form_acme_email"
class="col-md-6"
:field="form.acme_email"
input-type="email"
>
<template #label>
{{ $gettext('E-mail Address (Optional)') }}
</template>
@ -67,8 +82,13 @@
</b-wrapped-form-group>
<div class="form-group col">
<b-button size="sm" variant="primary" :disabled="form.$anyDirty" @click="generateAcmeCert">
<icon icon="badge"></icon>
<b-button
size="sm"
variant="primary"
:disabled="form.$anyDirty"
@click="generateAcmeCert"
>
<icon icon="badge" />
{{ $gettext('Generate/Renew Certificate') }}
<span v-if="form.$anyDirty">
({{ $gettext('Save Changes first') }})
@ -87,45 +107,68 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_mail_enabled"
:field="form.mail_enabled">
<b-wrapped-form-checkbox
id="edit_form_mail_enabled"
class="col-md-12"
:field="form.mail_enabled"
>
<template #label>
{{ $gettext('Enable Mail Delivery') }}
</template>
</b-wrapped-form-checkbox>
</div>
<div class="form-row mt-2" v-if="form.mail_enabled.$model">
<b-wrapped-form-group class="col-md-6" id="edit_form_mail_sender_name"
:field="form.mail_sender_name">
<div
v-if="form.mail_enabled.$model"
class="form-row mt-2"
>
<b-wrapped-form-group
id="edit_form_mail_sender_name"
class="col-md-6"
:field="form.mail_sender_name"
>
<template #label>
{{ $gettext('Sender Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_mail_sender_email"
:field="form.mail_sender_email" input-type="email">
<b-wrapped-form-group
id="edit_form_mail_sender_email"
class="col-md-6"
:field="form.mail_sender_email"
input-type="email"
>
<template #label>
{{ $gettext('Sender E-mail Address') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" id="edit_form_mail_smtp_host"
:field="form.mail_smtp_host">
<b-wrapped-form-group
id="edit_form_mail_smtp_host"
class="col-md-4"
:field="form.mail_smtp_host"
>
<template #label>
{{ $gettext('SMTP Host') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-3" id="edit_form_mail_smtp_port"
:field="form.mail_smtp_port" input-type="number">
<b-wrapped-form-group
id="edit_form_mail_smtp_port"
class="col-md-3"
:field="form.mail_smtp_port"
input-type="number"
>
<template #label>
{{ $gettext('SMTP Port') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-5" id="edit_form_mail_smtp_secure"
:field="form.mail_smtp_secure">
<b-wrapped-form-checkbox
id="edit_form_mail_smtp_secure"
class="col-md-5"
:field="form.mail_smtp_secure"
>
<template #label>
{{ $gettext('Use Secure (TLS) SMTP Connection') }}
</template>
@ -134,23 +177,35 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-6" id="edit_form_mail_smtp_username"
:field="form.mail_smtp_username">
<b-wrapped-form-group
id="edit_form_mail_smtp_username"
class="col-md-6"
:field="form.mail_smtp_username"
>
<template #label>
{{ $gettext('SMTP Username') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_mail_smtp_password"
:field="form.mail_smtp_password" input-type="password">
<b-wrapped-form-group
id="edit_form_mail_smtp_password"
class="col-md-6"
:field="form.mail_smtp_password"
input-type="password"
>
<template #label>
{{ $gettext('SMTP Password') }}
</template>
</b-wrapped-form-group>
<div class="form-group col">
<b-button size="sm" variant="primary" :disabled="form.$anyDirty" v-b-modal.send_test_message>
<icon icon="send"></icon>
<b-button
v-b-modal.send_test_message
size="sm"
variant="primary"
:disabled="form.$anyDirty"
>
<icon icon="send" />
{{ $gettext('Send Test Message') }}
<span v-if="form.$anyDirty">
({{ $gettext('Save Changes first') }})
@ -166,18 +221,29 @@
</template>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_avatar_service" :field="form.avatar_service">
<b-wrapped-form-group
id="edit_form_avatar_service"
class="col-md-6"
:field="form.avatar_service"
>
<template #label>
{{ $gettext('Avatar Service') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" v-model="props.field.$model"
:options="avatarServiceOptions"></b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="avatarServiceOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_avatar_default_url"
:field="form.avatar_default_url">
<b-wrapped-form-group
id="edit_form_avatar_default_url"
class="col-md-6"
:field="form.avatar_default_url"
>
<template #label>
{{ $gettext('Default Avatar URL') }}
</template>
@ -191,28 +257,41 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-6" id="use_external_album_art_in_apis"
:field="form.use_external_album_art_in_apis">
<b-wrapped-form-checkbox
id="use_external_album_art_in_apis"
class="col-md-6"
:field="form.use_external_album_art_in_apis"
>
<template #label>
{{ $gettext('Check Web Services for Album Art for "Now Playing" Tracks') }}
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox class="col-md-6" id="use_external_album_art_when_processing_media"
:field="form.use_external_album_art_when_processing_media">
<b-wrapped-form-checkbox
id="use_external_album_art_when_processing_media"
class="col-md-6"
:field="form.use_external_album_art_when_processing_media"
>
<template #label>
{{ $gettext('Check Web Services for Album Art When Uploading Media') }}
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-12" id="edit_form_last_fm_api_key" :field="form.last_fm_api_key">
<b-wrapped-form-group
id="edit_form_last_fm_api_key"
class="col-md-12"
:field="form.last_fm_api_key"
>
<template #label>
{{ $gettext('Last.fm API Key') }}
</template>
<template #description>
{{ $gettext('This service can provide album art for tracks where none is available locally.') }}
<br>
<a href="https://www.last.fm/api/account/create" target="_blank">
<a
href="https://www.last.fm/api/account/create"
target="_blank"
>
{{ $gettext('Apply for an API key at Last.fm') }}
</a>
</template>
@ -220,9 +299,9 @@
</div>
</b-form-fieldset>
<streaming-log-modal ref="$acmeModal"></streaming-log-modal>
<streaming-log-modal ref="$acmeModal" />
<admin-settings-test-message-modal :test-message-url="testMessageUrl"></admin-settings-test-message-modal>
<admin-settings-test-message-modal :test-message-url="testMessageUrl" />
</template>
<script setup>

View File

@ -1,17 +1,32 @@
<template>
<b-modal id="send_test_message" centered ref="$modal" :title="$gettext('Send Test Message')">
<b-modal
id="send_test_message"
ref="$modal"
centered
:title="$gettext('Send Test Message')"
>
<b-form @submit.prevent="doSendTest">
<b-wrapped-form-group id="email_address" :field="v$.emailAddress" autofocus>
<b-wrapped-form-group
id="email_address"
:field="v$.emailAddress"
autofocus
>
<template #label>
{{ $gettext('E-mail Address') }}
</template>
</b-wrapped-form-group>
</b-form>
<template #modal-footer>
<b-button variant="default" @click="close">
<b-button
variant="default"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button :variant="(v$.$invalid) ? 'danger' : 'primary'" @click="doSendTest">
<b-button
:variant="(v$.$invalid) ? 'danger' : 'primary'"
@click="doSendTest"
>
{{ $gettext('Send Test Message') }}
</b-button>
</template>

View File

@ -7,8 +7,10 @@
</div>
<div class="card-body">
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<div class="form-row">
<div class="col-md-7">
<fieldset>
@ -30,8 +32,10 @@
<li>
{{ $gettext('Download the Linux x64 binary from the Shoutcast Radio Manager:') }}
<br>
<a href="https://radiomanager.shoutcast.com/register/serverSoftwareFreemium"
target="_blank">
<a
href="https://radiomanager.shoutcast.com/register/serverSoftwareFreemium"
target="_blank"
>
{{ $gettext('Shoutcast Radio Manager') }}
</a>
</li>
@ -54,15 +58,24 @@
{{ $gettext('Current Installed Version') }}
</legend>
<p v-if="version" class="text-success card-text">
<p
v-if="version"
class="text-success card-text"
>
{{ langInstalledVersion }}
</p>
<p v-else class="text-danger card-text">
<p
v-else
class="text-danger card-text"
>
{{ $gettext('Shoutcast 2 DNAS is not currently installed on this installation.') }}
</p>
</fieldset>
<flow-upload :target-url="apiUrl" @complete="relist"></flow-upload>
<flow-upload
:target-url="apiUrl"
@complete="relist"
/>
</div>
</div>
</b-overlay>

View File

@ -1,19 +1,32 @@
<template>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Stations') }}</h2>
<h2 class="card-title">
{{ $gettext('Stations') }}
</h2>
</b-card-header>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doCreate">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="doCreate"
>
<icon icon="add" />
{{ $gettext('Add Station') }}
</b-button>
</b-card-body>
<data-table ref="datatable" id="stations" paginated :fields="fields" :api-url="listUrl">
<data-table
id="stations"
ref="datatable"
paginated
:fields="fields"
:api-url="listUrl"
>
<template #cell(name)="row">
<div class="typography-subheading">{{ row.item.name }}</div>
<div class="typography-subheading">
{{ row.item.name }}
</div>
<code>{{ row.item.short_name }}</code>
</template>
<template #cell(frontend_type)="row">
@ -24,17 +37,33 @@
</template>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="secondary" :href="row.item.links.manage" target="_blank">
<b-button
size="sm"
variant="secondary"
:href="row.item.links.manage"
target="_blank"
>
{{ $gettext('Manage') }}
</b-button>
<b-button size="sm" variant="secondary"
@click.prevent="doClone(row.item.name, row.item.links.clone)">
<b-button
size="sm"
variant="secondary"
@click.prevent="doClone(row.item.name, row.item.links.clone)"
>
{{ $gettext('Clone') }}
</b-button>
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
<b-button
size="sm"
variant="primary"
@click.prevent="doEdit(row.item.links.self)"
>
{{ $gettext('Edit') }}
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -42,10 +71,17 @@
</data-table>
</b-card>
<admin-stations-edit-modal v-bind="$props" ref="editModal" :create-url="listUrl"
@relist="relist"></admin-stations-edit-modal>
<admin-stations-edit-modal
v-bind="$props"
ref="editModal"
:create-url="listUrl"
@relist="relist"
/>
<admin-stations-clone-modal ref="cloneModal" @relist="relist"></admin-stations-clone-modal>
<admin-stations-clone-modal
ref="cloneModal"
@relist="relist"
/>
</template>
<script>

View File

@ -1,10 +1,14 @@
<template>
<modal-form ref="$modal" :loading="loading" :title="$gettext('Clone Station')" :error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-stations-clone-modal-form :form="v$"></admin-stations-clone-modal-form>
<modal-form
ref="$modal"
:loading="loading"
:title="$gettext('Clone Station')"
:error="error"
:disable-save-button="v$.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<admin-stations-clone-modal-form :form="v$" />
</modal-form>
</template>

View File

@ -1,19 +1,31 @@
<template>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_name" :field="form.name">
<b-wrapped-form-group
id="edit_form_name"
class="col-md-12"
:field="form.name"
>
<template #label>
{{ $gettext('New Station Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_description" :field="form.description"
input-type="textarea">
<b-wrapped-form-group
id="edit_form_description"
class="col-md-12"
:field="form.description"
input-type="textarea"
>
<template #label>
{{ $gettext('New Station Description') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_clone" :field="form.clone">
<b-wrapped-form-group
id="edit_form_clone"
class="col-md-12"
:field="form.clone"
>
<template #label>
{{ $gettext('Copy to New Station') }}
</template>
@ -23,7 +35,7 @@
v-model="props.field.$model"
:options="cloneOptions"
stacked
></b-form-checkbox-group>
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,19 +1,43 @@
<template>
<b-modal size="lg" id="station_edit_modal" ref="$modal" :title="langTitle" :busy="loading"
@shown="resetForm" @hidden="clearContents">
<admin-stations-form v-bind="$props" ref="$form" is-modal :create-url="createUrl" :edit-url="editUrl"
:is-edit-mode="isEditMode" @error="close" @submitted="onSubmit"
@validUpdate="onValidUpdate" @loadingUpdate="onLoadingUpdate">
<b-modal
id="station_edit_modal"
ref="$modal"
size="lg"
:title="langTitle"
:busy="loading"
@shown="resetForm"
@hidden="clearContents"
>
<admin-stations-form
v-bind="$props"
ref="$form"
is-modal
:create-url="createUrl"
:edit-url="editUrl"
:is-edit-mode="isEditMode"
@error="close"
@submitted="onSubmit"
@validUpdate="onValidUpdate"
@loadingUpdate="onLoadingUpdate"
>
<template #submitButton>
<invisible-submit-button></invisible-submit-button>
<invisible-submit-button />
</template>
</admin-stations-form>
<template #modal-footer>
<b-button variant="default" type="button" @click="close">
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button :variant="(disableSaveButton) ? 'danger' : 'primary'" type="submit" @click="doSubmit">
<b-button
:variant="(disableSaveButton) ? 'danger' : 'primary'"
type="submit"
@click="doSubmit"
>
{{ $gettext('Save Changes') }}
</b-button>
</template>

View File

@ -2,7 +2,11 @@
<b-form-group>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-6" id="edit_form_is_enabled" :field="form.is_enabled">
<b-wrapped-form-checkbox
id="edit_form_is_enabled"
class="col-md-6"
:field="form.is_enabled"
>
<template #label>
{{ $gettext('Enable Broadcasting') }}
</template>
@ -11,8 +15,13 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6" id="edit_form_radio_base_dir"
:field="form.radio_base_dir" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_radio_base_dir"
class="col-md-6"
:field="form.radio_base_dir"
advanced
>
<template #label>
{{ $gettext('Base Station Directory') }}
</template>
@ -26,38 +35,59 @@
</b-form-fieldset>
<b-form-fieldset>
<b-overlay variant="card" :show="storageLocationsLoading">
<b-overlay
variant="card"
:show="storageLocationsLoading"
>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_media_storage_location"
:field="form.media_storage_location">
<b-wrapped-form-group
id="edit_form_media_storage_location"
class="col-md-12"
:field="form.media_storage_location"
>
<template #label>
{{ $gettext('Media Storage Location') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="storageLocationOptions.media_storage_location"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="storageLocationOptions.media_storage_location"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_recordings_storage_location"
:field="form.recordings_storage_location">
<b-wrapped-form-group
id="edit_form_recordings_storage_location"
class="col-md-12"
:field="form.recordings_storage_location"
>
<template #label>
{{ $gettext('Live Recordings Storage Location') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="storageLocationOptions.recordings_storage_location"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="storageLocationOptions.recordings_storage_location"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_podcasts_storage_location"
:field="form.podcasts_storage_location">
<b-wrapped-form-group
id="edit_form_podcasts_storage_location"
class="col-md-12"
:field="form.podcasts_storage_location"
>
<template #label>
{{ $gettext('Podcasts Storage Location') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="storageLocationOptions.podcasts_storage_location"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="storageLocationOptions.podcasts_storage_location"
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,8 +1,11 @@
<template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_backend_type"
:field="form.backend_type">
<b-wrapped-form-group
id="edit_form_backend_type"
class="col-md-12"
:field="form.backend_type"
>
<template #label>
{{ $gettext('AutoDJ Service') }}
</template>
@ -12,9 +15,12 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="backendTypeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="backendTypeOptions"
/>
</template>
</b-wrapped-form-group>
</div>
@ -23,8 +29,11 @@
<b-form-fieldset v-if="isBackendEnabled">
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-7" id="edit_form_backend_crossfade_type"
:field="form.backend_config.crossfade_type">
<b-wrapped-form-group
id="edit_form_backend_crossfade_type"
class="col-md-7"
:field="form.backend_config.crossfade_type"
>
<template #label>
{{ $gettext('Crossfade Method') }}
</template>
@ -34,15 +43,22 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="crossfadeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="crossfadeOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-5" id="edit_form_backend_crossfade"
:field="form.backend_config.crossfade" input-type="number"
:input-attrs="{ min: '0.0', max: '30.0', step: '0.1' }">
<b-wrapped-form-group
id="edit_form_backend_crossfade"
class="col-md-5"
:field="form.backend_config.crossfade"
input-type="number"
:input-attrs="{ min: '0.0', max: '30.0', step: '0.1' }"
>
<template #label>
{{ $gettext('Crossfade Duration (Seconds)') }}
</template>
@ -52,8 +68,11 @@
</b-wrapped-form-group>
</div>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_backend_config_audio_processing_method"
:field="form.backend_config.audio_processing_method">
<b-wrapped-form-group
id="edit_form_backend_config_audio_processing_method"
class="col-md-12"
:field="form.backend_config.audio_processing_method"
>
<template #label>
{{ $gettext('Audio Processing Method') }}
</template>
@ -63,9 +82,12 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="audioProcessingOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="audioProcessingOptions"
/>
</template>
</b-wrapped-form-group>
</div>
@ -79,15 +101,22 @@
{{
$gettext('Stereo Tool is an industry standard for software audio processing. For more information on how to configure it, please refer to the')
}}
<a href="https://www.thimeo.com/stereo-tool/" target="_blank">
<a
href="https://www.thimeo.com/stereo-tool/"
target="_blank"
>
{{ $gettext('Stereo Tool documentation.') }}
</a>
</template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-7" id="edit_form_backend_stereo_tool_license_key"
:field="form.backend_config.stereo_tool_license_key" input-type="text">
<b-wrapped-form-group
id="edit_form_backend_stereo_tool_license_key"
class="col-md-7"
:field="form.backend_config.stereo_tool_license_key"
input-type="text"
>
<template #label>
{{ $gettext('Stereo Tool License Key') }}
</template>
@ -98,7 +127,10 @@
</template>
</b-wrapped-form-group>
<b-form-markup class="col-md-5" id="edit_form_backend_stereo_tool_config">
<b-form-markup
id="edit_form_backend_stereo_tool_config"
class="col-md-5"
>
<template #label>
{{ $gettext('Upload Stereo Tool Configuration') }}
</template>
@ -119,9 +151,12 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-6"
id="edit_form_backend_use_manual_autodj"
:field="form.backend_config.use_manual_autodj" advanced>
<b-wrapped-form-checkbox
id="edit_form_backend_use_manual_autodj"
class="col-md-6"
:field="form.backend_config.use_manual_autodj"
advanced
>
<template #label>
{{ $gettext('Manual AutoDJ Mode') }}
</template>
@ -132,9 +167,12 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox class="col-md-6"
id="edit_form_backend_enable_replaygain_metadata"
:field="form.backend_config.enable_replaygain_metadata" advanced>
<b-wrapped-form-checkbox
id="edit_form_backend_enable_replaygain_metadata"
class="col-md-6"
:field="form.backend_config.enable_replaygain_metadata"
advanced
>
<template #label>
{{ $gettext('Use Replaygain Metadata') }}
</template>
@ -145,9 +183,14 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_telnet_port"
:field="form.backend_config.telnet_port" input-type="number"
:input-attrs="{ min: '0' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_telnet_port"
class="col-md-6"
:field="form.backend_config.telnet_port"
input-type="number"
:input-attrs="{ min: '0' }"
advanced
>
<template #label>
{{ $gettext('Customize Internal Request Processing Port') }}
</template>
@ -158,9 +201,14 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_autodj_queue_length"
:field="form.backend_config.autodj_queue_length" input-type="number"
:input-attrs="{ min: '2', max: '25' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_autodj_queue_length"
class="col-md-6"
:field="form.backend_config.autodj_queue_length"
input-type="number"
:input-attrs="{ min: '2', max: '25' }"
advanced
>
<template #label>
{{ $gettext('AutoDJ Queue Length') }}
</template>
@ -171,8 +219,12 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_charset"
:field="form.backend_config.charset" advanced>
<b-wrapped-form-group
id="edit_form_backend_charset"
class="col-md-6"
:field="form.backend_config.charset"
advanced
>
<template #label>
{{ $gettext('Character Set Encoding') }}
</template>
@ -182,14 +234,21 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="charsetOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="charsetOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_performance_mode"
:field="form.backend_config.performance_mode" advanced>
<b-wrapped-form-group
id="edit_form_backend_performance_mode"
class="col-md-6"
:field="form.backend_config.performance_mode"
advanced
>
<template #label>
{{ $gettext('Liquidsoap Performance Tuning') }}
</template>
@ -199,15 +258,23 @@
}}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="performanceModeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="performanceModeOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_duplicate_prevention_time_range"
:field="form.backend_config.duplicate_prevention_time_range"
input-type="number" :input-attrs="{ min: '0', max: '1440' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_duplicate_prevention_time_range"
class="col-md-6"
:field="form.backend_config.duplicate_prevention_time_range"
input-type="number"
:input-attrs="{ min: '0', max: '1440' }"
advanced
>
<template #label>
{{ $gettext('Duplicate Prevention Time Range (Minutes)') }}
</template>

View File

@ -1,8 +1,11 @@
<template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_frontend_type"
:field="form.frontend_type">
<b-wrapped-form-group
id="edit_form_frontend_type"
class="col-md-12"
:field="form.frontend_type"
>
<template #label>
{{ $gettext('Broadcasting Service') }}
</template>
@ -10,9 +13,12 @@
{{ $gettext('This software delivers your broadcast to the listening audience.') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="frontendTypeOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="frontendTypeOptions"
/>
</template>
</b-wrapped-form-group>
</div>
@ -21,15 +27,21 @@
<b-form-fieldset v-if="isLocalFrontend">
<b-form-fieldset v-if="isShoutcastFrontend">
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_frontend_sc_license_id"
:field="form.frontend_config.sc_license_id">
<b-wrapped-form-group
id="edit_form_frontend_sc_license_id"
class="col-md-6"
:field="form.frontend_config.sc_license_id"
>
<template #label>
{{ $gettext('Shoutcast License ID') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_frontend_sc_user_id"
:field="form.frontend_config.sc_user_id">
<b-wrapped-form-group
id="edit_form_frontend_sc_user_id"
class="col-md-6"
:field="form.frontend_config.sc_user_id"
>
<template #label>
{{ $gettext('Shoutcast User ID') }}
</template>
@ -39,8 +51,11 @@
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_frontend_source_pw"
:field="form.frontend_config.source_pw">
<b-wrapped-form-group
id="edit_form_frontend_source_pw"
class="col-md-6"
:field="form.frontend_config.source_pw"
>
<template #label>
{{ $gettext('Customize Source Password') }}
</template>
@ -49,8 +64,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_frontend_admin_pw"
:field="form.frontend_config.admin_pw">
<b-wrapped-form-group
id="edit_form_frontend_admin_pw"
class="col-md-6"
:field="form.frontend_config.admin_pw"
>
<template #label>
{{ $gettext('Customize Administrator Password') }}
</template>
@ -59,9 +77,15 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6" id="edit_form_frontend_port"
:field="form.frontend_config.port" input-type="number"
:input-attrs="{min: '0'}" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_frontend_port"
class="col-md-6"
:field="form.frontend_config.port"
input-type="number"
:input-attrs="{min: '0'}"
advanced
>
<template #label>
{{ $gettext('Customize Broadcasting Port') }}
</template>
@ -72,8 +96,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6" id="edit_form_max_listeners"
:field="form.frontend_config.max_listeners" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_max_listeners"
class="col-md-6"
:field="form.frontend_config.max_listeners"
advanced
>
<template #label>
{{ $gettext('Maximum Listeners') }}
</template>
@ -89,9 +118,13 @@
<b-form-fieldset v-if="showAdvanced">
<div class="form-row">
<b-col md="5">
<b-wrapped-form-group id="edit_form_frontend_banned_ips"
:field="form.frontend_config.banned_ips" input-type="textarea"
:input-attrs="{class: 'text-preformatted'}" advanced>
<b-wrapped-form-group
id="edit_form_frontend_banned_ips"
:field="form.frontend_config.banned_ips"
input-type="textarea"
:input-attrs="{class: 'text-preformatted'}"
advanced
>
<template #label>
{{ $gettext('Banned IP Addresses') }}
</template>
@ -100,9 +133,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="edit_form_frontend_allowed_ips"
:field="form.frontend_config.allowed_ips" input-type="textarea"
:input-attrs="{class: 'text-preformatted'}" advanced>
<b-wrapped-form-group
id="edit_form_frontend_allowed_ips"
:field="form.frontend_config.allowed_ips"
input-type="textarea"
:input-attrs="{class: 'text-preformatted'}"
advanced
>
<template #label>
{{ $gettext('Allowed IP Addresses') }}
</template>
@ -111,9 +148,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="edit_form_frontend_banned_user_agents"
:field="form.frontend_config.banned_user_agents" input-type="textarea"
:input-attrs="{class: 'text-preformatted'}" advanced>
<b-wrapped-form-group
id="edit_form_frontend_banned_user_agents"
:field="form.frontend_config.banned_user_agents"
input-type="textarea"
:input-attrs="{class: 'text-preformatted'}"
advanced
>
<template #label>
{{ $gettext('Banned User Agents') }}
</template>
@ -123,9 +164,12 @@
</b-wrapped-form-group>
</b-col>
<b-wrapped-form-group class="col-md-7" id="edit_form_frontend_banned_countries"
:field="form.frontend_config.banned_countries"
advanced>
<b-wrapped-form-group
id="edit_form_frontend_banned_countries"
class="col-md-7"
:field="form.frontend_config.banned_countries"
advanced
>
<template #label>
{{ $gettext('Banned Countries') }}
</template>
@ -133,11 +177,19 @@
{{ $gettext('Select the countries that are not allowed to connect to the streams.') }}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="countryOptions" style="min-height: 300px;"
multiple></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="countryOptions"
style="min-height: 300px;"
multiple
/>
<b-button block variant="outline-primary" @click.prevent="clearCountries">
<b-button
block
variant="outline-primary"
@click.prevent="clearCountries"
>
{{ $gettext('Clear List') }}
</b-button>
</template>
@ -158,10 +210,14 @@
</template>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_frontend_custom_config"
:field="form.frontend_config.custom_config" input-type="textarea"
:input-attrs="{class: 'text-preformatted', spellcheck: 'false', 'max-rows': 25, rows: 5}"
advanced>
<b-wrapped-form-group
id="edit_form_frontend_custom_config"
class="col-md-12"
:field="form.frontend_config.custom_config"
input-type="textarea"
:input-attrs="{class: 'text-preformatted', spellcheck: 'false', 'max-rows': 25, rows: 5}"
advanced
>
<template #label>
{{ $gettext('Custom Configuration') }}
</template>

View File

@ -12,8 +12,11 @@
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_enable_hls"
:field="form.enable_hls">
<b-wrapped-form-checkbox
id="edit_form_enable_hls"
class="col-md-12"
:field="form.enable_hls"
>
<template #label>
{{ $gettext('Enable HTTP Live Streaming (HLS)') }}
</template>
@ -23,15 +26,21 @@
<b-form-fieldset v-if="form.enable_hls.$model">
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_backend_hls_enable_on_public_player"
:field="form.backend_config.hls_enable_on_public_player">
<b-wrapped-form-checkbox
id="edit_form_backend_hls_enable_on_public_player"
class="col-md-12"
:field="form.backend_config.hls_enable_on_public_player"
>
<template #label>
{{ $gettext('Show HLS Stream on Public Player') }}
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_backend_hls_is_default"
:field="form.backend_config.hls_is_default">
<b-wrapped-form-checkbox
id="edit_form_backend_hls_is_default"
class="col-md-12"
:field="form.backend_config.hls_is_default"
>
<template #label>
{{ $gettext('Make HLS Stream Default in Public Player') }}
</template>
@ -41,28 +50,40 @@
<b-form-fieldset v-if="showAdvanced && form.enable_hls.$model">
<div class="form-row">
<b-wrapped-form-group class="col-md-4"
id="edit_form_backend_hls_segment_length"
:field="form.backend_config.hls_segment_length" input-type="number"
:input-attrs="{ min: '0', max: '60' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_hls_segment_length"
class="col-md-4"
:field="form.backend_config.hls_segment_length"
input-type="number"
:input-attrs="{ min: '0', max: '60' }"
advanced
>
<template #label>
{{ $gettext('Segment Length (Seconds)') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4"
id="edit_form_backend_hls_segments_in_playlist"
:field="form.backend_config.hls_segments_in_playlist" input-type="number"
:input-attrs="{ min: '0', max: '60' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_hls_segments_in_playlist"
class="col-md-4"
:field="form.backend_config.hls_segments_in_playlist"
input-type="number"
:input-attrs="{ min: '0', max: '60' }"
advanced
>
<template #label>
{{ $gettext('Segments in Playlist') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4"
id="edit_form_backend_hls_segments_overhead"
:field="form.backend_config.hls_segments_overhead" input-type="number"
:input-attrs="{ min: '0', max: '60' }" advanced>
<b-wrapped-form-group
id="edit_form_backend_hls_segments_overhead"
class="col-md-4"
:field="form.backend_config.hls_segments_overhead"
input-type="number"
:input-attrs="{ min: '0', max: '60' }"
advanced
>
<template #label>
{{ $gettext('Segments Overhead') }}
</template>
@ -71,7 +92,7 @@
</b-form-fieldset>
</b-form-fieldset>
</b-form-fieldset>
<backend-disabled v-else></backend-disabled>
<backend-disabled v-else />
</template>
<script setup>

View File

@ -1,26 +1,43 @@
<template>
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="edit_form_name" :field="form.name">
<b-wrapped-form-group
id="edit_form_name"
class="col-md-12"
:field="form.name"
>
<template #label>
{{ $gettext('Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_description" :field="form.description"
input-type="textarea">
<b-wrapped-form-group
id="edit_form_description"
class="col-md-12"
:field="form.description"
input-type="textarea"
>
<template #label>
{{ $gettext('Description') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_genre" :field="form.genre">
<b-wrapped-form-group
id="edit_form_genre"
class="col-md-6"
:field="form.genre"
>
<template #label>
{{ $gettext('Genre') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_url" :field="form.url" input-type="url">
<b-wrapped-form-group
id="edit_form_url"
class="col-md-6"
:field="form.url"
input-type="url"
>
<template #label>
{{ $gettext('Web Site URL') }}
</template>
@ -31,7 +48,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_timezone" :field="form.timezone">
<b-wrapped-form-group
id="edit_form_timezone"
class="col-md-12"
:field="form.timezone"
>
<template #label>
{{ $gettext('Time Zone') }}
</template>
@ -41,13 +62,19 @@
}}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="timezoneOptions"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="timezoneOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_default_album_art_url"
:field="form.default_album_art_url">
<b-wrapped-form-group
id="edit_form_default_album_art_url"
class="col-md-6"
:field="form.default_album_art_url"
>
<template #label>
{{ $gettext('Default Album Art URL') }}
</template>
@ -58,8 +85,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6" id="edit_form_short_name"
:field="form.short_name" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_short_name"
class="col-md-6"
:field="form.short_name"
advanced
>
<template #label>
{{ $gettext('URL Stub') }}
</template>
@ -70,8 +102,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6" id="edit_form_api_history_items"
:field="form.api_history_items" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_api_history_items"
class="col-md-6"
:field="form.api_history_items"
advanced
>
<template #label>
{{ $gettext('Number of Visible Recent Songs') }}
</template>
@ -81,8 +118,11 @@
}}
</template>
<template #default="props">
<b-form-select :id="props.id" v-model="props.field.$model"
:options="historyItemsOptions"></b-form-select>
<b-form-select
:id="props.id"
v-model="props.field.$model"
:options="historyItemsOptions"
/>
</template>
</b-wrapped-form-group>
</div>
@ -94,8 +134,11 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_enable_public_page"
:field="form.enable_public_page">
<b-wrapped-form-checkbox
id="edit_form_enable_public_page"
class="col-md-12"
:field="form.enable_public_page"
>
<template #label>
{{ $gettext('Enable Public Pages') }}
</template>
@ -112,8 +155,11 @@
</template>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_enable_on_demand"
:field="form.enable_on_demand">
<b-wrapped-form-checkbox
id="edit_form_enable_on_demand"
class="col-md-12"
:field="form.enable_on_demand"
>
<template #label>
{{ $gettext('Enable On-Demand Streaming') }}
</template>
@ -124,9 +170,12 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox v-if="form.enable_on_demand.$model" class="col-md-12"
id="edit_form_enable_on_demand_download"
:field="form.enable_on_demand_download">
<b-wrapped-form-checkbox
v-if="form.enable_on_demand.$model"
id="edit_form_enable_on_demand_download"
class="col-md-12"
:field="form.enable_on_demand_download"
>
<template #label>
{{ $gettext('Enable Downloads on On-Demand Page') }}
</template>

View File

@ -12,8 +12,11 @@
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_enable_requests"
:field="form.enable_requests">
<b-wrapped-form-checkbox
id="edit_form_enable_requests"
class="col-md-12"
:field="form.enable_requests"
>
<template #label>
{{ $gettext('Allow Song Requests') }}
</template>
@ -28,9 +31,13 @@
<b-form-fieldset v-if="form.enable_requests.$model">
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_request_delay"
:field="form.request_delay" input-type="number"
:input-attrs="{ min: '0', max: '1440' }">
<b-wrapped-form-group
id="edit_form_request_delay"
class="col-md-6"
:field="form.request_delay"
input-type="number"
:input-attrs="{ min: '0', max: '1440' }"
>
<template #label>
{{ $gettext('Request Minimum Delay (Minutes)') }}
</template>
@ -41,9 +48,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_request_threshold"
:field="form.request_threshold" input-type="number"
:input-attrs="{ min: '0', max: '1440' }">
<b-wrapped-form-group
id="edit_form_request_threshold"
class="col-md-6"
:field="form.request_threshold"
input-type="number"
:input-attrs="{ min: '0', max: '1440' }"
>
<template #label>
{{ $gettext('Request Last Played Threshold (Minutes)') }}
</template>
@ -57,7 +68,7 @@
</b-form-fieldset>
</b-form-fieldset>
</b-form-fieldset>
<backend-disabled v-else></backend-disabled>
<backend-disabled v-else />
</template>
<script setup>

View File

@ -7,8 +7,11 @@
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_enable_streamers"
:field="form.enable_streamers">
<b-wrapped-form-checkbox
id="edit_form_enable_streamers"
class="col-md-12"
:field="form.enable_streamers"
>
<template #label>
{{ $gettext('Allow Streamers / DJs') }}
</template>
@ -24,8 +27,11 @@
<b-form-fieldset v-if="form.enable_streamers.$model">
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_backend_record_streams"
:field="form.backend_config.record_streams">
<b-wrapped-form-checkbox
id="edit_form_backend_record_streams"
class="col-md-12"
:field="form.backend_config.record_streams"
>
<template #label>
{{ $gettext('Record Live Broadcasts') }}
</template>
@ -40,29 +46,41 @@
<b-form-fieldset v-if="form.backend_config.record_streams.$model">
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_record_streams_format"
:field="form.backend_config.record_streams_format">
<b-wrapped-form-group
id="edit_form_backend_record_streams_format"
class="col-md-6"
:field="form.backend_config.record_streams_format"
>
<template #label>
{{ $gettext('Live Broadcast Recording Format') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="recordStreamsOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="recordStreamsOptions"
/>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_record_streams_bitrate"
:field="form.backend_config.record_streams_bitrate">
<b-wrapped-form-group
id="edit_form_backend_record_streams_bitrate"
class="col-md-6"
:field="form.backend_config.record_streams_bitrate"
>
<template #label>
{{ $gettext('Live Broadcast Recording Bitrate (kbps)') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="recordBitrateOptions"
v-model="props.field.$model">
</b-form-radio-group>
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
:options="recordBitrateOptions"
/>
</template>
</b-wrapped-form-group>
</div>
@ -70,9 +88,13 @@
<b-form-fieldset>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_disconnect_deactivate_streamer"
:field="form.disconnect_deactivate_streamer" input-type="number"
:input-attrs="{ min: '0' }">
<b-wrapped-form-group
id="edit_form_disconnect_deactivate_streamer"
class="col-md-6"
:field="form.disconnect_deactivate_streamer"
input-type="number"
:input-attrs="{ min: '0' }"
>
<template #label>
{{ $gettext('Deactivate Streamer on Disconnect (Seconds)') }}
</template>
@ -83,10 +105,15 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6"
id="edit_form_backend_dj_port"
:field="form.backend_config.dj_port" input-type="number"
:input-attrs="{ min: '0' }" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_backend_dj_port"
class="col-md-6"
:field="form.backend_config.dj_port"
input-type="number"
:input-attrs="{ min: '0' }"
advanced
>
<template #label>
{{ $gettext('Customize DJ/Streamer Port') }}
</template>
@ -101,9 +128,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backend_dj_buffer"
:field="form.backend_config.dj_buffer" input-type="number"
:input-attrs="{ min: '0', max: '60' }">
<b-wrapped-form-group
id="edit_form_backend_dj_buffer"
class="col-md-6"
:field="form.backend_config.dj_buffer"
input-type="number"
:input-attrs="{ min: '0', max: '60' }"
>
<template #label>
{{ $gettext('DJ/Streamer Buffer Time (Seconds)') }}
</template>
@ -114,9 +145,13 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group v-if="showAdvanced" class="col-md-6"
id="edit_form_backend_dj_mount_point"
:field="form.backend_config.dj_mount_point" advanced>
<b-wrapped-form-group
v-if="showAdvanced"
id="edit_form_backend_dj_mount_point"
class="col-md-6"
:field="form.backend_config.dj_mount_point"
advanced
>
<template #label>
{{ $gettext('Customize DJ/Streamer Mount Point') }}
</template>
@ -131,7 +166,7 @@
</b-form-fieldset>
</b-form-fieldset>
</b-form-fieldset>
<backend-disabled v-else></backend-disabled>
<backend-disabled v-else />
</template>
<script setup>

View File

@ -1,16 +1,37 @@
<template>
<b-overlay variant="card" :show="loading">
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-overlay
variant="card"
:show="loading"
>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-form class="form vue-form" @submit.prevent="submit">
<b-tabs :card="!isModal" pills :content-class="tabContentClass">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.profileTab)" active>
<b-form
class="form vue-form"
@submit.prevent="submit"
>
<b-tabs
:card="!isModal"
pills
:content-class="tabContentClass"
>
<b-tab
:title-link-class="getTabClass(v$.$validationGroups.profileTab)"
active
>
<template #title>
{{ $gettext('Profile') }}
</template>
<admin-stations-profile-form :form="v$" :timezones="timezones"
:show-advanced="showAdvanced"></admin-stations-profile-form>
<admin-stations-profile-form
:form="v$"
:timezones="timezones"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.frontendTab)">
@ -18,10 +39,12 @@
{{ $gettext('Broadcasting') }}
</template>
<admin-stations-frontend-form :form="v$"
:is-shoutcast-installed="isShoutcastInstalled"
:countries="countries"
:show-advanced="showAdvanced"></admin-stations-frontend-form>
<admin-stations-frontend-form
:form="v$"
:is-shoutcast-installed="isShoutcastInstalled"
:countries="countries"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.backendTab)">
@ -29,9 +52,12 @@
{{ $gettext('AutoDJ') }}
</template>
<admin-stations-backend-form :form="v$" :station="station"
:is-stereo-tool-installed="isStereoToolInstalled"
:show-advanced="showAdvanced"></admin-stations-backend-form>
<admin-stations-backend-form
:form="v$"
:station="station"
:is-stereo-tool-installed="isStereoToolInstalled"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.hlsTab)">
@ -39,8 +65,11 @@
{{ $gettext('HLS') }}
</template>
<admin-stations-hls-form :form="v$" :station="station" :show-advanced="showAdvanced">
</admin-stations-hls-form>
<admin-stations-hls-form
:form="v$"
:station="station"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.requestsTab)">
@ -48,8 +77,11 @@
{{ $gettext('Song Requests') }}
</template>
<admin-stations-requests-form :form="v$" :station="station" :show-advanced="showAdvanced">
</admin-stations-requests-form>
<admin-stations-requests-form
:form="v$"
:station="station"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.streamersTab)">
@ -57,26 +89,37 @@
{{ $gettext('Streamers/DJs') }}
</template>
<admin-stations-streamers-form :form="v$" :station="station" :show-advanced="showAdvanced">
</admin-stations-streamers-form>
<admin-stations-streamers-form
:form="v$"
:station="station"
:show-advanced="showAdvanced"
/>
</b-tab>
<b-tab v-if="showAdminTab" :title-link-class="getTabClass(v$.$validationGroups.adminTab)">
<b-tab
v-if="showAdminTab"
:title-link-class="getTabClass(v$.$validationGroups.adminTab)"
>
<template #title>
{{ $gettext('Administration') }}
</template>
<admin-stations-admin-form :form="v$"
:is-edit-mode="isEditMode"
:storage-location-api-url="storageLocationApiUrl"
:show-advanced="showAdvanced">
</admin-stations-admin-form>
<admin-stations-admin-form
:form="v$"
:is-edit-mode="isEditMode"
:storage-location-api-url="storageLocationApiUrl"
:show-advanced="showAdvanced"
/>
</b-tab>
</b-tabs>
<slot name="submitButton">
<b-card-body body-class="card-padding-sm">
<b-button size="lg" type="submit" :variant="(!isValid) ? 'danger' : 'primary'">
<b-button
size="lg"
type="submit"
:variant="(!isValid) ? 'danger' : 'primary'"
>
<slot name="submitButtonText">
{{ $gettext('Save Changes') }}
</slot>

View File

@ -7,7 +7,10 @@
</div>
<div class="card-body">
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<div class="form-row">
<div class="col-md-7">
<fieldset>
@ -37,8 +40,10 @@
$gettext('Download the appropriate binary from the Stereo Tool downloads page:')
}}
<br>
<a href="https://www.thimeo.com/stereo-tool/download/"
target="_blank">
<a
href="https://www.thimeo.com/stereo-tool/download/"
target="_blank"
>
{{ $gettext('Stereo Tool Downloads') }}
</a>
</li>
@ -61,15 +66,25 @@
{{ $gettext('Current Installed Version') }}
</legend>
<p v-if="version" class="text-success card-text">
<p
v-if="version"
class="text-success card-text"
>
{{ langInstalledVersion }}
</p>
<p v-else class="text-danger card-text">
<p
v-else
class="text-danger card-text"
>
{{ $gettext('Stereo Tool is not currently installed on this installation.') }}
</p>
</fieldset>
<flow-upload :target-url="apiUrl" @complete="relist" @error="onError"></flow-upload>
<flow-upload
:target-url="apiUrl"
@complete="relist"
@error="onError"
/>
</div>
</div>
</b-overlay>

View File

@ -1,42 +1,78 @@
<template>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Storage Locations') }}</h2>
<h2 class="card-title">
{{ $gettext('Storage Locations') }}
</h2>
</b-card-header>
<b-tabs pills card lazy>
<b-tab v-for="tab in tabs" :key="tab.type" :active="activeType === tab.type" @click="setType(tab.type)"
:title="tab.title" no-body></b-tab>
<b-tabs
pills
card
lazy
>
<b-tab
v-for="tab in tabs"
:key="tab.type"
:active="activeType === tab.type"
:title="tab.title"
no-body
@click="setType(tab.type)"
/>
</b-tabs>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doCreate">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="doCreate"
>
<icon icon="add" />
{{ $gettext('Add Storage Location') }}
</b-button>
</b-card-body>
<data-table ref="datatable" id="admin_storage_locations" :show-toolbar="false" :fields="fields"
:responsive="false"
:api-url="listUrlForType">
<data-table
id="admin_storage_locations"
ref="datatable"
:show-toolbar="false"
:fields="fields"
:responsive="false"
:api-url="listUrlForType"
>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
<b-button
size="sm"
variant="primary"
@click.prevent="doEdit(row.item.links.self)"
>
{{ $gettext('Edit') }}
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
</template>
<template #cell(adapter)="row">
<h5 class="m-0">{{ getAdapterName(row.item.adapter) }}</h5>
<p class="card-text">{{ row.item.uri }}</p>
<h5 class="m-0">
{{ getAdapterName(row.item.adapter) }}
</h5>
<p class="card-text">
{{ row.item.uri }}
</p>
</template>
<template #cell(space)="row">
<template v-if="row.item.storageAvailable">
<b-progress :value="row.item.storageUsedPercent" show-progress height="15px" class="mb-1"
:variant="getProgressVariant(row.item.storageUsedPercent)">
</b-progress>
<b-progress
:value="row.item.storageUsedPercent"
show-progress
height="15px"
class="mb-1"
:variant="getProgressVariant(row.item.storageUsedPercent)"
/>
{{ getSpaceUsed(row.item) }}
</template>
@ -50,7 +86,12 @@
</data-table>
</b-card>
<edit-modal ref="editModal" :create-url="listUrl" :type="activeType" @relist="relist"></edit-modal>
<edit-modal
ref="editModal"
:create-url="listUrl"
:type="activeType"
@relist="relist"
/>
</template>
<script>

View File

@ -1,9 +1,14 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<storage-location-form :form="v$.form"></storage-location-form>
<modal-form
ref="modal"
:loading="loading"
:title="langTitle"
:error="error"
:disable-save-button="v$.form.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<storage-location-form :form="v$.form" />
</modal-form>
</template>

View File

@ -1,12 +1,20 @@
<template>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="form_edit_adapter" :field="form.adapter">
<b-wrapped-form-group
id="form_edit_adapter"
class="col-md-12"
:field="form.adapter"
>
<template #label>
{{ $gettext('Storage Adapter') }}
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" v-model="props.field.$model">
<b-form-radio-group
:id="props.id"
v-model="props.field.$model"
stacked
>
<b-form-radio value="local">
{{ $gettext('Local Filesystem') }}
</b-form-radio>
@ -23,7 +31,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_edit_path" :field="form.path">
<b-wrapped-form-group
id="form_edit_path"
class="col-md-12"
:field="form.path"
>
<template #label>
{{ $gettext('Path/Suffix') }}
</template>
@ -34,7 +46,11 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_edit_storageQuota" :field="form.storageQuota">
<b-wrapped-form-group
id="form_edit_storageQuota"
class="col-md-12"
:field="form.storageQuota"
>
<template #label>
{{ $gettext('Storage Quota') }}
</template>
@ -47,7 +63,11 @@
</div>
</b-form-group>
<b-card v-show="form.adapter.$model === 's3'" class="mb-3" no-body>
<b-card
v-show="form.adapter.$model === 's3'"
class="mb-3"
no-body
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Remote: S3 Compatible') }}
@ -56,39 +76,61 @@
<b-card-body>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="form_edit_s3CredentialKey"
:field="form.s3CredentialKey">
<b-wrapped-form-group
id="form_edit_s3CredentialKey"
class="col-md-6"
:field="form.s3CredentialKey"
>
<template #label>
{{ $gettext('Access Key ID') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_s3CredentialSecret"
:field="form.s3CredentialSecret">
<b-wrapped-form-group
id="form_edit_s3CredentialSecret"
class="col-md-6"
:field="form.s3CredentialSecret"
>
<template #label>
{{ $gettext('Secret Key') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_s3Endpoint" :field="form.s3Endpoint">
<b-wrapped-form-group
id="form_edit_s3Endpoint"
class="col-md-6"
:field="form.s3Endpoint"
>
<template #label>
{{ $gettext('Endpoint') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_s3Bucket" :field="form.s3Bucket">
<b-wrapped-form-group
id="form_edit_s3Bucket"
class="col-md-6"
:field="form.s3Bucket"
>
<template #label>
{{ $gettext('Bucket Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_s3Region" :field="form.s3Region">
<b-wrapped-form-group
id="form_edit_s3Region"
class="col-md-6"
:field="form.s3Region"
>
<template #label>
{{ $gettext('Region') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_s3Version" :field="form.s3Version">
<b-wrapped-form-group
id="form_edit_s3Version"
class="col-md-6"
:field="form.s3Version"
>
<template #label>
{{ $gettext('API Version') }}
</template>
@ -98,7 +140,11 @@
</b-card-body>
</b-card>
<b-card v-show="form.adapter.$model === 'dropbox'" class="mb-3" no-body>
<b-card
v-show="form.adapter.$model === 'dropbox'"
class="mb-3"
no-body
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Remote: Dropbox') }}
@ -107,8 +153,11 @@
<b-card-body>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-12" id="form_edit_dropboxAuthToken"
:field="form.dropboxAuthToken">
<b-wrapped-form-group
id="form_edit_dropboxAuthToken"
class="col-md-12"
:field="form.dropboxAuthToken"
>
<template #label>
{{ $gettext('Dropbox Generated Access Token') }}
</template>
@ -123,7 +172,11 @@
</b-card-body>
</b-card>
<b-card v-show="form.adapter.$model === 'sftp'" class="mb-3" no-body>
<b-card
v-show="form.adapter.$model === 'sftp'"
class="mb-3"
no-body
>
<div class="card-header bg-primary-dark">
<h2 class="card-title">
{{ $gettext('Remote: SFTP') }}
@ -132,44 +185,65 @@
<b-card-body>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-12 col-lg-6" id="form_edit_sftpHost"
:field="form.sftpHost">
<b-wrapped-form-group
id="form_edit_sftpHost"
class="col-md-12 col-lg-6"
:field="form.sftpHost"
>
<template #label>
{{ $gettext('SFTP Host') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12 col-lg-6" id="form_edit_sftpPort" input-type="number" min="1"
step="1"
:field="form.sftpPort">
<b-wrapped-form-group
id="form_edit_sftpPort"
class="col-md-12 col-lg-6"
input-type="number"
min="1"
step="1"
:field="form.sftpPort"
>
<template #label>
{{ $gettext('SFTP Port') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12 col-lg-6" id="form_edit_sftpUsername"
:field="form.sftpUsername">
<b-wrapped-form-group
id="form_edit_sftpUsername"
class="col-md-12 col-lg-6"
:field="form.sftpUsername"
>
<template #label>
{{ $gettext('SFTP Username') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12 col-lg-6" id="form_edit_sftpPassword"
:field="form.sftpPassword">
<b-wrapped-form-group
id="form_edit_sftpPassword"
class="col-md-12 col-lg-6"
:field="form.sftpPassword"
>
<template #label>
{{ $gettext('SFTP Password') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_edit_sftpPrivateKeyPassPhrase"
:field="form.sftpPrivateKeyPassPhrase">
<b-wrapped-form-group
id="form_edit_sftpPrivateKeyPassPhrase"
class="col-md-12"
:field="form.sftpPrivateKeyPassPhrase"
>
<template #label>
{{ $gettext('SFTP Private Key Pass Phrase') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_edit_sftpPrivateKey" input-type="textarea"
:field="form.sftpPrivateKey">
<b-wrapped-form-group
id="form_edit_sftpPrivateKey"
class="col-md-12"
input-type="textarea"
:field="form.sftpPrivateKey"
>
<template #label>
{{ $gettext('SFTP Private Key') }}
</template>

View File

@ -1,38 +1,76 @@
<template>
<b-card no-body>
<b-card-header header-bg-variant="primary-dark">
<h2 class="card-title">{{ $gettext('Users') }}</h2>
<h2 class="card-title">
{{ $gettext('Users') }}
</h2>
</b-card-header>
<b-card-body body-class="card-padding-sm">
<b-button variant="outline-primary" @click.prevent="doCreate">
<icon icon="add"></icon>
<b-button
variant="outline-primary"
@click.prevent="doCreate"
>
<icon icon="add" />
{{ $gettext('Add User') }}
</b-button>
</b-card-body>
<data-table ref="datatable" id="users" paginated :fields="fields" :api-url="listUrl">
<data-table
id="users"
ref="datatable"
paginated
:fields="fields"
:api-url="listUrl"
>
<template #cell(name)="row">
<h5 class="mb-0" v-if="row.item.name !== ''">{{ row.item.name }}</h5>
<h5
v-if="row.item.name !== ''"
class="mb-0"
>
{{ row.item.name }}
</h5>
<a :href="'mailto:'+row.item.email">{{ row.item.email }}</a>
<span v-if="row.item.is_me" class="badge badge-primary">
<span
v-if="row.item.is_me"
class="badge badge-primary"
>
{{ $gettext('You') }}
</span>
</template>
<template #cell(roles)="row">
<div v-for="role in row.item.roles" :key="role.id">
<div
v-for="role in row.item.roles"
:key="role.id"
>
{{ role.name }}
</div>
</template>
<template #cell(actions)="row">
<b-button-group size="sm" v-if="!row.item.is_me">
<b-button size="sm" variant="secondary" :href="row.item.links.masquerade" target="_blank">
<b-button-group
v-if="!row.item.is_me"
size="sm"
>
<b-button
size="sm"
variant="secondary"
:href="row.item.links.masquerade"
target="_blank"
>
{{ $gettext('Log In') }}
</b-button>
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
<b-button
size="sm"
variant="primary"
@click.prevent="doEdit(row.item.links.self)"
>
{{ $gettext('Edit') }}
</b-button>
<b-button size="sm" variant="danger" @click.prevent="doDelete(row.item.links.self)">
<b-button
size="sm"
variant="danger"
@click.prevent="doDelete(row.item.links.self)"
>
{{ $gettext('Delete') }}
</b-button>
</b-button-group>
@ -40,7 +78,12 @@
</data-table>
</b-card>
<edit-modal ref="editModal" :create-url="listUrl" :roles="roles" @relist="relist"></edit-modal>
<edit-modal
ref="editModal"
:create-url="listUrl"
:roles="roles"
@relist="relist"
/>
</template>
<script>

View File

@ -1,10 +1,18 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error"
:disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-users-form :form="v$.form" :roles="roles" :is-edit-mode="isEditMode"></admin-users-form>
<modal-form
ref="modal"
:loading="loading"
:title="langTitle"
:error="error"
:disable-save-button="v$.form.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
<admin-users-form
:form="v$.form"
:roles="roles"
:is-edit-mode="isEditMode"
/>
</modal-form>
</template>
@ -19,13 +27,13 @@ import validatePassword from "~/functions/validatePassword";
export default {
name: 'AdminUsersEditModal',
components: {AdminUsersForm},
setup() {
return {v$: useVuelidate()}
},
mixins: [BaseEditModal],
props: {
roles: Object
},
setup() {
return {v$: useVuelidate()}
},
computed: {
langTitle() {
return this.isEditMode

View File

@ -1,37 +1,59 @@
<template>
<b-form-group>
<div class="form-row">
<b-wrapped-form-group class="col-md-6" id="edit_form_email" :field="form.email" input-type="email">
<b-wrapped-form-group
id="edit_form_email"
class="col-md-6"
:field="form.email"
input-type="email"
>
<template #label>
{{ $gettext('E-mail Address') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_new_password" :field="form.new_password"
input-type="password">
<b-wrapped-form-group
id="edit_form_new_password"
class="col-md-6"
:field="form.new_password"
input-type="password"
>
<template #label>
{{ $gettext('Reset Password') }}
{{ $gettext('Password') }}
</template>
<template v-if="isEditMode" #description="{lang}">
<template
v-if="isEditMode"
#description="{lang}"
>
{{ $gettext('Leave blank to use the current password.') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_name" :field="form.name">
<b-wrapped-form-group
id="edit_form_name"
class="col-md-12"
:field="form.name"
>
<template #label>
{{ $gettext('Display Name') }}
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_roles"
:field="form.roles">
<b-wrapped-form-group
id="edit_form_roles"
class="col-md-12"
:field="form.roles"
>
<template #label>
{{ $gettext('Roles') }}
</template>
<template #default="props">
<b-form-checkbox-group :id="props.id" :options="roleOptions" v-model="props.field.$model">
</b-form-checkbox-group>
<b-form-checkbox-group
:id="props.id"
v-model="props.field.$model"
:options="roleOptions"
/>
</template>
</b-wrapped-form-group>
</div>

View File

@ -1,17 +1,20 @@
<template>
<a v-if="src" :href="src" class="album-art" target="_blank" data-fancybox="gallery">
<img class="album_art" :src="src" loading="lazy" alt="">
<a
v-if="src"
:href="src"
class="album-art"
target="_blank"
data-fancybox="gallery"
>
<img
class="album_art"
:src="src"
loading="lazy"
alt=""
>
</a>
</template>
<style scoped>
img.album_art {
width: v-bind(widthPx);
height: auto;
border-radius: 5px;
}
</style>
<script setup>
import {computed} from "vue";
@ -27,3 +30,11 @@ const widthPx = computed(() => {
return props.width + 'px';
});
</script>
<style scoped>
img.album_art {
width: v-bind(widthPx);
height: auto;
border-radius: 5px;
}
</style>

View File

@ -1,5 +1,9 @@
<template>
<audio ref="audio" v-if="isPlaying" v-bind:title="title"/>
<audio
v-if="isPlaying"
ref="audio"
:title="title"
/>
</template>
<script>

View File

@ -1,7 +1,17 @@
<template>
<a :href="serviceUrl" class="avatar" target="_blank" v-b-tooltip.hover.right :title="langAvatar"
v-if="'' !== serviceUrl">
<img :src="url" :style="{ width: this.width+'px', height: 'auto' }" alt="">
<a
v-if="'' !== serviceUrl"
v-b-tooltip.hover.right
:href="serviceUrl"
class="avatar"
target="_blank"
:title="langAvatar"
>
<img
:src="url"
:style="{ width: width+'px', height: 'auto' }"
alt=""
>
</a>
</template>

View File

@ -1,5 +1,8 @@
<template>
<small class="badge badge-pill ml-2" :class="badgeClass">{{ badgeText }}</small>
<small
class="badge badge-pill ml-2"
:class="badgeClass"
>{{ badgeText }}</small>
</template>
<script setup>

View File

@ -1,5 +1,8 @@
<template>
<small class="badge badge-pill ml-2" :class="badgeClass">{{ badgeText }}</small>
<small
class="badge badge-pill ml-2"
:class="badgeClass"
>{{ badgeText }}</small>
</template>
<script setup>

View File

@ -1,5 +1,5 @@
<template>
<b-modal ref="modal"></b-modal>
<b-modal ref="modal" />
</template>
<script>
@ -10,13 +10,13 @@ import mergeExisting from "~/functions/mergeExisting";
export default {
name: 'BaseEditModal',
components: {ModalForm},
setup() {
return {v$: useVuelidate()}
},
emits: ['relist'],
props: {
createUrl: String
},
emits: ['relist'],
setup() {
return {v$: useVuelidate()}
},
data() {
return {
loading: true,

View File

@ -1,6 +1,6 @@
<template>
<canvas ref="$canvas">
<slot></slot>
<slot />
</canvas>
</template>

View File

@ -1,6 +1,6 @@
<template>
<canvas ref="$canvas">
<slot></slot>
<slot />
</canvas>
</template>

View File

@ -1,6 +1,6 @@
<template>
<canvas ref="$canvas">
<slot></slot>
<slot />
</canvas>
</template>

View File

@ -1,5 +1,10 @@
<template>
<code-mirror basic v-model="textValue" :lang="lang" :dark="dark"></code-mirror>
<code-mirror
v-model="textValue"
basic
:lang="lang"
:dark="dark"
/>
</template>
<script setup>

View File

@ -1,7 +1,14 @@
<template>
<button ref="btn" class="btn btn-copy btn-link btn-xs" @click.prevent="doCopy"
:aria-label="$gettext('Copy to Clipboard')">
<icon class="sm" icon="file_copy"></icon>
<button
ref="btn"
class="btn btn-copy btn-link btn-xs"
:aria-label="$gettext('Copy to Clipboard')"
@click.prevent="doCopy"
>
<icon
class="sm"
icon="file_copy"
/>
<span v-if="!hideText">{{ $gettext('Copy to Clipboard') }}</span>
</button>
</template>

View File

@ -1,52 +1,113 @@
<template>
<div :id="id" style="display: contents">
<div class="datatable-toolbar-top card-body" v-if="showToolbar">
<div
:id="id"
style="display: contents"
>
<div
v-if="showToolbar"
class="datatable-toolbar-top card-body"
>
<b-row class="align-items-center mb-2">
<b-col xl="6" lg="5" md="12" sm="12" v-if="showPagination">
<b-pagination v-model="currentPage" :total-rows="totalRows" :per-page="perPage"
class="mb-0" v-if="showPagination">
</b-pagination>
<b-col
v-if="showPagination"
xl="6"
lg="5"
md="12"
sm="12"
>
<b-pagination
v-if="showPagination"
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
class="mb-0"
/>
</b-col>
<b-col xl="6" lg="5" md="12" sm="12" v-else>
<b-col
v-else
xl="6"
lg="5"
md="12"
sm="12"
>
&nbsp;
</b-col>
<b-col xl="6" lg="7" md="12" sm="12" class="d-flex my-2">
<b-col
xl="6"
lg="7"
md="12"
sm="12"
class="d-flex my-2"
>
<div class="flex-fill">
<div class="input-group">
<div class="input-group-prepend text-muted">
<icon icon="search"></icon>
<icon icon="search" />
</div>
<b-form-input debounce="200" v-model="filter" type="search"
class="search-field form-control"
:placeholder="langSearch"></b-form-input>
<b-form-input
v-model="filter"
debounce="200"
type="search"
class="search-field form-control"
:placeholder="langSearch"
/>
</div>
</div>
<div class="flex-shrink-1 pl-3 pr-3">
<b-btn-group class="actions">
<b-button variant="default" title="Refresh" @click="onClickRefresh" v-b-tooltip.hover
:title="langRefreshTooltip">
<icon icon="refresh"></icon>
<b-button
v-b-tooltip.hover
variant="default"
title="Refresh"
:title="langRefreshTooltip"
@click="onClickRefresh"
>
<icon icon="refresh" />
</b-button>
<b-dropdown variant="default" :text="perPageLabel" v-b-tooltip.hover v-if="paginated"
:title="langPerPageTooltip">
<b-dropdown-item v-for="pageOption in pageOptions" :key="pageOption"
:active="(pageOption === perPage)" @click="setPerPage(pageOption)">
<b-dropdown
v-if="paginated"
v-b-tooltip.hover
variant="default"
:text="perPageLabel"
:title="langPerPageTooltip"
>
<b-dropdown-item
v-for="pageOption in pageOptions"
:key="pageOption"
:active="(pageOption === perPage)"
@click="setPerPage(pageOption)"
>
{{ getPerPageLabel(pageOption) }}
</b-dropdown-item>
</b-dropdown>
<b-dropdown variant="default" v-if="selectFields" v-b-tooltip.hover
:title="langSelectFieldsTooltip">
<b-dropdown
v-if="selectFields"
v-b-tooltip.hover
variant="default"
:title="langSelectFieldsTooltip"
>
<template #button-content>
<icon icon="filter_list"></icon>
<span class="caret"></span>
<icon icon="filter_list" />
<span class="caret" />
</template>
<b-dropdown-form class="pt-3">
<div v-for="field in selectableFields" class="form-group">
<div
v-for="field in selectableFields"
class="form-group"
>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input"
v-bind:id="'chk_field_' + field.key" name="is_field_visible"
v-model="field.visible" @change="storeSettings">
<label class="custom-control-label" v-bind:for="'chk_field_'+field.key">
<input
:id="'chk_field_' + field.key"
v-model="field.visible"
type="checkbox"
class="custom-control-input"
name="is_field_visible"
@change="storeSettings"
>
<label
class="custom-control-label"
:for="'chk_field_'+field.key"
>
{{ field.label }}
</label>
</div>
@ -59,43 +120,68 @@
</b-row>
</div>
<div class="datatable-main">
<b-table ref="table" show-empty striped hover :selectable="selectable" :api-url="apiUrl" :per-page="perPage"
v-model:current-page="currentPage" @row-selected="onRowSelected" :items="itemProvider"
:fields="visibleFields"
:empty-text="langNoRecords" :empty-filtered-text="langNoRecords" :responsive="responsive"
:no-provider-paging="handleClientSide" :no-provider-sorting="handleClientSide"
:no-provider-filtering="handleClientSide"
tbody-tr-class="align-middle" thead-tr-class="align-middle" selected-variant=""
:filter="filter" @filtered="onFiltered" @refreshed="onRefreshed"
v-model:sort-by="sortBy" v-model:sort-desc="sortDesc" @sort-changed="onSortChanged">
<b-table
ref="table"
v-model:current-page="currentPage"
show-empty
striped
hover
:selectable="selectable"
:api-url="apiUrl"
:per-page="perPage"
:items="itemProvider"
:fields="visibleFields"
:empty-text="langNoRecords"
:empty-filtered-text="langNoRecords"
:responsive="responsive"
:no-provider-paging="handleClientSide"
:no-provider-sorting="handleClientSide"
:no-provider-filtering="handleClientSide"
tbody-tr-class="align-middle"
@row-selected="onRowSelected"
thead-tr-class="align-middle"
selected-variant=""
:filter="filter"
v-model:sort-by="sortBy"
v-model:sort-desc="sortDesc"
@filtered="onFiltered"
@refreshed="onRefreshed"
@sort-changed="onSortChanged"
>
<template #head(selected)="data">
<b-form-checkbox :aria-label="langSelectAll" :checked="allSelected"
@change="toggleSelected"></b-form-checkbox>
<b-form-checkbox
:aria-label="langSelectAll"
:checked="allSelected"
@change="toggleSelected"
/>
</template>
<template #cell(selected)="{ rowSelected }">
<div class="text-muted">
<template v-if="rowSelected">
<span class="sr-only">{{ langDeselectRow }}</span>
<icon icon="check_box"></icon>
<icon icon="check_box" />
</template>
<template v-else>
<span class="sr-only">{{ langSelectRow }}</span>
<icon icon="check_box_outline_blank"></icon>
<icon icon="check_box_outline_blank" />
</template>
</div>
</template>
<template #table-busy>
<div role="alert" aria-live="polite">
<div
role="alert"
aria-live="polite"
>
<div class="text-center my-2">
<div class="progress-circular progress-circular-primary mx-auto mb-3">
<div class="progress-circular-wrapper">
<div class="progress-circular-inner">
<div class="progress-circular-left">
<div class="progress-circular-spinner"></div>
<div class="progress-circular-spinner" />
</div>
<div class="progress-circular-gap"></div>
<div class="progress-circular-gap" />
<div class="progress-circular-right">
<div class="progress-circular-spinner"></div>
<div class="progress-circular-spinner" />
</div>
</div>
</div>
@ -106,53 +192,29 @@
</div>
</template>
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of $slots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</b-table>
</div>
<div class="datatable-toolbar-bottom card-body">
<b-pagination v-model="currentPage" :total-rows="totalRows" :per-page="perPage"
class="mb-0 mt-2" v-if="showPagination">
</b-pagination>
<b-pagination
v-if="showPagination"
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
class="mb-0 mt-2"
/>
</div>
</div>
</template>
<style lang="scss">
div.datatable-main {
flex: 1;
}
div.datatable-toolbar-top,
div.datatable-toolbar-bottom {
flex: 0;
padding: 0;
}
table.b-table {
td.shrink {
width: 0.1%;
white-space: nowrap;
}
}
table.b-table-selectable {
thead tr th:nth-child(1),
tbody tr td:nth-child(1),
tbody tr th:nth-child(1) {
padding-right: 0.75rem;
width: 3rem;
}
thead tr th:nth-child(2),
tbody tr td:nth-child(2),
tbody tr th:nth-child(2) {
padding-left: 0.5rem;
}
}
</style>
<script>
import store from 'store';
import _ from 'lodash';
@ -237,9 +299,6 @@ export default defineComponent({
flushCache: false
};
},
created() {
this.loadStoredSettings();
},
computed: {
langRefreshTooltip() {
return this.$gettext('Refresh rows');
@ -325,6 +384,9 @@ export default defineComponent({
this.currentPage = 1;
}
},
created() {
this.loadStoredSettings();
},
methods: {
loadStoredSettings() {
if (store.enabled && store.get(this.storeKey) !== undefined) {
@ -463,3 +525,37 @@ export default defineComponent({
}
});
</script>
<style lang="scss">
div.datatable-main {
flex: 1;
}
div.datatable-toolbar-top,
div.datatable-toolbar-bottom {
flex: 0;
padding: 0;
}
table.b-table {
td.shrink {
width: 0.1%;
white-space: nowrap;
}
}
table.b-table-selectable {
thead tr th:nth-child(1),
tbody tr td:nth-child(1),
tbody tr th:nth-child(1) {
padding-right: 0.75rem;
width: 3rem;
}
thead tr th:nth-child(2),
tbody tr td:nth-child(2),
tbody tr th:nth-child(2) {
padding-left: 0.5rem;
}
}
</style>

View File

@ -1,25 +1,40 @@
<template>
<date-range-picker
v-bind="$props" ref="picker" controlContainerClass="" opens="left" show-dropdowns
:time-picker-increment="1" :ranges="ranges" v-model="dateRange" v-model:date-range="dateRange"
@select="onSelect">
v-bind="$props"
ref="picker"
v-model="dateRange"
v-model:date-range="dateRange"
control-container-class=""
opens="left"
show-dropdowns
:time-picker-increment="1"
:ranges="ranges"
@select="onSelect"
>
<template #input="datePicker">
<a class="btn btn-bg dropdown-toggle" id="reportrange" href="#" @click.prevent="">
<icon icon="date_range"></icon>
<a
id="reportrange"
class="btn btn-bg dropdown-toggle"
href="#"
@click.prevent=""
>
<icon icon="date_range" />
{{ datePicker.rangeText }}
</a>
</template>
<template v-for="(_, slot) of $slots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of $slots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</date-range-picker>
</template>
<style lang="scss">
@import 'vue3-daterange-picker/src/assets/daterangepicker';
</style>
<script>
import DateRangePicker from 'vue3-daterange-picker';
import Icon from "./Icon";
@ -28,7 +43,6 @@ import {DateTime} from 'luxon';
export default {
name: 'DateRangeDropdown',
components: {DateRangePicker, Icon},
emits: ['update:modelValue', 'update'],
inheritAttrs: false,
model: {
prop: 'modelValue',
@ -64,6 +78,7 @@ export default {
default: null,
},
},
emits: ['update:modelValue', 'update'],
computed: {
dateRange: {
get() {
@ -127,3 +142,7 @@ export default {
}
}
</script>
<style lang="scss">
@import 'vue3-daterange-picker/src/assets/daterangepicker';
</style>

View File

@ -1,72 +1,69 @@
<template>
<div class="flow-upload">
<div class="upload-progress">
<template v-for="(file, key) in files.value" :key="key">
<div v-if="file.isVisible" class="uploading-file pt-1" :id="'file_upload_' + file.uniqueIdentifier"
:class="{ 'text-success': file.isCompleted, 'text-danger': file.error }">
<h6 class="fileuploadname m-0">{{ file.name }}</h6>
<div v-if="!file.isCompleted" class="progress h-15 my-1">
<div class="progress-bar h-15" role="progressbar" :style="{width: file.progressPercent+'%'}"
:aria-valuenow="file.progressPercent" aria-valuemin="0" aria-valuemax="100"></div>
<template
v-for="(file, key) in files.value"
:key="key"
>
<div
v-if="file.isVisible"
:id="'file_upload_' + file.uniqueIdentifier"
class="uploading-file pt-1"
:class="{ 'text-success': file.isCompleted, 'text-danger': file.error }"
>
<h6 class="fileuploadname m-0">
{{ file.name }}
</h6>
<div
v-if="!file.isCompleted"
class="progress h-15 my-1"
>
<div
class="progress-bar h-15"
role="progressbar"
:style="{width: file.progressPercent+'%'}"
:aria-valuenow="file.progressPercent"
aria-valuemin="0"
aria-valuemax="100"
/>
</div>
<div class="upload-status" v-if="file.error">
<div
v-if="file.error"
class="upload-status"
>
{{ file.error }}
</div>
<div class="size">{{ file.size }}</div>
<div class="size">
{{ file.size }}
</div>
</div>
</template>
</div>
<div class="file-drop-target" ref="$fileDropTarget">
<div
ref="$fileDropTarget"
class="file-drop-target"
>
{{ $gettext('Drag file(s) here to upload or') }}
<button ref="$fileBrowseTarget" class="file-upload btn btn-primary text-center ml-1" type="button">
<icon icon="cloud_upload"></icon>
<button
ref="$fileBrowseTarget"
class="file-upload btn btn-primary text-center ml-1"
type="button"
>
<icon icon="cloud_upload" />
{{ $gettext('Select File') }}
</button>
<small class="file-name"></small>
<input type="file" :accept="validMimeTypesList" :multiple="allowMultiple"
style="visibility: hidden; position: absolute;"/>
<small class="file-name" />
<input
type="file"
:accept="validMimeTypesList"
:multiple="allowMultiple"
style="visibility: hidden; position: absolute;"
>
</div>
</div>
</template>
<style lang="scss">
div.flow-upload {
div.upload-progress {
padding: 4px 0;
& > div {
padding: 3px 0;
}
.error {
color: #a00;
}
.progress {
margin-bottom: 5px;
.progress-bar {
border-bottom-width: 10px;
&::after {
height: 10px;
}
}
}
}
div.file-drop-target {
padding: 25px 0;
text-align: center;
input {
display: inline;
}
}
}
</style>
<script setup>
import formatFileSize from '~/functions/formatFileSize.js';
import Icon from './Icon.vue';
@ -216,3 +213,40 @@ onUnmounted(() => {
files.reset();
});
</script>
<style lang="scss">
div.flow-upload {
div.upload-progress {
padding: 4px 0;
& > div {
padding: 3px 0;
}
.error {
color: #a00;
}
.progress {
margin-bottom: 5px;
.progress-bar {
border-bottom-width: 10px;
&::after {
height: 10px;
}
}
}
}
div.file-drop-target {
padding: 25px 0;
text-align: center;
input {
display: inline;
}
}
}
</style>

View File

@ -1,5 +1,8 @@
<template>
<i :class="iconClass" aria-hidden="true">{{ icon }}</i>
<i
:class="iconClass"
aria-hidden="true"
>{{ icon }}</i>
</template>
<script setup>

View File

@ -1,10 +1,13 @@
<template>
<div class="card-body alert-info d-flex align-items-center" role="alert">
<div
class="card-body alert-info d-flex align-items-center"
role="alert"
>
<div class="flex-shrink-0 mr-2">
<icon icon="info"/>
<icon icon="info" />
</div>
<div class="flex-fill">
<slot></slot>
<slot />
</div>
</div>
</template>

View File

@ -1,3 +1,8 @@
<template>
<input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;" tabindex="-1" aria-hidden="true"/>
<input
type="submit"
style="position: absolute; left: -9999px; width: 1px; height: 1px;"
tabindex="-1"
aria-hidden="true"
>
</template>

View File

@ -1,7 +1,12 @@
<template>
<div class="list-group list-group-flush">
<a v-for="log in logs" :key="log.key" class="list-group-item list-group-item-action log-item"
href="#" @click.prevent="viewLog(log.links.self)">
<a
v-for="log in logs"
:key="log.key"
class="list-group-item list-group-item-action log-item"
href="#"
@click.prevent="viewLog(log.links.self)"
>
<span class="log-name">{{ log.name }}</span><br>
<small class="text-secondary">{{ log.path }}</small>
</a>
@ -11,10 +16,10 @@
<script>
export default {
name: 'LogList',
emits: ['view'],
props: {
url: String,
},
emits: ['view'],
data() {
return {
loading: true,

View File

@ -1,25 +1,58 @@
<template>
<b-modal :size="size" :centered="centered" :id="id" ref="modal" :title="title" :busy="loading" @shown="onShown"
@hidden="onHidden" :no-enforce-focus="noEnforceFocus">
<b-modal
:id="id"
ref="modal"
:size="size"
:centered="centered"
:title="title"
:busy="loading"
:no-enforce-focus="noEnforceFocus"
@shown="onShown"
@hidden="onHidden"
>
<template #default="slotProps">
<b-overlay variant="card" :show="loading">
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-overlay
variant="card"
:show="loading"
>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<b-form class="form vue-form" @submit.prevent="doSubmit">
<slot name="default" v-bind="slotProps">
</slot>
<b-form
class="form vue-form"
@submit.prevent="doSubmit"
>
<slot
name="default"
v-bind="slotProps"
/>
<invisible-submit-button/>
<invisible-submit-button />
</b-form>
</b-overlay>
</template>
<template #modal-footer="slotProps">
<slot name="modal-footer" v-bind="slotProps">
<b-button variant="default" type="button" @click="close">
<slot
name="modal-footer"
v-bind="slotProps"
>
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button :variant="(disableSaveButton) ? 'danger' : 'primary'" type="submit" @click="doSubmit">
<b-button
:variant="(disableSaveButton) ? 'danger' : 'primary'"
type="submit"
@click="doSubmit"
>
<slot name="save-button-name">
{{ $gettext('Save Changes') }}
</slot>
@ -27,8 +60,14 @@
</slot>
</template>
<template v-for="(_, slot) of filteredScopedSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of filteredScopedSlots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</b-modal>
</template>
@ -40,7 +79,6 @@ import _ from "lodash";
export default defineComponent({
components: {InvisibleSubmitButton},
emits: ['submit', 'shown', 'hidden'],
props: {
title: {
type: String,
@ -74,6 +112,7 @@ export default defineComponent({
type: String
}
},
emits: ['submit', 'shown', 'hidden'],
computed: {
filteredScopedSlots() {
return _.filter(this.$slots, (slot, name) => {

View File

@ -1,6 +1,13 @@
<template>
<a href="#" @click.prevent="toggle" :title="langTitle">
<icon :class="iconClass" :icon="iconText"></icon>
<a
href="#"
:title="langTitle"
@click.prevent="toggle"
>
<icon
:class="iconClass"
:icon="iconText"
/>
</a>
</template>

View File

@ -1,5 +1,8 @@
<template>
<full-calendar ref="calendar" :options="calendarOptions"></full-calendar>
<full-calendar
ref="calendar"
:options="calendarOptions"
/>
</template>
<script setup>

View File

@ -1,13 +1,31 @@
<template>
<b-modal id="logs_modal" size="lg" ref="$modal" @hidden="clearContents"
:title="$gettext('Log Viewer')" no-enforce-focus>
<streaming-log-view ref="$logView" :log-url="logUrl"></streaming-log-view>
<b-modal
id="logs_modal"
ref="$modal"
size="lg"
:title="$gettext('Log Viewer')"
no-enforce-focus
@hidden="clearContents"
>
<streaming-log-view
ref="$logView"
:log-url="logUrl"
/>
<template #modal-footer>
<b-button variant="default" type="button" @click="close">
<b-button
variant="default"
type="button"
@click="close"
>
{{ $gettext('Close') }}
</b-button>
<b-button variant="primary" class="btn_copy" @click.prevent="doCopy" type="button">
<b-button
variant="primary"
class="btn_copy"
type="button"
@click.prevent="doCopy"
>
{{ $gettext('Copy to Clipboard') }}
</b-button>
</template>

View File

@ -1,13 +1,24 @@
<template>
<b-overlay variant="card" :show="loading">
<b-overlay
variant="card"
:show="loading"
>
<b-form-group label-for="modal_scroll_to_bottom">
<b-form-checkbox id="modal_scroll_to_bottom" v-model="scrollToBottom">
<b-form-checkbox
id="modal_scroll_to_bottom"
v-model="scrollToBottom"
>
{{ $gettext('Automatically Scroll to Bottom') }}
</b-form-checkbox>
</b-form-group>
<textarea class="form-control log-viewer" ref="textarea" id="log-view-contents" spellcheck="false"
readonly>{{ logs }}</textarea>
<textarea
id="log-view-contents"
ref="textarea"
class="form-control log-viewer"
spellcheck="false"
readonly
>{{ logs }}</textarea>
</b-overlay>
</template>
@ -56,7 +67,7 @@ export default {
this.loading = false;
});
},
beforeDestroy() {
beforeUnmount() {
clearTimeout(this.timeoutUpdateLog);
},
methods: {

View File

@ -1,5 +1,11 @@
<template>
<b-input v-bind="$attrs" type="time" v-model="timeCode" pattern="[0-9]{2}:[0-9]{2}" placeholder="13:45"></b-input>
<b-input
v-bind="$attrs"
v-model="timeCode"
type="time"
pattern="[0-9]{2}:[0-9]{2}"
placeholder="13:45"
/>
</template>
<script setup>

View File

@ -3,8 +3,8 @@
<b-row>
<b-form-group class="col-md-12">
<div class="waveform__container">
<div id="waveform-timeline"></div>
<div id="waveform"></div>
<div id="waveform-timeline" />
<div id="waveform" />
</div>
</b-form-group>
</b-row>
@ -17,27 +17,48 @@
</label>
</div>
<div class="flex-fill mx-3">
<b-form-input id="waveform-zoom" v-model.number="zoom" type="range" min="0" max="256"
class="w-100"></b-form-input>
<b-form-input
id="waveform-zoom"
v-model.number="zoom"
type="range"
min="0"
max="256"
class="w-100"
/>
</div>
</div>
</b-col>
<b-col md="5">
<div class="inline-volume-controls d-flex align-items-center">
<div class="flex-shrink-0">
<a class="btn btn-sm btn-outline-inverse" href="#" @click.prevent="volume = 0"
:title="$gettext('Mute')">
<icon icon="volume_mute"></icon>
<a
class="btn btn-sm btn-outline-inverse"
href="#"
:title="$gettext('Mute')"
@click.prevent="volume = 0"
>
<icon icon="volume_mute" />
</a>
</div>
<div class="flex-fill mx-1">
<input type="range" :title="$gettext('Volume')" class="player-volume-range custom-range w-100"
min="0" max="100" step="1" v-model.number="volume">
<input
v-model.number="volume"
type="range"
:title="$gettext('Volume')"
class="player-volume-range custom-range w-100"
min="0"
max="100"
step="1"
>
</div>
<div class="flex-shrink-0">
<a class="btn btn-sm btn-outline-inverse" href="#" @click.prevent="volume = 100"
:title="$gettext('Full Volume')">
<icon icon="volume_up"></icon>
<a
class="btn btn-sm btn-outline-inverse"
href="#"
:title="$gettext('Full Volume')"
@click.prevent="volume = 100"
>
<icon icon="volume_up" />
</a>
</div>
</div>

View File

@ -1,42 +1,90 @@
<template>
<div id="dashboard">
<section class="card mb-4" role="region">
<section
class="card mb-4"
role="region"
>
<div class="card-header bg-primary-dark d-flex flex-wrap align-items-center">
<avatar class="flex-shrink-0 mr-3" v-if="user.avatar.url" :url="user.avatar.url"
:service="user.avatar.service" :service-url="user.avatar.serviceUrl"></avatar>
<avatar
v-if="user.avatar.url"
class="flex-shrink-0 mr-3"
:url="user.avatar.url"
:service="user.avatar.service"
:service-url="user.avatar.serviceUrl"
/>
<div class="flex-fill">
<h2 class="card-title mt-0">{{ user.name }}</h2>
<h3 class="card-subtitle">{{ user.email }}</h3>
<h2 class="card-title mt-0">
{{ user.name }}
</h2>
<h3 class="card-subtitle">
{{ user.email }}
</h3>
</div>
<div class="flex-md-shrink-0 mt-3 mt-md-0 buttons">
<a class="btn btn-bg" role="button" :href="profileUrl">
<icon icon="account_circle"></icon>
<a
class="btn btn-bg"
role="button"
:href="profileUrl"
>
<icon icon="account_circle" />
{{ $gettext('My Account') }}
</a>
<a v-if="showAdmin" class="btn btn-bg" role="button" :href="adminUrl">
<icon icon="settings"></icon>
<a
v-if="showAdmin"
class="btn btn-bg"
role="button"
:href="adminUrl"
>
<icon icon="settings" />
{{ $gettext('Administration') }}
</a>
</div>
</div>
<template v-if="!notificationsLoading && notifications.length > 0">
<div v-for="notification in notifications" class="card-body d-flex align-items-center"
:class="'alert-'+notification.type" role="alert">
<div class="flex-shrink-0 mr-3" v-if="'info' === notification.type">
<icon class="lg" icon="info"></icon>
<div
v-for="notification in notifications"
class="card-body d-flex align-items-center"
:class="'alert-'+notification.type"
role="alert"
>
<div
v-if="'info' === notification.type"
class="flex-shrink-0 mr-3"
>
<icon
class="lg"
icon="info"
/>
</div>
<div class="flex-shrink-0 mr-3" v-else>
<icon class="lg" icon="warning"></icon>
<div
v-else
class="flex-shrink-0 mr-3"
>
<icon
class="lg"
icon="warning"
/>
</div>
<div class="flex-fill">
<h4>{{ notification.title }}</h4>
<p class="card-text" v-html="notification.body"></p>
<p
class="card-text"
v-html="notification.body"
/>
</div>
<div v-if="notification.actionLabel && notification.actionUrl" class="flex-shrink-0 ml-3">
<b-button :href="notification.actionUrl" target="_blank" size="sm" variant="light">
<div
v-if="notification.actionLabel && notification.actionUrl"
class="flex-shrink-0 ml-3"
>
<b-button
:href="notification.actionUrl"
target="_blank"
size="sm"
variant="light"
>
{{ notification.actionLabel }}
</b-button>
</div>
@ -44,31 +92,60 @@
</template>
</section>
<section class="card mb-4" role="region" v-if="showCharts">
<section
v-if="showCharts"
class="card mb-4"
role="region"
>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h3 class="card-title">{{ $gettext('Listeners Per Station') }}</h3>
<h3 class="card-title">
{{ $gettext('Listeners Per Station') }}
</h3>
</div>
<div class="flex-shrink-0">
<b-button variant="outline-light" size="sm" class="py-2" @click="toggleCharts">{{
<b-button
variant="outline-light"
size="sm"
class="py-2"
@click="toggleCharts"
>
{{
langShowHideCharts
}}
</b-button>
</div>
</div>
<b-collapse id="charts" v-model="chartsVisible">
<b-overlay variant="card" :show="chartsLoading">
<div class="card-body py-5" v-if="chartsLoading">
<b-collapse
id="charts"
v-model="chartsVisible"
>
<b-overlay
variant="card"
:show="chartsLoading"
>
<div
v-if="chartsLoading"
class="card-body py-5"
>
&nbsp;
</div>
<b-tabs pills card lazy v-else>
<b-tabs
v-else
pills
card
lazy
>
<b-tab active>
<template #title>
{{ $gettext('Average Listeners') }}
</template>
<time-series-chart style="width: 100%;" :data="chartsData.average.metrics">
<span v-html="chartsData.average.alt"></span>
<time-series-chart
style="width: 100%;"
:data="chartsData.average.metrics"
>
<span v-html="chartsData.average.alt" />
</time-series-chart>
</b-tab>
<b-tab>
@ -76,8 +153,11 @@
{{ $gettext('Unique Listeners') }}
</template>
<time-series-chart style="width: 100%;" :data="chartsData.unique.metrics">
<span v-html="chartsData.unique.alt"></span>
<time-series-chart
style="width: 100%;"
:data="chartsData.unique.metrics"
>
<span v-html="chartsData.unique.alt" />
</time-series-chart>
</b-tab>
</b-tabs>
@ -85,24 +165,47 @@
</b-collapse>
</section>
<section class="card" role="region">
<section
class="card"
role="region"
>
<div class="card-header bg-primary-dark d-flex flex-wrap align-items-center">
<div class="flex-fill">
<h2 class="card-title">{{ $gettext('Station Overview') }}</h2>
<h2 class="card-title">
{{ $gettext('Station Overview') }}
</h2>
</div>
<div class="flex-shrink-0" v-if="showAdmin">
<b-button variant="outline-light" size="sm" class="py-2" :href="manageStationsUrl">
<icon icon="settings"></icon>
<div
v-if="showAdmin"
class="flex-shrink-0"
>
<b-button
variant="outline-light"
size="sm"
class="py-2"
:href="manageStationsUrl"
>
<icon icon="settings" />
{{ $gettext('Manage Stations') }}
</b-button>
</div>
</div>
<b-overlay variant="card" :show="stationsLoading">
<div class="card-body py-3" v-if="stationsLoading">
<b-overlay
variant="card"
:show="stationsLoading"
>
<div
v-if="stationsLoading"
class="card-body py-3"
>
&nbsp;
</div>
<table class="table table-striped table-responsive mb-0" id="station_dashboard" v-else>
<table
v-else
id="station_dashboard"
class="table table-striped table-responsive mb-0"
>
<colgroup>
<col width="5%">
<col width="30%">
@ -111,68 +214,105 @@
<col width="15%">
</colgroup>
<thead>
<tr>
<th class="pr-3">&nbsp;</th>
<th class="pl-2">{{ $gettext('Station Name') }}</th>
<th class="text-center">{{ $gettext('Listeners') }}</th>
<th>{{ $gettext('Now Playing') }}</th>
<th class="text-right"></th>
</tr>
<tr>
<th class="pr-3">
&nbsp;
</th>
<th class="pl-2">
{{ $gettext('Station Name') }}
</th>
<th class="text-center">
{{ $gettext('Listeners') }}
</th>
<th>{{ $gettext('Now Playing') }}</th>
<th class="text-right" />
</tr>
</thead>
<tbody>
<tr class="align-middle" v-for="item in stations" :key="item.station.id">
<td class="text-center pr-3">
<play-button class="file-icon" icon-class="lg outlined align-middle"
:url="item.station.listen_url" is-stream></play-button>
</td>
<td class="pl-2">
<div class="typography-subheading">{{ item.station.name }}</div>
<div v-if="item.station.is_public">
<a :href="item.links.public" target="_blank">
{{ $gettext('Public Page') }}
</a>
</div>
</td>
<td class="text-center">
<span class="pr-1">
<icon class="sm align-middle" icon="headset"></icon>
</span>
<template v-if="item.links.listeners">
<a :href="item.links.listeners">
<tr
v-for="item in stations"
:key="item.station.id"
class="align-middle"
>
<td class="text-center pr-3">
<play-button
class="file-icon"
icon-class="lg outlined align-middle"
:url="item.station.listen_url"
is-stream
/>
</td>
<td class="pl-2">
<div class="typography-subheading">
{{ item.station.name }}
</div>
<div v-if="item.station.is_public">
<a
:href="item.links.public"
target="_blank"
>
{{ $gettext('Public Page') }}
</a>
</div>
</td>
<td class="text-center">
<span class="pr-1">
<icon
class="sm align-middle"
icon="headset"
/>
</span>
<template v-if="item.links.listeners">
<a :href="item.links.listeners">
{{ item.listeners.total }}
</a>
</template>
<template v-else>
{{ item.listeners.total }}
</a>
</template>
<template v-else>
{{ item.listeners.total }}
</template>
</td>
<td>
<div class="d-flex align-items-center">
<album-art v-if="showAlbumArt" :src="item.now_playing.song.art"
class="flex-shrink-0 pr-3"></album-art>
</template>
</td>
<td>
<div class="d-flex align-items-center">
<album-art
v-if="showAlbumArt"
:src="item.now_playing.song.art"
class="flex-shrink-0 pr-3"
/>
<div v-if="!item.is_online" class="flex-fill text-muted">
{{ $gettext('Station Offline') }}
<div
v-if="!item.is_online"
class="flex-fill text-muted"
>
{{ $gettext('Station Offline') }}
</div>
<div
v-else-if="item.now_playing.song.title !== ''"
class="flex-fill"
>
<strong><span class="nowplaying-title">
{{ item.now_playing.song.title }}
</span></strong><br>
<span class="nowplaying-artist">{{ item.now_playing.song.artist }}</span>
</div>
<div
v-else
class="flex-fill"
>
<strong><span class="nowplaying-title">
{{ item.now_playing.song.text }}
</span></strong>
</div>
</div>
<div v-else-if="item.now_playing.song.title !== ''" class="flex-fill">
<strong><span class="nowplaying-title">
{{ item.now_playing.song.title }}
</span></strong><br>
<span class="nowplaying-artist">{{ item.now_playing.song.artist }}</span>
</div>
<div v-else class="flex-fill">
<strong><span class="nowplaying-title">
{{ item.now_playing.song.text }}
</span></strong>
</div>
</div>
</td>
<td class="text-right">
<a class="btn btn-primary" v-bind:href="item.links.manage">
{{ $gettext('Manage') }}
</a>
</td>
</tr>
</td>
<td class="text-right">
<a
class="btn btn-primary"
:href="item.links.manage"
>
{{ $gettext('Manage') }}
</a>
</td>
</tr>
</tbody>
</table>
</b-overlay>

View File

@ -1,20 +1,38 @@
<template>
<b-form-group v-bind="$attrs" :label-for="id">
<b-form-group
v-bind="$attrs"
:label-for="id"
>
<template #default="slotProps">
<div :id="id">
<slot name="default" v-bind="slotProps"></slot>
<slot
name="default"
v-bind="slotProps"
/>
</div>
</template>
<template #label="slotProps">
<slot name="label" v-bind="slotProps"></slot>
<slot
name="label"
v-bind="slotProps"
/>
</template>
<template #description="slotProps">
<slot name="description" v-bind="slotProps"></slot>
<slot
name="description"
v-bind="slotProps"
/>
</template>
<template v-for="(_, slot) of filteredSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of filteredSlots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</b-form-group>
</template>

View File

@ -1,30 +1,57 @@
<template>
<b-form-group v-bind="$attrs" :label-for="id" :state="fieldState">
<b-form-group
v-bind="$attrs"
:label-for="id"
:state="fieldState"
>
<template #default>
<slot name="default" v-bind="{ id, field, state: fieldState }">
<b-form-checkbox v-bind="inputAttrs" v-model="field.$model" :id="id" :name="name">
<slot name="label"></slot>
<span v-if="isRequired" class="text-danger">
<slot
name="default"
v-bind="{ id, field, state: fieldState }"
>
<b-form-checkbox
v-bind="inputAttrs"
:id="id"
v-model="field.$model"
:name="name"
>
<slot name="label" />
<span
v-if="isRequired"
class="text-danger"
>
<span aria-hidden="true">*</span>
<span class="sr-only">Required</span>
</span>
<span v-if="advanced" class="badge small badge-primary">
<span
v-if="advanced"
class="badge small badge-primary"
>
{{ $gettext('Advanced') }}
</span>
</b-form-checkbox>
<b-form-invalid-feedback :state="fieldState">
<vuelidate-error :field="field"></vuelidate-error>
<vuelidate-error :field="field" />
</b-form-invalid-feedback>
</slot>
</template>
<template #description="slotProps">
<slot name="description" v-bind="slotProps"></slot>
<slot
name="description"
v-bind="slotProps"
/>
</template>
<template v-for="(_, slot) of filteredSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of filteredSlots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</b-form-group>
</template>

View File

@ -1,36 +1,82 @@
<template>
<b-form-group v-bind="$attrs" :label-for="id" :state="fieldState">
<b-form-group
v-bind="$attrs"
:label-for="id"
:state="fieldState"
>
<template #default>
<slot name="default" v-bind="{ id, field, state: fieldState }">
<b-form-textarea v-bind="inputAttrs" v-if="inputType === 'textarea'" ref="$input" :id="id" :name="name"
v-model="modelValue" :required="isRequired" :number="isNumeric" :trim="inputTrim"
:autofocus="autofocus" :state="fieldState"></b-form-textarea>
<b-form-input v-bind="inputAttrs" v-else ref="$input" :type="inputType" :id="id" :name="name"
v-model="modelValue" :required="isRequired" :number="isNumeric" :trim="inputTrim"
:autofocus="autofocus" :state="fieldState"></b-form-input>
<slot
name="default"
v-bind="{ id, field, state: fieldState }"
>
<b-form-textarea
v-if="inputType === 'textarea'"
v-bind="inputAttrs"
:id="id"
ref="$input"
v-model="modelValue"
:name="name"
:required="isRequired"
:number="isNumeric"
:trim="inputTrim"
:autofocus="autofocus"
:state="fieldState"
/>
<b-form-input
v-else
v-bind="inputAttrs"
:id="id"
ref="$input"
v-model="modelValue"
:type="inputType"
:name="name"
:required="isRequired"
:number="isNumeric"
:trim="inputTrim"
:autofocus="autofocus"
:state="fieldState"
/>
</slot>
<b-form-invalid-feedback :state="fieldState">
<vuelidate-error :field="field"></vuelidate-error>
<vuelidate-error :field="field" />
</b-form-invalid-feedback>
</template>
<template #label="slotProps">
<slot v-bind="slotProps" name="label"></slot>
<span v-if="isRequired" class="text-danger">
<slot
v-bind="slotProps"
name="label"
/>
<span
v-if="isRequired"
class="text-danger"
>
<span aria-hidden="true">*</span>
<span class="sr-only">Required</span>
</span>
<span v-if="advanced" class="badge small badge-primary ml-2">
<span
v-if="advanced"
class="badge small badge-primary ml-2"
>
{{ $gettext('Advanced') }}
</span>
</template>
<template #description="slotProps">
<slot v-bind="slotProps" name="description"></slot>
<slot
v-bind="slotProps"
name="description"
/>
</template>
<template v-for="(_, slot) of filteredSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope"></slot>
<template
v-for="(_, slot) of filteredSlots"
#[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</b-form-group>
</template>

View File

@ -1,69 +1,81 @@
<template>
<audio-player ref="$player" :volume="volume" :is-muted="isMuted"></audio-player>
<audio-player
ref="$player"
:volume="volume"
:is-muted="isMuted"
/>
<div class="ml-3 player-inline" v-if="isPlaying">
<div class="inline-seek d-inline-flex align-items-center ml-1" v-if="!current.isStream && duration !== 0">
<div
v-if="isPlaying"
class="ml-3 player-inline"
>
<div
v-if="!current.isStream && duration !== 0"
class="inline-seek d-inline-flex align-items-center ml-1"
>
<div class="flex-shrink-0 mx-1 text-white-50 time-display">
{{ currentTimeText }}
</div>
<div class="flex-fill mx-2">
<input type="range" :title="$gettext('Seek')" class="player-seek-range custom-range" min="0"
max="100"
step="1" v-model="progress">
<input
v-model="progress"
type="range"
:title="$gettext('Seek')"
class="player-seek-range custom-range"
min="0"
max="100"
step="1"
>
</div>
<div class="flex-shrink-0 mx-1 text-white-50 time-display">
{{ durationText }}
</div>
</div>
<a class="btn btn-sm btn-outline-light px-2 ml-1" href="#" @click.prevent="stop()"
:aria-label="$gettext('Stop')">
<icon icon="stop"></icon>
<a
class="btn btn-sm btn-outline-light px-2 ml-1"
href="#"
:aria-label="$gettext('Stop')"
@click.prevent="stop()"
>
<icon icon="stop" />
</a>
<div class="inline-volume-controls d-inline-flex align-items-center ml-1">
<div class="flex-shrink-0">
<a class="btn btn-sm btn-outline-light px-2" href="#" @click.prevent="mute"
:aria-label="$gettext('Mute')">
<icon icon="volume_mute"></icon>
<a
class="btn btn-sm btn-outline-light px-2"
href="#"
:aria-label="$gettext('Mute')"
@click.prevent="mute"
>
<icon icon="volume_mute" />
</a>
</div>
<div class="flex-fill mx-1">
<input type="range" :title="$gettext('Volume')" class="player-volume-range custom-range" min="0"
max="100"
step="1" v-model="volume">
<input
v-model="volume"
type="range"
:title="$gettext('Volume')"
class="player-volume-range custom-range"
min="0"
max="100"
step="1"
>
</div>
<div class="flex-shrink-0">
<a class="btn btn-sm btn-outline-light px-2" href="#" @click.prevent="fullVolume"
:aria-label="$gettext('Full Volume')">
<icon icon="volume_up"></icon>
<a
class="btn btn-sm btn-outline-light px-2"
href="#"
:aria-label="$gettext('Full Volume')"
@click.prevent="fullVolume"
>
<icon icon="volume_up" />
</a>
</div>
</div>
</div>
</template>
<style lang="scss">
.player-inline {
.inline-seek {
width: 300px;
div.time-display {
font-size: 90%;
}
}
.inline-volume-controls {
width: 175px;
}
input.player-volume-range,
input.player-seek-range {
width: 100%;
height: 10px;
}
}
</style>
<script setup>
import AudioPlayer from '~/components/Common/AudioPlayer.vue';
import formatTime from '~/functions/formatTime.js';
@ -122,3 +134,25 @@ const fullVolume = () => {
volume.value = 100;
};
</script>
<style lang="scss">
.player-inline {
.inline-seek {
width: 300px;
div.time-display {
font-size: 90%;
}
}
.inline-volume-controls {
width: 175px;
}
input.player-volume-range,
input.player-seek-range {
width: 100%;
height: 10px;
}
}
</style>

View File

@ -2,33 +2,54 @@
<div class="public-page">
<div class="card">
<div class="card-body">
<h2 class="card-title">{{ stationName }}</h2>
<h2 class="card-title">
{{ stationName }}
</h2>
<div class="stations nowplaying">
<radio-player v-bind="$props" @np_updated="onNowPlayingUpdate"></radio-player>
<radio-player
v-bind="$props"
@np_updated="onNowPlayingUpdate"
/>
</div>
</div>
<div class="card-actions">
<a class="btn btn-sm btn-outline-secondary" v-b-modal.song_history_modal>
<icon icon="history"></icon>
<a
v-b-modal.song_history_modal
class="btn btn-sm btn-outline-secondary"
>
<icon icon="history" />
{{ $gettext('Song History') }}
</a>
<a class="btn btn-sm btn-outline-secondary" v-if="enableRequests" v-b-modal.request_modal>
<icon icon="help_outline"></icon>
<a
v-if="enableRequests"
v-b-modal.request_modal
class="btn btn-sm btn-outline-secondary"
>
<icon icon="help_outline" />
{{ $gettext('Request Song') }}
</a>
<a class="btn btn-sm btn-outline-secondary" :href="downloadPlaylistUri">
<icon icon="file_download"></icon>
<a
class="btn btn-sm btn-outline-secondary"
:href="downloadPlaylistUri"
>
<icon icon="file_download" />
{{ $gettext('Playlist') }}
</a>
</div>
</div>
</div>
<song-history-modal :show-album-art="showAlbumArt" :history="history"></song-history-modal>
<request-modal :show-album-art="showAlbumArt" :request-list-uri="requestListUri"
:custom-fields="customFields"></request-modal>
<song-history-modal
:show-album-art="showAlbumArt"
:history="history"
/>
<request-modal
:show-album-art="showAlbumArt"
:request-list-uri="requestListUri"
:custom-fields="customFields"
/>
</template>
<script setup>

View File

@ -1,7 +1,17 @@
<template>
<b-modal size="lg" id="request_modal" ref="$modal" :title="$gettext('Request a Song')" hide-footer>
<song-request :show-album-art="showAlbumArt" :request-list-uri="requestListUri" :custom-fields="customFields"
@submitted="doClose"></song-request>
<b-modal
id="request_modal"
ref="$modal"
size="lg"
:title="$gettext('Request a Song')"
hide-footer
>
<song-request
:show-album-art="showAlbumArt"
:request-list-uri="requestListUri"
:custom-fields="customFields"
@submitted="doClose"
/>
</b-modal>
</template>

View File

@ -1,21 +1,54 @@
<template>
<div id="station-history">
<p v-if="history.length <= 0">{{ $gettext('No records to display.') }}</p>
<div class="song" v-for="(row, index) in history">
<p v-if="history.length <= 0">
{{ $gettext('No records to display.') }}
</p>
<div
v-for="(row, index) in history"
class="song"
>
<strong class="order">{{ history.length - index }}</strong>
<img v-if="showAlbumArt" class="art" :src="row.song.art">
<img
v-if="showAlbumArt"
class="art"
:src="row.song.art"
>
<div class="name">
<strong v-html="row.song.title"></strong>
<span v-html="albumAndArtist(row.song)"></span>
<strong v-html="row.song.title" />
<span v-html="albumAndArtist(row.song)" />
</div>
<div class="break"></div>
<div class="break" />
<small class="date-played text-muted">
<span v-html="unixTimestampToDate(row.played_at)"></span>
<span v-html="unixTimestampToDate(row.played_at)" />
</small>
</div>
</div>
</template>
<script setup>
import {DateTime} from "luxon";
const props = defineProps({
history: Array,
showAlbumArt: {
type: Boolean,
default: true
},
});
const unixTimestampToDate = (timestamp) => {
if (!timestamp) {
return '';
}
return DateTime.fromSeconds(timestamp).toRelative();
};
const albumAndArtist = (song) => {
return [song.artist, song.album].filter(str => !!str).join(', ');
};
</script>
<style lang="scss">
#station-history {
.song {
@ -76,27 +109,3 @@
}
}
</style>
<script setup>
import {DateTime} from "luxon";
const props = defineProps({
history: Array,
showAlbumArt: {
type: Boolean,
default: true
},
});
const unixTimestampToDate = (timestamp) => {
if (!timestamp) {
return '';
}
return DateTime.fromSeconds(timestamp).toRelative();
};
const albumAndArtist = (song) => {
return [song.artist, song.album].filter(str => !!str).join(', ');
};
</script>

View File

@ -1,6 +1,16 @@
<template>
<b-modal size="md" id="song_history_modal" ref="modal" :title="$gettext('Song History')" centered hide-footer>
<song-history :show-album-art="showAlbumArt" :history="history"></song-history>
<b-modal
id="song_history_modal"
ref="modal"
size="md"
:title="$gettext('Song History')"
centered
hide-footer
>
<song-history
:show-album-art="showAlbumArt"
:history="history"
/>
</b-modal>
</template>

View File

@ -1,6 +1,9 @@
<template>
<div id="song_history">
<song-history :show-album-art="showAlbumArt" :history="history"></song-history>
<song-history
:show-album-art="showAlbumArt"
:history="history"
/>
</div>
</template>

View File

@ -1,7 +1,18 @@
<template>
<section id="content" role="main" class="d-flex align-items-stretch" style="height: 100vh;">
<div class="container pt-5 pb-5 h-100" style="flex: 1;">
<div class="card" style="height: 100%;">
<section
id="content"
role="main"
class="d-flex align-items-stretch"
style="height: 100vh;"
>
<div
class="container pt-5 pb-5 h-100"
style="flex: 1;"
>
<div
class="card"
style="height: 100%;"
>
<div class="card-header bg-primary-dark">
<div class="d-flex align-items-center">
<div class="flex-shrink">
@ -15,31 +26,56 @@
</h2>
</div>
<div class="flex-fill text-right">
<inline-player ref="player"></inline-player>
<inline-player ref="player" />
</div>
</div>
</div>
<data-table ref="datatable" id="station_on_demand_table" paginated select-fields
:fields="fields" :api-url="listUrl">
<data-table
id="station_on_demand_table"
ref="datatable"
paginated
select-fields
:fields="fields"
:api-url="listUrl"
>
<template #cell(download_url)="row">
<play-button class="file-icon" icon-class="outlined" :url="row.item.download_url"
:is-stream="false"></play-button>
<play-button
class="file-icon"
icon-class="outlined"
:url="row.item.download_url"
:is-stream="false"
/>
<template v-if="showDownloadButton">
&nbsp;
<a class="name" :href="row.item.download_url" target="_blank" :title="$gettext('Download')">
<icon icon="cloud_download"></icon>
<a
class="name"
:href="row.item.download_url"
target="_blank"
:title="$gettext('Download')"
>
<icon icon="cloud_download" />
</a>
</template>
</template>
<template #cell(media_art)="row">
<a :href="row.item.media_art" class="album-art" target="_blank"
data-fancybox="gallery">
<img class="media_manager_album_art" :alt="$gettext('Album Art')" :src="row.item.media_art">
<a
:href="row.item.media_art"
class="album-art"
target="_blank"
data-fancybox="gallery"
>
<img
class="media_manager_album_art"
:alt="$gettext('Album Art')"
:src="row.item.media_art"
>
</a>
</template>
<template #cell(size)="row">
<template v-if="!row.item.size">&nbsp;</template>
<template v-if="!row.item.size">
&nbsp;
</template>
<template v-else>
{{ formatFileSize(row.item.size) }}
</template>
@ -50,6 +86,43 @@
</section>
</template>
<script setup>
import InlinePlayer from '../InlinePlayer';
import DataTable from '~/components/Common/DataTable';
import {forEach} from 'lodash';
import Icon from '~/components/Common/Icon';
import PlayButton from "~/components/Common/PlayButton";
import {useTranslate} from "~/vendor/gettext";
const props = defineProps({
listUrl: String,
stationName: String,
customFields: Array,
showDownloadButton: Boolean
});
const {$gettext} = useTranslate();
let fields = [
{key: 'download_url', label: ' '},
{key: 'media_art', label: $gettext('Art')},
{key: 'media_title', label: $gettext('Title'), sortable: true, selectable: true},
{key: 'media_artist', label: $gettext('Artist'), sortable: true, selectable: true},
{key: 'media_album', label: $gettext('Album'), sortable: true, selectable: true, visible: false},
{key: 'playlist', label: $gettext('Playlist'), sortable: true, selectable: true, visible: false}
];
forEach(props.customFields.slice(), (field) => {
fields.push({
key: field.display_key,
label: field.label,
sortable: true,
selectable: true,
visible: false
});
});
</script>
<style lang="scss">
.ondemand.embed {
.container {
@ -91,40 +164,3 @@
}
}
</style>
<script setup>
import InlinePlayer from '../InlinePlayer';
import DataTable from '~/components/Common/DataTable';
import {forEach} from 'lodash';
import Icon from '~/components/Common/Icon';
import PlayButton from "~/components/Common/PlayButton";
import {useTranslate} from "~/vendor/gettext";
const props = defineProps({
listUrl: String,
stationName: String,
customFields: Array,
showDownloadButton: Boolean
});
const {$gettext} = useTranslate();
let fields = [
{key: 'download_url', label: ' '},
{key: 'media_art', label: $gettext('Art')},
{key: 'media_title', label: $gettext('Title'), sortable: true, selectable: true},
{key: 'media_artist', label: $gettext('Artist'), sortable: true, selectable: true},
{key: 'media_album', label: $gettext('Album'), sortable: true, selectable: true, visible: false},
{key: 'playlist', label: $gettext('Playlist'), sortable: true, selectable: true, visible: false}
];
forEach(props.customFields.slice(), (field) => {
fields.push({
key: field.display_key,
label: field.label,
sortable: true,
selectable: true,
visible: false
});
});
</script>

View File

@ -1,16 +1,33 @@
<template>
<div class="radio-player-widget">
<audio-player ref="$player" :title="np.now_playing.song.text" :volume="volume"
:is-muted="isMuted"></audio-player>
<audio-player
ref="$player"
:title="np.now_playing.song.text"
:volume="volume"
:is-muted="isMuted"
/>
<div class="now-playing-details">
<div class="now-playing-art" v-if="showAlbumArt && np.now_playing.song.art">
<a :href="np.now_playing.song.art" data-fancybox target="_blank">
<img :src="np.now_playing.song.art" :alt="$gettext('Album Art')">
<div
v-if="showAlbumArt && np.now_playing.song.art"
class="now-playing-art"
>
<a
:href="np.now_playing.song.art"
data-fancybox
target="_blank"
>
<img
:src="np.now_playing.song.art"
:alt="$gettext('Album Art')"
>
</a>
</div>
<div class="now-playing-main">
<h6 class="now-playing-live" v-if="np.live.is_live">
<h6
v-if="np.live.is_live"
class="now-playing-live"
>
{{ $gettext('Live') }}
{{ np.live.streamer_name }}
</h6>
@ -21,21 +38,33 @@
</h4>
</div>
<div v-else-if="np.now_playing.song.title !== ''">
<h4 class="now-playing-title">{{ np.now_playing.song.title }}</h4>
<h5 class="now-playing-artist">{{ np.now_playing.song.artist }}</h5>
<h4 class="now-playing-title">
{{ np.now_playing.song.title }}
</h4>
<h5 class="now-playing-artist">
{{ np.now_playing.song.artist }}
</h5>
</div>
<div v-else>
<h4 class="now-playing-title">{{ np.now_playing.song.text }}</h4>
<h4 class="now-playing-title">
{{ np.now_playing.song.text }}
</h4>
</div>
<div class="time-display" v-if="currentTimeElapsedDisplay != null">
<div
v-if="currentTimeElapsedDisplay != null"
class="time-display"
>
<div class="time-display-played text-secondary">
{{ currentTimeElapsedDisplay }}
</div>
<div class="time-display-progress">
<div class="progress">
<div class="progress-bar bg-secondary" role="progressbar"
:style="{ width: currentTrackPercent+'%' }"></div>
<div
class="progress-bar bg-secondary"
role="progressbar"
:style="{ width: currentTrackPercent+'%' }"
/>
</div>
</div>
<div class="time-display-total text-secondary">
@ -48,18 +77,39 @@
<hr>
<div class="radio-controls">
<play-button class="radio-control-play-button" icon-class="outlined lg" :url="currentStream.url"
:is-hls="currentStream.hls" is-stream></play-button>
<play-button
class="radio-control-play-button"
icon-class="outlined lg"
:url="currentStream.url"
:is-hls="currentStream.hls"
is-stream
/>
<div class="radio-control-select-stream">
<div v-if="streams.length > 1" class="dropdown">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="btn-select-stream"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div
v-if="streams.length > 1"
class="dropdown"
>
<button
id="btn-select-stream"
class="btn btn-sm btn-outline-primary dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ currentStream.name }}
</button>
<div class="dropdown-menu" aria-labelledby="btn-select-stream">
<a class="dropdown-item" v-for="stream in streams" href="javascript:"
@click.prevent="switchStream(stream)">
<div
class="dropdown-menu"
aria-labelledby="btn-select-stream"
>
<a
v-for="stream in streams"
class="dropdown-item"
href="javascript:"
@click.prevent="switchStream(stream)"
>
{{ stream.name }}
</a>
</div>
@ -67,146 +117,41 @@
</div>
<div class="radio-control-mute-button">
<a href="#" class="text-secondary" :title="$gettext('Mute')" @click.prevent="toggleMute">
<icon icon="volume_mute"></icon>
<a
href="#"
class="text-secondary"
:title="$gettext('Mute')"
@click.prevent="toggleMute"
>
<icon icon="volume_mute" />
</a>
</div>
<div class="radio-control-volume-slider">
<input type="range" :title="$gettext('Volume')" class="custom-range" min="0" max="100" step="1"
:disabled="isMuted" v-model.number="volume">
<input
v-model.number="volume"
type="range"
:title="$gettext('Volume')"
class="custom-range"
min="0"
max="100"
step="1"
:disabled="isMuted"
>
</div>
<div class="radio-control-max-volume-button">
<a href="#" class="text-secondary" :title="$gettext('Full Volume')" @click.prevent="fullVolume">
<icon icon="volume_up"></icon>
<a
href="#"
class="text-secondary"
:title="$gettext('Full Volume')"
@click.prevent="fullVolume"
>
<icon icon="volume_up" />
</a>
</div>
</div>
</div>
</template>
<style lang="scss">
.radio-player-widget {
.now-playing-details {
display: flex;
align-items: center;
.now-playing-art {
padding-right: .5rem;
img {
width: 75px;
height: auto;
border-radius: 5px;
@media (max-width: 575px) {
width: 50px;
}
}
}
.now-playing-main {
flex: 1;
min-width: 0;
}
h4, h5, h6 {
margin: 0;
line-height: 1.3;
}
h4 {
font-size: 15px;
}
h5 {
font-size: 13px;
font-weight: normal;
}
h6 {
font-size: 11px;
font-weight: normal;
}
.now-playing-title,
.now-playing-artist {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
&:hover {
text-overflow: clip;
white-space: normal;
word-break: break-all;
}
}
.time-display {
font-size: 10px;
margin-top: .25rem;
flex-direction: row;
align-items: center;
display: flex;
.time-display-played {
margin-right: .5rem;
}
.time-display-progress {
flex: 1 1 auto;
.progress-bar {
-webkit-transition: width 1s; /* Safari */
transition: width 1s;
transition-timing-function: linear;
}
}
.time-display-total {
margin-left: .5rem;
}
}
}
hr {
margin-top: .5rem;
margin-bottom: .5rem;
}
i.material-icons {
line-height: 1;
}
.radio-controls {
display: flex;
flex-direction: row;
align-items: center;
.radio-control-play-button {
margin-right: .25rem;
}
.radio-control-select-stream {
flex: 1 1 auto;
}
.radio-control-mute-button,
.radio-control-max-volume-button {
flex-shrink: 0;
}
.radio-control-volume-slider {
flex: 1 1 auto;
max-width: 30%;
input {
height: 10px;
}
}
}
}
</style>
<script setup>
import AudioPlayer from '~/components/Common/AudioPlayer';
import Icon from '~/components/Common/Icon';
@ -362,3 +307,126 @@ const onNowPlayingUpdated = (np_new) => {
watch(np, onNowPlayingUpdated, {immediate: true});
</script>
<style lang="scss">
.radio-player-widget {
.now-playing-details {
display: flex;
align-items: center;
.now-playing-art {
padding-right: .5rem;
img {
width: 75px;
height: auto;
border-radius: 5px;
@media (max-width: 575px) {
width: 50px;
}
}
}
.now-playing-main {
flex: 1;
min-width: 0;
}
h4, h5, h6 {
margin: 0;
line-height: 1.3;
}
h4 {
font-size: 15px;
}
h5 {
font-size: 13px;
font-weight: normal;
}
h6 {
font-size: 11px;
font-weight: normal;
}
.now-playing-title,
.now-playing-artist {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
&:hover {
text-overflow: clip;
white-space: normal;
word-break: break-all;
}
}
.time-display {
font-size: 10px;
margin-top: .25rem;
flex-direction: row;
align-items: center;
display: flex;
.time-display-played {
margin-right: .5rem;
}
.time-display-progress {
flex: 1 1 auto;
.progress-bar {
-webkit-transition: width 1s; /* Safari */
transition: width 1s;
transition-timing-function: linear;
}
}
.time-display-total {
margin-left: .5rem;
}
}
}
hr {
margin-top: .5rem;
margin-bottom: .5rem;
}
i.material-icons {
line-height: 1;
}
.radio-controls {
display: flex;
flex-direction: row;
align-items: center;
.radio-control-play-button {
margin-right: .25rem;
}
.radio-control-select-stream {
flex: 1 1 auto;
}
.radio-control-mute-button,
.radio-control-max-volume-button {
flex-shrink: 0;
}
.radio-control-volume-slider {
flex: 1 1 auto;
max-width: 30%;
input {
height: 10px;
}
}
}
}
</style>

View File

@ -1,12 +1,23 @@
<template>
<div style="overflow-x: hidden">
<data-table ref="datatable" id="song_requests" paginated select-fields :page-options="pageOptions"
:fields="fields"
:responsive="false" :api-url="requestListUri">
<data-table
id="song_requests"
ref="datatable"
paginated
select-fields
:page-options="pageOptions"
:fields="fields"
:responsive="false"
:api-url="requestListUri"
>
<template #cell(name)="row">
<div class="d-flex align-items-center">
<album-art v-if="showAlbumArt" :src="row.item.song.art" :width="40"
class="flex-shrink-1 pr-3"></album-art>
<album-art
v-if="showAlbumArt"
:src="row.item.song.art"
:width="40"
class="flex-shrink-1 pr-3"
/>
<div class="flex-fill">
{{ row.item.song.title }}<br>
<small>{{ row.item.song.artist }}</small>
@ -15,7 +26,11 @@
</template>
<template #cell(actions)="row">
<b-button-group size="sm">
<b-button size="sm" variant="primary" @click.prevent="doSubmitRequest(row.item.request_url)">
<b-button
size="sm"
variant="primary"
@click.prevent="doSubmitRequest(row.item.request_url)"
>
{{ $gettext('Request') }}
</b-button>
</b-button-group>
@ -24,14 +39,6 @@
</div>
</template>
<style lang="scss">
img.album_art {
width: 40px;
height: auto;
border-radius: 5px;
}
</style>
<script>
import DataTable from '~/components/Common/DataTable';
import _ from 'lodash';
@ -39,7 +46,6 @@ import AlbumArt from '~/components/Common/AlbumArt';
export default {
components: {AlbumArt, DataTable},
emits: ['submitted'],
props: {
requestListUri: {
type: String,
@ -55,6 +61,7 @@ export default {
default: () => []
}
},
emits: ['submitted'],
data () {
let fields = [
{key: 'name', isRowHeader: true, label: this.$gettext('Name'), sortable: true, selectable: true},
@ -121,3 +128,11 @@ export default {
}
};
</script>
<style lang="scss">
img.album_art {
width: 40px;
height: auto;
border-radius: 5px;
}
</style>

View File

@ -1,7 +1,18 @@
<template>
<section id="content" role="main" class="d-flex align-items-stretch" style="height: 100vh;">
<div class="container pt-5 pb-5 h-100" style="flex: 1;">
<div class="card" style="height: 100%;">
<section
id="content"
role="main"
class="d-flex align-items-stretch"
style="height: 100vh;"
>
<div
class="container pt-5 pb-5 h-100"
style="flex: 1;"
>
<div
class="card"
style="height: 100%;"
>
<div class="card-header bg-primary-dark">
<div class="d-flex align-items-center">
<div class="flex-shrink">
@ -18,14 +29,27 @@
</div>
<div id="station-schedule-calendar">
<schedule ref="schedule" :schedule-url="scheduleUrl"
:station-time-zone="stationTimeZone"></schedule>
<schedule
ref="schedule"
:schedule-url="scheduleUrl"
:station-time-zone="stationTimeZone"
/>
</div>
</div>
</div>
</section>
</template>
<script setup>
import Schedule from '~/components/Common/ScheduleView';
const props = defineProps({
scheduleUrl: String,
stationName: String,
stationTimeZone: String
});
</script>
<style lang="scss">
.schedule.embed {
.container {
@ -38,13 +62,3 @@
overflow-y: auto;
}
</style>
<script setup>
import Schedule from '~/components/Common/ScheduleView';
const props = defineProps({
scheduleUrl: String,
stationName: String,
stationTimeZone: String
});
</script>

View File

@ -1,29 +1,33 @@
<template>
<section id="content" role="main" style="height: 100vh;">
<section
id="content"
role="main"
style="height: 100vh;"
>
<div class="container pt-5">
<div class="form-row">
<div class="col-md-4 mb-sm-4">
<settings-panel v-bind="{ stationName, baseUri, libUrls }"></settings-panel>
<settings-panel v-bind="{ stationName, baseUri, libUrls }" />
</div>
<div class="col-md-8">
<div class="form-row mb-3">
<div class="col-md-12">
<microphone-panel></microphone-panel>
<microphone-panel />
</div>
</div>
<div class="form-row mb-3">
<div class="col-md-12">
<mixer-panel></mixer-panel>
<mixer-panel />
</div>
</div>
<div class="form-row mb-4">
<div class="col-md-6 mb-sm-4">
<playlist-panel id="playlist_1"></playlist-panel>
<playlist-panel id="playlist_1" />
</div>
<div class="col-md-6">
<playlist-panel id="playlist_2"></playlist-panel>
<playlist-panel id="playlist_2" />
</div>
</div>
</div>
@ -41,26 +45,26 @@ import SettingsPanel from './WebDJ/SettingsPanel.vue';
import Stream from './WebDJ/Stream.js';
export default {
data: function () {
return {
'stream': Stream
};
},
components: {
MixerPanel,
MicrophonePanel,
PlaylistPanel,
SettingsPanel
},
provide: function () {
return {
getStream: this.getStream,
resumeStream: this.resumeStream
};
},
props: {
stationName: String,
libUrls: Array,
baseUri: String
},
provide: function () {
data: function () {
return {
getStream: this.getStream,
resumeStream: this.resumeStream
'stream': Stream
};
},
methods: {

View File

@ -8,7 +8,7 @@
</h5>
</div>
<div class="flex-shrink-0 pl-3">
<volume-slider v-model.number="volume"></volume-slider>
<volume-slider v-model.number="volume" />
</div>
</div>
</div>
@ -18,11 +18,18 @@
<div class="d-flex-shrink-0">
<div class="control-group">
<div class="btn-group btn-group-sm">
<button class="btn btn-danger" v-on:click="toggleRecording"
v-bind:class="{ active: playing }">
<icon icon="mic"></icon>
<button
class="btn btn-danger"
:class="{ active: playing }"
@click="toggleRecording"
>
<icon icon="mic" />
</button>
<button class="btn" v-on:click="cue" v-bind:class="{ 'btn-primary': passThrough }">
<button
class="btn"
:class="{ 'btn-primary': passThrough }"
@click="cue"
>
{{ $gettext('Cue') }}
</button>
</div>
@ -30,12 +37,22 @@
</div>
<div class="flex-fill pl-3">
<div class="form-group microphone-entry mb-0">
<label for="select_microphone_source" class="mb-2">
<label
for="select_microphone_source"
class="mb-2"
>
{{ $gettext('Microphone Source') }}
</label>
<div class="controls">
<select id="select_microphone_source" v-model="device" class="form-control">
<option v-for="device_row in devices" v-bind:value="device_row.deviceId">
<select
id="select_microphone_source"
v-model="device"
class="form-control"
>
<option
v-for="device_row in devices"
:value="device_row.deviceId"
>
{{ device_row.label }}
</option>
</select>
@ -44,12 +61,21 @@
</div>
</div>
<div v-if="playing" class="mt-3">
<div
v-if="playing"
class="mt-3"
>
<div class="progress mb-1">
<div class="progress-bar" v-bind:style="{ width: volumeLeft+'%' }"></div>
<div
class="progress-bar"
:style="{ width: volumeLeft+'%' }"
/>
</div>
<div class="progress mb-2">
<div class="progress-bar" v-bind:style="{ width: volumeRight+'%' }"></div>
<div
class="progress-bar"
:style="{ width: volumeRight+'%' }"
/>
</div>
</div>
</div>

View File

@ -13,9 +13,16 @@
{{ $gettext('Playlist 1') }}
</div>
<div class="flex-fill px-2">
<input type="range" min="0" max="1" step="0.01" class="custom-range slider"
v-model="position" @click.right.prevent="position = 0.5"
style="width: 200px; height: 10px;">
<input
v-model="position"
type="range"
min="0"
max="1"
step="0.01"
class="custom-range slider"
style="width: 200px; height: 10px;"
@click.right.prevent="position = 0.5"
>
</div>
<div class="flex-shrink-0">
{{ $gettext('Playlist 2') }}

View File

@ -3,64 +3,114 @@
<div class="card-header bg-primary-dark">
<div class="d-flex align-items-center">
<div class="flex-fill text-nowrap">
<h5 class="card-title">{{ lang_header }}</h5>
<h5 class="card-title">
{{ lang_header }}
</h5>
</div>
<div class="flex-shrink-0 pl-3">
<volume-slider v-model.number="volume"></volume-slider>
<volume-slider v-model.number="volume" />
</div>
</div>
</div>
<div class="card-body">
<div class="control-group d-flex justify-content-center">
<div class="btn-group btn-group-sm">
<button class="btn btn-sm btn-success" v-if="!playing || paused" v-on:click="play">
<icon icon="play_arrow"></icon>
<button
v-if="!playing || paused"
class="btn btn-sm btn-success"
@click="play"
>
<icon icon="play_arrow" />
</button>
<button class="btn btn-sm btn-warning" v-if="playing && !paused" v-on:click="togglePause()">
<icon icon="pause"></icon>
<button
v-if="playing && !paused"
class="btn btn-sm btn-warning"
@click="togglePause()"
>
<icon icon="pause" />
</button>
<button class="btn btn-sm" v-on:click="previous()">
<icon icon="fast_rewind"></icon>
<button
class="btn btn-sm"
@click="previous()"
>
<icon icon="fast_rewind" />
</button>
<button class="btn btn-sm" v-on:click="next()">
<icon icon="fast_forward"></icon>
<button
class="btn btn-sm"
@click="next()"
>
<icon icon="fast_forward" />
</button>
<button class="btn btn-sm btn-danger" v-on:click="stop()">
<icon icon="stop"></icon>
<button
class="btn btn-sm btn-danger"
@click="stop()"
>
<icon icon="stop" />
</button>
<button class="btn btn-sm" v-on:click="cue()" v-bind:class="{ 'btn-primary': passThrough }">
<button
class="btn btn-sm"
:class="{ 'btn-primary': passThrough }"
@click="cue()"
>
{{ $gettext('Cue') }}
</button>
</div>
</div>
<div class="mt-3" v-if="playing">
<div
v-if="playing"
class="mt-3"
>
<div class="d-flex flex-row mb-2">
<div class="flex-shrink-0 pt-1 pr-2">{{ prettifyTime(position) }}</div>
<div class="flex-fill">
<input type="range" min="0" max="100" step="0.1" class="custom-range slider"
v-bind:value="seekingPosition"
v-on:mousedown="isSeeking = true"
v-on:mousemove="doSeek($event)"
v-on:mouseup="isSeeking = false">
<div class="flex-shrink-0 pt-1 pr-2">
{{ prettifyTime(position) }}
</div>
<div class="flex-fill">
<input
type="range"
min="0"
max="100"
step="0.1"
class="custom-range slider"
:value="seekingPosition"
@mousedown="isSeeking = true"
@mousemove="doSeek($event)"
@mouseup="isSeeking = false"
>
</div>
<div class="flex-shrink-0 pt-1 pl-2">
{{ prettifyTime(duration) }}
</div>
<div class="flex-shrink-0 pt-1 pl-2">{{ prettifyTime(duration) }}</div>
</div>
<div class="progress mb-1">
<div class="progress-bar" v-bind:style="{ width: volumeLeft+'%' }"></div>
<div
class="progress-bar"
:style="{ width: volumeLeft+'%' }"
/>
</div>
<div class="progress">
<div class="progress-bar" v-bind:style="{ width: volumeRight+'%' }"></div>
<div
class="progress-bar"
:style="{ width: volumeRight+'%' }"
/>
</div>
</div>
<div class="form-group mt-2">
<div class="custom-file">
<input v-bind:id="id + '_files'" type="file" class="custom-file-input files" accept="audio/*"
multiple="multiple" v-on:change="addNewFiles($event.target.files)">
<label v-bind:for="id + '_files'" class="custom-file-label">
<input
:id="id + '_files'"
type="file"
class="custom-file-input files"
accept="audio/*"
multiple="multiple"
@change="addNewFiles($event.target.files)"
>
<label
:for="id + '_files'"
class="custom-file-label"
>
{{ $gettext('Add Files to Playlist') }}
</label>
</div>
@ -69,15 +119,30 @@
<div class="form-group mb-0">
<div class="controls">
<div class="custom-control custom-checkbox custom-control-inline">
<input v-bind:id="id + '_playthrough'" type="checkbox" class="custom-control-input"
v-model="playThrough">
<label v-bind:for="id + '_playthrough'" class="custom-control-label">
<input
:id="id + '_playthrough'"
v-model="playThrough"
type="checkbox"
class="custom-control-input"
>
<label
:for="id + '_playthrough'"
class="custom-control-label"
>
{{ $gettext('Continuous Play') }}
</label>
</div>
<div class="custom-control custom-checkbox custom-control-inline">
<input v-bind:id="id + '_loop'" type="checkbox" class="custom-control-input" v-model="loop">
<label v-bind:for="id + '_loop'" class="custom-control-label">
<input
:id="id + '_loop'"
v-model="loop"
type="checkbox"
class="custom-control-input"
>
<label
:for="id + '_loop'"
class="custom-control-label"
>
{{ $gettext('Repeat') }}
</label>
</div>
@ -85,14 +150,21 @@
</div>
</div>
<div class="list-group list-group-flush" v-if="files.length > 0">
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start"
v-for="(rowFile, rowIndex) in files" v-bind:class="{ active: rowIndex === fileIndex }"
v-on:click.prevent="play({ fileIndex: rowIndex })">
<div
v-if="files.length > 0"
class="list-group list-group-flush"
>
<a
v-for="(rowFile, rowIndex) in files"
href="#"
class="list-group-item list-group-item-action flex-column align-items-start"
:class="{ active: rowIndex === fileIndex }"
@click.prevent="play({ fileIndex: rowIndex })"
>
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-0">{{
rowFile.metadata.title ? rowFile.metadata.title : lang_unknown_title
}}</h5>
rowFile.metadata.title ? rowFile.metadata.title : lang_unknown_title
}}</h5>
<small class="pt-1">{{ prettifyTime(rowFile.audio.length) }}</small>
</div>
<p class="mb-0">{{ rowFile.metadata.artist ? rowFile.metadata.artist : lang_unknown_artist }}</p>
@ -110,6 +182,9 @@ import VolumeSlider from "~/components/Public/WebDJ/VolumeSlider";
export default {
components: {VolumeSlider, Icon},
extends: track,
props: {
id: String
},
data() {
return {
'fileIndex': -1,
@ -144,9 +219,6 @@ export default {
return (this.isSeeking) ? this.seekPosition : this.positionPercent;
}
},
props: {
id: String
},
mounted () {
this.mixGainObj = this.getStream().context.createGain();
this.mixGainObj.connect(this.getStream().webcast);

View File

@ -12,12 +12,20 @@
<div class="col-sm-12">
<ul class="nav nav-tabs card-header-tabs mt-0">
<li class="nav-item">
<a class="nav-link active" href="#settings" data-toggle="tab">
<a
class="nav-link active"
href="#settings"
data-toggle="tab"
>
{{ $gettext('Settings') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#metadata" data-toggle="tab">
<a
class="nav-link"
href="#metadata"
data-toggle="tab"
>
{{ $gettext('Metadata') }}
</a>
</li>
@ -27,70 +35,157 @@
<div class="form-row">
<div class="col-sm-12">
<div class="tab-content mt-1">
<div class="tab-pane active" id="settings">
<div
id="settings"
class="tab-pane active"
>
<div class="form-group">
<label class="mb-2">
{{ $gettext('Encoder') }}
</label>
<div class="controls">
<div class="custom-control custom-radio custom-control-inline">
<input id="encoder_mp3" type="radio" v-model="encoder" value="mp3"
class="custom-control-input">
<label for="encoder_mp3" class="custom-control-label">
<input
id="encoder_mp3"
v-model="encoder"
type="radio"
value="mp3"
class="custom-control-input"
>
<label
for="encoder_mp3"
class="custom-control-label"
>
{{ $gettext('MP3') }}
</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input id="encoder_raw" type="radio" v-model="encoder" value="raw"
class="custom-control-input">
<label for="encoder_raw" class="custom-control-label">
<input
id="encoder_raw"
v-model="encoder"
type="radio"
value="raw"
class="custom-control-input"
>
<label
for="encoder_raw"
class="custom-control-label"
>
{{ $gettext('Raw') }}
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="select_samplerate" class="mb-2">
<label
for="select_samplerate"
class="mb-2"
>
{{ $gettext('Sample Rate') }}
</label>
<div class="controls">
<select id="select_samplerate" class="form-control" v-model.number="samplerate">
<option value="8000">8 kHz</option>
<option value="11025">11.025 kHz</option>
<option value="12000">12 kHz</option>
<option value="16000">16 kHz</option>
<option value="22050">22.05 kHz</option>
<option value="24000">24 kHz</option>
<option value="32000">32 kHz</option>
<option value="44100">44.1 kHz</option>
<option value="48000">48 kHz</option>
<select
id="select_samplerate"
v-model.number="samplerate"
class="form-control"
>
<option value="8000">
8 kHz
</option>
<option value="11025">
11.025 kHz
</option>
<option value="12000">
12 kHz
</option>
<option value="16000">
16 kHz
</option>
<option value="22050">
22.05 kHz
</option>
<option value="24000">
24 kHz
</option>
<option value="32000">
32 kHz
</option>
<option value="44100">
44.1 kHz
</option>
<option value="48000">
48 kHz
</option>
</select>
</div>
</div>
<div class="form-group">
<label for="select_bitrate" class="mb-2">
<label
for="select_bitrate"
class="mb-2"
>
{{ $gettext('Bit Rate') }}
</label>
<div class="controls">
<select id="select_bitrate" class="form-control" v-model.number="bitrate">
<option value="8">8 kbps</option>
<option value="16">16 kbps</option>
<option value="24">24 kbps</option>
<option value="32">32 kbps</option>
<option value="40">40 kbps</option>
<option value="48">48 kbps</option>
<option value="56">56 kbps</option>
<option value="64">64 kbps</option>
<option value="80">80 kbps</option>
<option value="96">96 kbps</option>
<option value="112">112 kbps</option>
<option value="128">128 kbps</option>
<option value="144">144 kbps</option>
<option value="160">160 kbps</option>
<option value="192">192 kbps</option>
<option value="224">224 kbps</option>
<option value="256">256 kbps</option>
<option value="320">320 kbps</option>
<select
id="select_bitrate"
v-model.number="bitrate"
class="form-control"
>
<option value="8">
8 kbps
</option>
<option value="16">
16 kbps
</option>
<option value="24">
24 kbps
</option>
<option value="32">
32 kbps
</option>
<option value="40">
40 kbps
</option>
<option value="48">
48 kbps
</option>
<option value="56">
56 kbps
</option>
<option value="64">
64 kbps
</option>
<option value="80">
80 kbps
</option>
<option value="96">
96 kbps
</option>
<option value="112">
112 kbps
</option>
<option value="128">
128 kbps
</option>
<option value="144">
144 kbps
</option>
<option value="160">
160 kbps
</option>
<option value="192">
192 kbps
</option>
<option value="224">
224 kbps
</option>
<option value="256">
256 kbps
</option>
<option value="320">
320 kbps
</option>
</select>
</div>
</div>
@ -101,47 +196,84 @@
<div class="form-row">
<div class="col-6">
<input type="text" v-model="djUsername" class="form-control"
v-bind:placeholder="langDjUsername">
<input
v-model="djUsername"
type="text"
class="form-control"
:placeholder="langDjUsername"
>
</div>
<div class="col-6">
<input type="password" v-model="djPassword" class="form-control"
v-bind:placeholder="langDjPassword">
<input
v-model="djPassword"
type="password"
class="form-control"
:placeholder="langDjPassword"
>
</div>
</div>
</div>
<div class="form-group mb-0">
<div class="custom-control custom-checkbox">
<input id="use_async_worker" type="checkbox" v-model="asynchronous"
class="custom-control-input">
<label for="use_async_worker" class="custom-control-label">
<input
id="use_async_worker"
v-model="asynchronous"
type="checkbox"
class="custom-control-input"
>
<label
for="use_async_worker"
class="custom-control-label"
>
{{ $gettext('Use Asynchronous Worker') }}
</label>
</div>
</div>
</div>
<div class="tab-pane" id="metadata">
<div
id="metadata"
class="tab-pane"
>
<div class="form-group">
<label for="metadata_title" class="mb-2">
<label
for="metadata_title"
class="mb-2"
>
{{ $gettext('Title') }}
</label>
<div class="controls">
<input id="metadata_title" class="form-control" type="text" v-model="metadata.title"
v-bind:disabled="!isStreaming">
<input
id="metadata_title"
v-model="metadata.title"
class="form-control"
type="text"
:disabled="!isStreaming"
>
</div>
</div>
<div class="form-group">
<label for="metadata_artist" class="mb-2">
<label
for="metadata_artist"
class="mb-2"
>
{{ $gettext('Artist') }}
</label>
<div class="controls">
<input id="metadata_artist" class="form-control" type="text"
v-model="metadata.artist" v-bind:disabled="!isStreaming">
<input
id="metadata_artist"
v-model="metadata.artist"
class="form-control"
type="text"
:disabled="!isStreaming"
>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary" v-on:click="updateMetadata"
v-bind:disabled="!isStreaming">
<button
class="btn btn-primary"
:disabled="!isStreaming"
@click="updateMetadata"
>
{{ $gettext('Update Metadata') }}
</button>
</div>
@ -152,13 +284,25 @@
</div>
<div class="card-actions">
<button class="btn btn-success" v-on:click="startStreaming" v-if="!isStreaming">
<button
v-if="!isStreaming"
class="btn btn-success"
@click="startStreaming"
>
{{ langStreamButton }}
</button>
<button class="btn btn-danger" v-on:click="stopStreaming" v-if="isStreaming">
<button
v-if="isStreaming"
class="btn btn-danger"
@click="stopStreaming"
>
{{ langStreamButton }}
</button>
<button class="btn" v-on:click="cue" v-bind:class="{ 'btn-primary': passThrough }">
<button
class="btn"
:class="{ 'btn-primary': passThrough }"
@click="cue"
>
{{ $gettext('Cue') }}
</button>
</div>
@ -168,6 +312,11 @@
<script>
export default {
inject: ['getStream', 'resumeStream'],
props: {
stationName: String,
libUrls: Array,
baseUri: String
},
data () {
return {
'isStreaming': false,
@ -200,11 +349,6 @@ export default {
return 'wss://' + this.djUsername + ':' + this.djPassword + '@' + this.baseUri;
}
},
props: {
stationName: String,
libUrls: Array,
baseUri: String
},
mounted () {
this.$root.$on('new-cue', this.onNewCue);
this.$root.$on('metadata-update', this.onMetadataUpdate);

View File

@ -1,14 +1,21 @@
<template>
<div class="d-flex flex-row align-items-center">
<div class="flex-shrink-0">
<icon icon="volume_mute"></icon>
<icon icon="volume_mute" />
</div>
<div class="flex-fill px-2">
<input type="range" min="0" max="100" class="custom-range slider"
v-model.number="volume" @click.right.prevent="reset" style="height: 10px; width: 100px;">
<input
v-model.number="volume"
type="range"
min="0"
max="100"
class="custom-range slider"
style="height: 10px; width: 100px;"
@click.right.prevent="reset"
>
</div>
<div class="flex-shrink-0">
<icon icon="volume_up"></icon>
<icon icon="volume_up" />
</div>
</div>
</template>

View File

@ -13,21 +13,49 @@
</h3>
</div>
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<b-alert
variant="danger"
:show="error != null"
>
{{ error }}
</b-alert>
<form id="recover-form" class="form vue-form" action="" method="post">
<input type="hidden" name="csrf" :value="csrf"/>
<form
id="recover-form"
class="form vue-form"
action=""
method="post"
>
<input
type="hidden"
name="csrf"
:value="csrf"
>
<b-wrapped-form-group id="password" name="password" label-class="mb-2" :field="v$.password"
input-type="password">
<b-wrapped-form-group
id="password"
name="password"
label-class="mb-2"
:field="v$.password"
input-type="password"
>
<template #label>
<icon icon="vpn_key" class="mr-1"></icon>
<icon
icon="vpn_key"
class="mr-1"
/>
{{ $gettext('Password') }}
</template>
</b-wrapped-form-group>
<b-button type="submit" size="lg" block variant="primary" :disabled="v$.$invalid"
class="mt-2">
<b-button
type="submit"
size="lg"
block
variant="primary"
:disabled="v$.$invalid"
class="mt-2"
>
{{ $gettext('Recover Account') }}
</b-button>
</form>

Some files were not shown because too many files have changed in this diff Show More