Added support within the UI to change a clients playback latency
This commit is contained in:
parent
259a317440
commit
cbad10f717
8 changed files with 286 additions and 0 deletions
84
shared/css/static/modal-latency.scss
Normal file
84
shared/css/static/modal-latency.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -256,6 +256,10 @@
|
||||||
.modal-body.modal-green {
|
.modal-body.modal-green {
|
||||||
border-left: 2px solid #00d400;
|
border-left: 2px solid #00d400;
|
||||||
}
|
}
|
||||||
|
.modal-body.modal-red {
|
||||||
|
border: none;
|
||||||
|
border-left: 2px solid #d50000;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-body.modal-body-input {
|
.modal-body.modal-body-input {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
|
|
|
@ -2586,6 +2586,48 @@
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</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 -->
|
<!-- Permission overview -->
|
||||||
<script class="jsrender-template" id="tmpl_server_permissions" type="text/html">
|
<script class="jsrender-template" id="tmpl_server_permissions" type="text/html">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
|
@ -55,6 +55,11 @@ namespace connection {
|
||||||
STOPPED
|
STOPPED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LatencySettings = {
|
||||||
|
min_buffer: number; /* milliseconds */
|
||||||
|
max_buffer: number; /* milliseconds */
|
||||||
|
}
|
||||||
|
|
||||||
export interface VoiceClient {
|
export interface VoiceClient {
|
||||||
client_id: number;
|
client_id: number;
|
||||||
|
|
||||||
|
@ -69,6 +74,11 @@ namespace connection {
|
||||||
set_volume(volume: number) : void;
|
set_volume(volume: number) : void;
|
||||||
|
|
||||||
abort_replay();
|
abort_replay();
|
||||||
|
|
||||||
|
support_latency_settings() : boolean;
|
||||||
|
|
||||||
|
reset_latency_settings();
|
||||||
|
latency_settings(settings?: LatencySettings) : LatencySettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AbstractVoiceConnection {
|
export abstract class AbstractVoiceConnection {
|
||||||
|
|
|
@ -625,6 +625,17 @@ class ClientEntry {
|
||||||
//TODO: Update in info
|
//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,
|
type: contextmenu.MenuEntryType.ENTRY,
|
||||||
icon_class: "client-input_muted_local",
|
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(),
|
contextmenu.Entry.HR(),
|
||||||
{
|
{
|
||||||
name: tr("Delete bot"),
|
name: tr("Delete bot"),
|
||||||
|
|
110
shared/js/ui/modal/ModalChangeLatency.ts
Normal file
110
shared/js/ui/modal/ModalChangeLatency.ts
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -185,6 +185,7 @@ const loader_javascript = {
|
||||||
"js/ui/modal/ModalCreateChannel.js",
|
"js/ui/modal/ModalCreateChannel.js",
|
||||||
"js/ui/modal/ModalServerEdit.js",
|
"js/ui/modal/ModalServerEdit.js",
|
||||||
"js/ui/modal/ModalChangeVolume.js",
|
"js/ui/modal/ModalChangeVolume.js",
|
||||||
|
"js/ui/modal/ModalChangeLatency.js",
|
||||||
"js/ui/modal/ModalBanClient.js",
|
"js/ui/modal/ModalBanClient.js",
|
||||||
"js/ui/modal/ModalIconSelect.js",
|
"js/ui/modal/ModalIconSelect.js",
|
||||||
"js/ui/modal/ModalInvite.js",
|
"js/ui/modal/ModalInvite.js",
|
||||||
|
@ -343,6 +344,7 @@ const loader_style = {
|
||||||
"css/static/modal-channel.css",
|
"css/static/modal-channel.css",
|
||||||
"css/static/modal-query.css",
|
"css/static/modal-query.css",
|
||||||
"css/static/modal-volume.css",
|
"css/static/modal-volume.css",
|
||||||
|
"css/static/modal-latency.css",
|
||||||
"css/static/modal-invite.css",
|
"css/static/modal-invite.css",
|
||||||
"css/static/modal-playlist.css",
|
"css/static/modal-playlist.css",
|
||||||
"css/static/modal-banlist.css",
|
"css/static/modal-banlist.css",
|
||||||
|
|
|
@ -211,6 +211,18 @@ namespace audio {
|
||||||
abort_replay() {
|
abort_replay() {
|
||||||
this.stopAudio(true);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue