1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-02 16:08:31 +00:00
* Added: Audio Compression for the Twitch Player. Make loud sounds quieter and quiet sounds louder for a better listening experience.
* Fixed: Player modifications not applying on the Stream Manager and Squad Stream pages.
This commit is contained in:
SirStendec 2020-01-27 15:14:10 -05:00
parent 2d14c359c9
commit cc36905170
15 changed files with 352 additions and 20 deletions

View file

@ -675,6 +675,34 @@
"css": "fast-fw",
"code": 59453,
"src": "fontawesome"
},
{
"uid": "b31ef806be114b4d6fc6c40bafef5be9",
"css": "comp-on",
"code": 59454,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M850 200C877.7 200 900 222.3 900 250V750C900 777.7 877.7 800 850 800S800 777.7 800 750V250C800 222.3 822.3 200 850 200ZM570 250C597.7 250 620 272.3 620 300V700C620 727.7 597.7 750 570 750S520 727.7 520 700V300C520 272.3 542.3 250 570 250ZM710 225C737.7 225 760 247.3 760 275V725C760 752.7 737.7 775 710 775S660 752.7 660 725V275C660 247.3 682.3 225 710 225ZM430 250C457.7 250 480 272.3 480 300V700C480 727.7 457.7 750 430 750S380 727.7 380 700V300C380 272.3 402.3 250 430 250ZM290 225C317.7 225 340 247.3 340 275V725C340 752.7 317.7 775 290 775S240 752.7 240 725V275C240 247.3 262.3 225 290 225ZM150 200C177.7 200 200 222.3 200 250V750C200 777.7 177.7 800 150 800S100 777.7 100 750V250C100 222.3 122.3 200 150 200Z",
"width": 1000
},
"search": [
"eq-on-mini"
]
},
{
"uid": "924b188520c6928119ea87f6d11fa43d",
"css": "comp-off",
"code": 59455,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M850 202.3C877.7 202.3 900 224.6 900 252.3V745.5C900 773.2 877.7 795.5 850 795.5S800 773.2 800 745.5V252.3C800 224.6 822.3 202.3 850 202.3ZM570 167.8C597.7 167.8 620 190.1 620 217.8V780C620 807.7 597.7 830 570 830S520 807.7 520 780V217.8C520 190.1 542.3 167.8 570 167.8ZM710 264.4C737.7 264.4 760 286.7 760 314.4V683.3C760 711 737.7 733.3 710 733.3S660 711 660 683.3V314.4C660 286.7 682.3 264.4 710 264.4ZM430 98.1C457.7 98.1 480 120.4 480 148.1V849.6C480 877.3 457.7 899.6 430 899.6S380 877.3 380 849.6V148.1C380 120.4 402.3 98.1 430 98.1ZM290 217.2C317.7 217.2 340 239.5 340 267.2V730.5C340 758.2 317.7 780.5 290 780.5S240 758.2 240 730.5V267.2C240 239.5 262.3 217.2 290 217.2ZM150 299.6C177.7 299.6 200 321.9 200 349.6V648.1C200 675.8 177.7 698.1 150 698.1S100 675.8 100 648.1V349.6C100 321.9 122.3 299.6 150 299.6Z",
"width": 1000
},
"search": [
"eq-off-mini"
]
}
]
}

View file

@ -1,7 +1,7 @@
{
"name": "frankerfacez",
"author": "Dan Salvato LLC",
"version": "4.17.15",
"version": "4.18.0",
"description": "FrankerFaceZ is a Twitch enhancement suite.",
"license": "Apache-2.0",
"scripts": {

Binary file not shown.

View file

@ -1,7 +1,7 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
<metadata>Copyright (C) 2020 by original authors @ fontello.com</metadata>
<defs>
<font id="ffz-fontello" horiz-adv-x="1000" >
<font-face font-family="ffz-fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
@ -130,6 +130,10 @@
<glyph glyph-name="fast-fw" unicode="&#xe83d;" d="M25-71q-10-11-18-8t-7 18v822q0 14 7 18t18-8l396-396q5-5 8-10v396q0 14 7 18t18-8l396-396q11-10 11-25t-11-25l-396-396q-11-11-18-8t-7 18v397q-3-6-8-11z" horiz-adv-x="928.6" />
<glyph glyph-name="comp-on" unicode="&#xe83e;" d="M850 650c28 0 50-22 50-50v-500c0-28-22-50-50-50s-50 22-50 50v500c0 28 22 50 50 50z m-280-50c28 0 50-22 50-50v-400c0-28-22-50-50-50s-50 22-50 50v400c0 28 22 50 50 50z m140 25c28 0 50-22 50-50v-450c0-28-22-50-50-50s-50 22-50 50v450c0 28 22 50 50 50z m-280-25c28 0 50-22 50-50v-400c0-28-22-50-50-50s-50 22-50 50v400c0 28 22 50 50 50z m-140 25c28 0 50-22 50-50v-450c0-28-22-50-50-50s-50 22-50 50v450c0 28 22 50 50 50z m-140 25c28 0 50-22 50-50v-500c0-28-22-50-50-50s-50 22-50 50v500c0 28 22 50 50 50z" horiz-adv-x="1000" />
<glyph glyph-name="comp-off" unicode="&#xe83f;" d="M850 648c28 0 50-23 50-50v-493c0-28-22-50-50-50s-50 22-50 50v493c0 27 22 50 50 50z m-280 34c28 0 50-22 50-50v-562c0-28-22-50-50-50s-50 22-50 50v562c0 28 22 50 50 50z m140-96c28 0 50-23 50-50v-369c0-28-22-50-50-50s-50 22-50 50v369c0 27 22 50 50 50z m-280 166c28 0 50-22 50-50v-702c0-27-22-50-50-50s-50 23-50 50v702c0 28 22 50 50 50z m-140-119c28 0 50-22 50-50v-463c0-28-22-50-50-50s-50 22-50 50v463c0 28 22 50 50 50z m-140-83c28 0 50-22 50-50v-298c0-28-22-50-50-50s-50 22-50 50v298c0 28 22 50 50 50z" horiz-adv-x="1000" />
<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
<glyph glyph-name="twitter" unicode="&#xf099;" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -7,7 +7,12 @@
import Module from 'utilities/module';
import {createElement, on, off} from 'utilities/dom';
export const PLAYER_ROUTES = ['front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following', 'dash'];
export const PLAYER_ROUTES = [
'front-page', 'user', 'video', 'user-video', 'user-clip', 'user-videos',
'user-clips', 'user-collections', 'user-events', 'user-followers',
'user-following', 'dash', 'squad', 'command-center', 'dash-stream-manager'];
const HAS_COMPRESSOR = window.AudioContext && window.DynamicsCompressorNode != null;
const STYLE_VALIDATOR = createElement('span');
@ -73,6 +78,137 @@ export default class Player extends Module {
// Settings
if ( HAS_COMPRESSOR ) {
this.settings.add('player.compressor.enable', {
default: true,
ui: {
path: 'Player > Compressor @{"description": "These settings control optional dynamic range compression for the player, a form of audio processing that reduces the volume of loud sounds and amplifies quiet sounds, thus normalizing or compressing the volume."} >> General',
title: 'Enable the audio compressor and add an `Audio Compressor` button to the player controls.',
sort: -1000,
component: 'setting-check-box'
},
changed: () => {
for(const inst of this.Player.instances)
this.addCompressorButton(inst);
}
});
this.settings.add('player.compressor.default', {
default: false,
ui: {
path: 'Player > Compressor >> General',
title: 'Enable the compressor by default.',
component: 'setting-check-box'
},
changed: () => {
for(const inst of this.Player.instances)
this.compressPlayer(inst);
}
});
this.settings.add('player.compressor.threshold', {
default: -50,
ui: {
path: 'Player > Compressor >> Advanced @{"sort": 1000}',
title: 'Threshold',
sort: 0,
description: 'Range: -100 ~ 0',
component: 'setting-text-box',
process(val) {
val = parseInt(val, 10);
if ( isNaN(val) || ! isFinite(val) || val > 0 || val < -100 )
return -50;
return val;
}
},
changed: () => this.updateCompressors()
});
this.settings.add('player.compressor.knee', {
default: 40,
ui: {
path: 'Player > Compressor >> Advanced',
title: 'Knee',
sort: 5,
description: 'Range: 0 ~ 40',
component: 'setting-text-box',
process(val) {
val = parseInt(val, 10);
if ( isNaN(val) || ! isFinite(val) || val < 0 || val > 40 )
return 40;
return val;
}
},
changed: () => this.updateCompressors()
});
this.settings.add('player.compressor.ratio', {
default: 12,
ui: {
path: 'Player > Compressor >> Advanced',
title: 'Ratio',
sort: 10,
description: 'Range: 0 ~ 20',
component: 'setting-text-box',
process(val) {
val = parseInt(val, 10);
if ( isNaN(val) || ! isFinite(val) || val < 1 || val > 20 )
return 12;
return val;
}
},
changed: () => this.updateCompressors()
});
this.settings.add('player.compressor.attack', {
default: 0,
ui: {
path: 'Player > Compressor >> Advanced',
title: 'Attack',
sort: 15,
description: 'Range: 0 ~ 1',
component: 'setting-text-box',
process(val) {
val = parseFloat(val);
if ( isNaN(val) || ! isFinite(val) || val < 0 || val > 1 )
return 0;
return val;
}
},
changed: () => this.updateCompressors()
});
this.settings.add('player.compressor.release', {
default: 0.25,
ui: {
path: 'Player > Compressor >> Advanced',
title: 'Release',
sort: 20,
description: 'Range: 0 ~ 1',
component: 'setting-text-box',
process(val) {
val = parseFloat(val);
if ( isNaN(val) || ! isFinite(val) || val < 0 || val > 1 )
return 0.25;
return val;
}
},
changed: () => this.updateCompressors()
});
}
this.settings.add('player.allow-catchup', {
default: true,
ui: {
@ -631,11 +767,18 @@ export default class Player extends Module {
}
this.updateGUI(inst);
this.compressPlayer(inst);
}
});
this.Player.on('mount', this.updateGUI, this);
this.Player.on('update', this.updateGUI, this);
this.Player.on('mount', inst => {
this.updateGUI(inst);
this.compressPlayer(inst);
});
this.Player.on('update', inst => {
this.updateGUI(inst);
this.compressPlayer(inst);
});
this.Player.on('unmount', inst => {
inst.ffzUninstall();
@ -797,6 +940,7 @@ export default class Player extends Module {
updateGUI(inst) {
this.addPiPButton(inst);
this.addResetButton(inst);
this.addCompressorButton(inst);
const player = inst?.props?.mediaPlayerInstance;
if ( player && ! this.settings.get('player.allow-catchup') ) {
@ -806,6 +950,150 @@ export default class Player extends Module {
}
addCompressorButton(inst, tries = 0) {
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video,
container = outer && outer.querySelector('.player-controls__left-control-group'),
has_comp = HAS_COMPRESSOR && video != null && this.settings.get('player.compressor.enable');
if ( ! container ) {
if ( ! has_comp )
return;
if ( tries < 5 )
return setTimeout(this.addCompressorButton.bind(this, inst, (tries || 0) + 1), 250);
return;
}
let icon, tip, btn, cont = container.querySelector('.ffz--player-comp');
if ( ! has_comp ) {
if ( cont )
cont.remove();
return;
}
if ( ! cont ) {
cont = (<div class="ffz--player-comp tw-inline-flex tw-relative tw-tooltip-wrapper">
{btn = (<button
class="tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon tw-button-icon--overlay tw-core-button tw-core-button--border tw-core-button--overlay tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative"
type="button"
data-a-target="ffz-player-comp-button"
onClick={this.compressPlayer.bind(this, inst)} // eslint-disable-line react/jsx-no-bind
>
<div class="tw-align-items-center tw-flex tw-flex-grow-0">
<div class="tw-button-icon__icon">
{icon = (<figure class="ffz-player-icon" />)}
</div>
</div>
</button>)}
{tip = (<div class="tw-tooltip tw-tooltip--align-right tw-tooltip--up" role="tooltip" />)}
</div>);
container.appendChild(cont);
} else {
icon = cont.querySelector('figure');
btn = cont.querySelector('button');
tip = cont.querySelector('.tw-tooltip');
}
const comp_active = video._ffz_compressed,
label = comp_active ?
this.i18n.t('player.comp_button.off', 'Disable Audio Compressor') :
this.i18n.t('player.comp_button.on', 'Enable Audio Compressor');
icon.classList.toggle('ffz-i-comp-on', comp_active);
icon.classList.toggle('ffz-i-comp-off', ! comp_active);
btn.setAttribute('aria-label', label);
tip.textContent = label;
}
compressPlayer(inst, e) {
const video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video;
if ( ! video || ! HAS_COMPRESSOR )
return;
const compressed = video._ffz_compressed || false;
let wanted = this.settings.get('player.compressor.default');
if ( e != null ) {
video._ffz_toggled = true;
e.preventDefault();
wanted = ! video._ffz_compressed;
} else if ( video._ffz_toggled )
return;
if ( wanted == compressed )
return;
if ( ! video._ffz_compressor ) {
if ( ! wanted )
return;
this.createCompressor(video);
}
const ctx = video._ffz_context,
comp = video._ffz_compressor,
src = video._ffz_source;
if ( ! ctx || ! comp || ! src )
return;
if ( wanted ) {
src.disconnect(ctx.destination);
src.connect(comp);
comp.connect(ctx.destination);
} else {
src.disconnect(comp);
comp.disconnect(ctx.destination);
src.connect(ctx.destination);
}
video._ffz_compressed = wanted;
this.addCompressorButton(inst);
}
createCompressor(video) {
if ( ! HAS_COMPRESSOR )
return;
let comp = video._ffz_compressor;
if ( ! comp ) {
const ctx = video._ffz_context = new AudioContext(),
src = video._ffz_source = ctx.createMediaElementSource(video);
src.connect(ctx.destination);
comp = video._ffz_compressor = ctx.createDynamicsCompressor();
video._ffz_compressed = false;
}
this.updateCompressor(null, comp);
}
updateCompressors() {
for(const inst of this.Player.instances)
this.updateCompressor(inst);
}
updateCompressor(inst, comp) {
if ( comp == null ) {
const video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video;
comp = video?._ffz_compressor;
}
if ( ! comp )
return;
comp.threshold.value = this.settings.get('player.compressor.threshold');
comp.knee.value = this.settings.get('player.compressor.knee');
comp.ratio.value = this.settings.get('player.compressor.ratio');
comp.attack.value = this.settings.get('player.compressor.attack');
comp.release.value = this.settings.get('player.compressor.release');
}
addPiPButton(inst, tries = 0) {
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video,

View file

@ -91,5 +91,7 @@ export default [
"whispers",
"cake",
"channel-points",
"fast-fw"
"fast-fw",
"comp-on",
"comp-off"
];

View file

@ -61,6 +61,8 @@
.ffz-i-whispers:before { content: '\e83b'; } /* '' */
.ffz-i-channel-points:before { content: '\e83c'; } /* '' */
.ffz-i-fast-fw:before { content: '\e83d'; } /* '' */
.ffz-i-comp-on:before { content: '\e83e'; } /* '' */
.ffz-i-comp-off:before { content: '\e83f'; } /* '' */
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
.ffz-i-twitter:before { content: '\f099'; } /* '' */
.ffz-i-github:before { content: '\f09b'; } /* '' */

File diff suppressed because one or more lines are too long

View file

@ -61,6 +61,8 @@
.ffz-i-whispers { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83b;&nbsp;'); }
.ffz-i-channel-points { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83c;&nbsp;'); }
.ffz-i-fast-fw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83d;&nbsp;'); }
.ffz-i-comp-on { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83e;&nbsp;'); }
.ffz-i-comp-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83f;&nbsp;'); }
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf099;&nbsp;'); }
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf09b;&nbsp;'); }

View file

@ -72,6 +72,8 @@
.ffz-i-whispers { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83b;&nbsp;'); }
.ffz-i-channel-points { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83c;&nbsp;'); }
.ffz-i-fast-fw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83d;&nbsp;'); }
.ffz-i-comp-on { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83e;&nbsp;'); }
.ffz-i-comp-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe83f;&nbsp;'); }
.ffz-i-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
.ffz-i-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf099;&nbsp;'); }
.ffz-i-github { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf09b;&nbsp;'); }

View file

@ -1,11 +1,11 @@
@font-face {
font-family: 'ffz-fontello';
src: url('../font/ffz-fontello.eot?37997143');
src: url('../font/ffz-fontello.eot?37997143#iefix') format('embedded-opentype'),
url('../font/ffz-fontello.woff2?37997143') format('woff2'),
url('../font/ffz-fontello.woff?37997143') format('woff'),
url('../font/ffz-fontello.ttf?37997143') format('truetype'),
url('../font/ffz-fontello.svg?37997143#ffz-fontello') format('svg');
src: url('../font/ffz-fontello.eot?47364399');
src: url('../font/ffz-fontello.eot?47364399#iefix') format('embedded-opentype'),
url('../font/ffz-fontello.woff2?47364399') format('woff2'),
url('../font/ffz-fontello.woff?47364399') format('woff'),
url('../font/ffz-fontello.ttf?47364399') format('truetype'),
url('../font/ffz-fontello.svg?47364399#ffz-fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@ -15,7 +15,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'ffz-fontello';
src: url('../font/ffz-fontello.svg?37997143#ffz-fontello') format('svg');
src: url('../font/ffz-fontello.svg?47364399#ffz-fontello') format('svg');
}
}
*/
@ -117,6 +117,8 @@
.ffz-i-whispers:before { content: '\e83b'; } /* '' */
.ffz-i-channel-points:before { content: '\e83c'; } /* '' */
.ffz-i-fast-fw:before { content: '\e83d'; } /* '' */
.ffz-i-comp-on:before { content: '\e83e'; } /* '' */
.ffz-i-comp-off:before { content: '\e83f'; } /* '' */
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
.ffz-i-twitter:before { content: '\f099'; } /* '' */
.ffz-i-github:before { content: '\f09b'; } /* '' */