Added support within the UI to change a clients playback latency

This commit is contained in:
WolverinDEV 2019-11-09 15:56:01 +01:00
parent 259a317440
commit cbad10f717
8 changed files with 286 additions and 0 deletions

View file

@ -0,0 +1,84 @@
@import "mixin";
@import "properties";
.modal-body.modal-latency {
@include user-select(none);
@include transform(all $button_hover_animation_time ease-in-out);
.info {
display: flex;
flex-direction: row;
justify-content: stretch;
.htmltag-client {
color: #999!important;
margin-left: .25em;
}
div {
display: flex;
flex-direction: row;
justify-content: stretch;
}
.value {
margin-left: .25em;
}
}
.container-min, .container-max {
display: flex;
flex-direction: row;
justify-content: stretch;
height: 2em;
.container-value {
display: flex;
flex-direction: row;
justify-content: space-between;
flex-shrink: 0;
flex-grow: 0;
margin-right: 1em;
width: 9em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
align-self: center;
.value {
}
}
}
.container-max {
padding-top: 1em;
}
.container-slider {
align-self: center;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: stretch;
margin-top: 2em;
.spacer {
flex-grow: 1;
flex-shrink: 1;
min-width: 0;
}
button:not(:last-of-type) {
margin-right: 1em;
}
}
}

View file

@ -256,6 +256,10 @@
.modal-body.modal-green {
border-left: 2px solid #00d400;
}
.modal-body.modal-red {
border: none;
border-left: 2px solid #d50000;
}
.modal-body.modal-body-input {
color: #999999;

View file

@ -2586,6 +2586,48 @@
</div>
</script>
<script class="jsrender-template" id="tmpl_change_latency" type="text/html">
<div> <!-- for the renderer -->
<div class="info">
<div>{{tr "Change latency for client "/}} <node key="client"></node></div>
</div>
<div class="container-min">
<div class="container-value">
{{tr "Min buffer: "/}}
<a class="value">20ms</a>
</div>
<div class="container-slider">
<div class="filler" style="width: 30%"></div>
<div class="thumb container-tooltip" style="left: 30%">
<div class="tooltip">
<a>86%</a>
</div>
</div>
</div>
</div>
<div class="container-max">
<div class="container-value">
{{tr "Max buffer: "/}}
<a class="value">20ms</a>
</div>
<div class="container-slider">
<div class="filler" style="width: 30%"></div>
<div class="thumb container-tooltip" style="left: 30%">
<div class="tooltip">
<a>86%</a>
</div>
</div>
</div>
</div>
<div class="buttons">
<button class="btn btn-blue button-reset">{{tr "Reset" /}}</button>
<div class="spacer"></div>
<button class="btn btn-danger button-cancel">{{tr "Cancel" /}}</button>
<button class="btn btn-success button-close">{{tr "Close" /}}</button>
</div>
</div>
</script>
<!-- Permission overview -->
<script class="jsrender-template" id="tmpl_server_permissions" type="text/html">
<div class="container">

View file

@ -55,6 +55,11 @@ namespace connection {
STOPPED
}
export type LatencySettings = {
min_buffer: number; /* milliseconds */
max_buffer: number; /* milliseconds */
}
export interface VoiceClient {
client_id: number;
@ -69,6 +74,11 @@ namespace connection {
set_volume(volume: number) : void;
abort_replay();
support_latency_settings() : boolean;
reset_latency_settings();
latency_settings(settings?: LatencySettings) : LatencySettings;
}
export abstract class AbstractVoiceConnection {

View file

@ -625,6 +625,17 @@ class ClientEntry {
//TODO: Update in info
});
}
},
{
type: contextmenu.MenuEntryType.ENTRY,
name: tr("Change playback latency"),
callback: () => {
Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
this._audio_handle.reset_latency_settings();
return this._audio_handle.latency_settings();
}, settings => this._audio_handle.latency_settings(settings));
},
visible: this._audio_handle && this._audio_handle.support_latency_settings()
}, {
type: contextmenu.MenuEntryType.ENTRY,
icon_class: "client-input_muted_local",
@ -1413,6 +1424,17 @@ class MusicClientEntry extends ClientEntry {
});
}
},
{
type: contextmenu.MenuEntryType.ENTRY,
name: tr("Change playback latency"),
callback: () => {
Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => {
this._audio_handle.reset_latency_settings();
return this._audio_handle.latency_settings();
}, settings => this._audio_handle.latency_settings(settings));
},
visible: this._audio_handle && this._audio_handle.support_latency_settings()
},
contextmenu.Entry.HR(),
{
name: tr("Delete bot"),

View file

@ -0,0 +1,110 @@
/// <reference path="../../ui/elements/modal.ts" />
/// <reference path="../../ConnectionHandler.ts" />
/// <reference path="../../proto.ts" />
namespace Modals {
let modal: Modal;
export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any) {
if(modal) modal.close();
const begin = Object.assign({}, current);
current = Object.assign({}, current);
modal = createModal({
header: tr("Change playback latency"),
body: function () {
let tag = $("#tmpl_change_latency").renderTag({
client: htmltags.generate_client_object({
add_braces: false,
client_name: client.clientNickName(),
client_unique_id: client.properties.client_unique_identifier,
client_id: client.clientId()
})
});
const update_value = () => {
const valid = current.min_buffer < current.max_buffer;
modal.htmlTag.find(".modal-body").toggleClass("modal-red", !valid);
modal.htmlTag.find(".modal-body").toggleClass("modal-green", valid);
if(!valid)
return;
apply(current);
};
let slider_min: Slider, slider_max: Slider;
{
const container = tag.find(".container-min");
const tag_value = container.find(".value");
const slider_tag = container.find(".container-slider");
slider_min = sliderfy(slider_tag, {
initial_value: current.min_buffer,
step: 20,
max_value: 1000,
min_value: 0,
unit: 'ms'
});
slider_tag.on('change', event => {
current.min_buffer = parseInt(slider_tag.attr("value"));
tag_value.text(current.min_buffer + "ms");
update_value();
});
tag_value.text(current.min_buffer + "ms");
}
{
const container = tag.find(".container-max");
const tag_value = container.find(".value");
const slider_tag = container.find(".container-slider");
slider_max = sliderfy(slider_tag, {
initial_value: current.max_buffer,
step: 20,
max_value: 1020,
min_value: 20,
unit: 'ms'
});
slider_tag.on('change', event => {
current.max_buffer = parseInt(slider_tag.attr("value"));
tag_value.text(current.max_buffer + "ms");
update_value();
});
tag_value.text(current.max_buffer + "ms");
}
setTimeout(update_value, 0);
tag.find(".button-close").on('click', event => {
modal.close();
});
tag.find(".button-cancel").on('click', event => {
apply(begin);
modal.close();
});
tag.find(".button-reset").on('click', event => {
current = Object.assign({}, reset());
slider_max.value(current.max_buffer);
slider_min.value(current.min_buffer);
});
return tag.children();
},
footer: null,
width: 600
});
modal.close_listener.push(() => modal = undefined);
modal.open();
modal.htmlTag.find(".modal-body").addClass("modal-latency");
}
}

View file

@ -185,6 +185,7 @@ const loader_javascript = {
"js/ui/modal/ModalCreateChannel.js",
"js/ui/modal/ModalServerEdit.js",
"js/ui/modal/ModalChangeVolume.js",
"js/ui/modal/ModalChangeLatency.js",
"js/ui/modal/ModalBanClient.js",
"js/ui/modal/ModalIconSelect.js",
"js/ui/modal/ModalInvite.js",
@ -343,6 +344,7 @@ const loader_style = {
"css/static/modal-channel.css",
"css/static/modal-query.css",
"css/static/modal-volume.css",
"css/static/modal-latency.css",
"css/static/modal-invite.css",
"css/static/modal-playlist.css",
"css/static/modal-banlist.css",

View file

@ -211,6 +211,18 @@ namespace audio {
abort_replay() {
this.stopAudio(true);
}
latency_settings(settings?: connection.voice.LatencySettings): connection.voice.LatencySettings {
throw "not supported";
}
reset_latency_settings() {
throw "not supported";
}
support_latency_settings(): boolean {
return false;
}
}
}
}