Skip to content

Commit

Permalink
feat: OBS audio volume widget (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 authored Mar 3, 2021
1 parent 3808fda commit 4662d73
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 7 deletions.
10 changes: 8 additions & 2 deletions app/server/libs/actions/types/obs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ function create(action) {
const actions = {
ToggleAudio(action) {
const { source } = action.widget.component.props;

return obs.send("ToggleMute", { source });
},
AudioVolume(action) {
const { source } = action.widget.component.props;
const { volume, mute } = action.data || action.eventProps || "undefined";
if (mute !== undefined) {
return obs.send("SetMute", { source, mute });
}
return obs.send("SetVolume", { source, volume, useDecibel: false });
},
ToggleScene(action) {
const { currentScene } = obs.getState();
const { scene1, scene2 } = action.widget.component.props;
const scene = currentScene === scene1 ? scene2 : scene1;

return obs.send("SetCurrentScene", { "scene-name": scene });
},
GoToScene(action) {
Expand Down
1 change: 1 addition & 0 deletions app/server/libs/obs.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let state = {
recording: false,
status: null,
scenes: null,
sources: null,
currentScene: null,
};

Expand Down
1 change: 1 addition & 0 deletions app/server/libs/twitch/pushActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const types = {
GoToScene: "obs",
ToggleScene: "obs",
ToggleAudio: "obs",
AudioVolume: "obs",
AnimeTimeline: "anime",
};

Expand Down
1 change: 1 addition & 0 deletions app/static/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"go-to-scene": "OBS | Go to scene",
"toggle-scene": "OBS | Toggle between two scenes",
"toggle-audio": "OBS | Toggle audio source",
"audio-volume": "OBS | Audio volume",
"no-scene-selected": "No scene selected",
"connect-at-startup": "Connect OBS at startup",
"first-start-install-sentence": "If you want to use OBS with Marv you need to install OBS WebSocket.",
Expand Down
1 change: 1 addition & 0 deletions app/static/locales/es/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"go-to-scene": "OBS | Ir a la escena",
"toggle-scene": "OBS | Cambiar entre dos escenas",
"toggle-audio": "OBS | Alternar fuente de audio",
"audio-volume": "OBS | Volumen de audio",
"no-scene-selected": "Ninguna escena seleccionada",
"connect-at-startup": "Conectar a OBS al inicio",
"first-start-install-sentence": "Para conectar Marv a OBS, debes instalar OBS WebSocket.",
Expand Down
1 change: 1 addition & 0 deletions app/static/locales/fr/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"go-to-scene": "OBS | Aller à la scène",
"toggle-scene": "OBS | Basculer entre deux scènes",
"toggle-audio": "OBS | Basculer source audio",
"audio-volume": "OBS | Volume audio",
"no-scene-selected": "Aucune scène sélectionnée",
"connect-at-startup": "Connecter OBS au démarrage",
"first-start-install-sentence": "Pour connecter Marv à OBS, tu dois installer OBS WebSocket.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script>
import { _ } from "@/libs/i18next";
import { state } from "@/stores/obs";
import { update } from "@/libs/panels";
import Select from "@/components/UI/Select.svelte";
export let data;
let sources = [];
let { panel, widget } = data;
let none = _("words.none");
$: if ($state.sources) {
sources = $state.sources.filter((source) => source.caps.hasAudio);
sources = [none, ...sources.map((source) => source.name)];
}
$: props = widget.component.props;
function onChange(key, { detail }) {
props[key] = detail === none ? null : detail;
update(panel);
}
</script>

<div class="p-2 pt-0 space-y-2 flex flex-auto flex-col">
<Select
items="{sources}"
value="{props.source}"
label="{_('words.source')}"
on:change="{onChange.bind(null, 'source')}"
/>
</div>
89 changes: 89 additions & 0 deletions front-src/client/components/Widgets/OBS/AudioVolume/Widget.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script>
import obs from "@/api/obs";
import { onMount } from "svelte";
import { _ } from "@/libs/i18next";
import actions from "@/api/actions";
import MdVolumeUp from "svelte-icons/md/MdVolumeUp.svelte";
import MdVolumeOff from "svelte-icons/md/MdVolumeOff.svelte";
import WidgetWrapper from "@/components/Widgets/OBS/WidgetWrapper.svelte";
export let widget;
let state = { volume: 0.5, muted: false };
$: source = widget.component.props.source;
$: icon = !state.muted ? MdVolumeUp : MdVolumeOff;
$: if (source) getVolume();
function getVolume() {
obs.emit("GetVolume", { source }).then((result) => (state = result));
}
function db(mul) {
return (Math.log10(mul) * 20).toFixed(1);
}
function sendAction(data) {
actions.push({ type: "obs", widget, data }).catch((error) => {
console.log(">>>Error:", error);
});
}
function onToggleAudio() {
sendAction({ mute: !state.muted });
}
function onVolumeChange({ target }) {
sendAction({ volume: Math.pow(parseFloat(target.value), 3) });
}
onMount(async () => {
obs.on(`connected`, () => {
getVolume();
});
obs.on(`source.volume`, ({ sourceName, volume }) => {
if (sourceName === source) {
state.volume = volume;
}
});
obs.on(`source.muted`, ({ sourceName, muted }) => {
if (sourceName === source) {
state.muted = muted;
}
});
});
</script>

<WidgetWrapper widget="{widget}">
<div class="flex flex-col h-full">
{#if source}
<div
class="flex flex-col w-full h-full {state.muted ? 'text-red-600' : ''}"
>
<div class="flex p-2 gap-2">
<div class="break-words flex-auto">{source}</div>
<div class="text-right">{db(state.volume)} dB</div>
</div>
<div class="flex p-2 gap-2 items-center">
<input
type="range"
min="{0}"
max="1"
step="0.01"
class="flex-auto"
value="{Math.pow(state.volume, 1 / 3)}"
on:input="{onVolumeChange}"
/>
<div class="w-8 h-8 flex-shrink-0" on:click="{onToggleAudio}">
<svelte:component this="{icon}" />
</div>
</div>
</div>
{:else}
<div class="p-2 bg-red-600">{_('obs.no-source-selected')}</div>
{/if}
</div>
</WidgetWrapper>
13 changes: 13 additions & 0 deletions front-src/client/components/Widgets/OBS/AudioVolume/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default {
name: "AudioVolume",
label: "obs.audio-volume",
hasTrigger: true,
hasEvent: true,
minSize: {
w: 4,
h: 3,
},
props: {
source: null,
},
};
3 changes: 3 additions & 0 deletions front-src/client/components/Widgets/OBS/AudioVolume/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as Settings } from "./Settings.svelte";
export { default as Widget } from "./Widget.svelte";
export { default as config } from "./config";
15 changes: 10 additions & 5 deletions front-src/client/components/Widgets/OBS/ToggleAudio/Widget.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
import { onMount } from "svelte";
import { _ } from "@/libs/i18next";
import actions from "@/api/actions";
import Icon from "@/components/UI/Icon.svelte";
import MdVolumeUp from "svelte-icons/md/MdVolumeUp.svelte";
import MdVolumeOff from "svelte-icons/md/MdVolumeOff.svelte";
import WidgetWrapper from "@/components/Widgets/OBS/WidgetWrapper.svelte";
export let widget;
let state = null;
let state = { volume: 0.5, muted: false };
$: source = widget.component.props.source;
$: icon = state && !state.muted ? MdVolumeUp : MdVolumeOff;
$: icon = !state.muted ? MdVolumeUp : MdVolumeOff;
$: if (source) {
$: if (source) getVolume();
function getVolume() {
obs.emit("GetVolume", { source }).then((result) => (state = result));
}
Expand All @@ -26,6 +27,10 @@
}
onMount(async () => {
obs.on(`connected`, () => {
getVolume();
});
obs.on(`source.volume`, ({ sourceName, volume }) => {
if (sourceName === source) {
state.volume = volume;
Expand All @@ -42,7 +47,7 @@

<WidgetWrapper widget="{widget}" on:click="{onClick}">
<div class="flex flex-col h-full text-center">
{#if source && state}
{#if source}
<div
class="flex flex-col w-full h-full {state.muted ? 'text-red-600' : ''}"
>
Expand Down
2 changes: 2 additions & 0 deletions front-src/client/components/Widgets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as SceneList from "./OBS/SceneList";
import * as GoToScene from "./OBS/GoToScene";
import * as ToggleScene from "./OBS/ToggleScene";
import * as ToggleAudio from "./OBS/ToggleAudio";
import * as AudioVolume from "./OBS/AudioVolume";

import * as TwitchChat from "./Twitch/Chat";
import * as TwitchStream from "./Twitch/Stream";
Expand All @@ -18,6 +19,7 @@ const widgets = {
GoToScene,
ToggleScene,
ToggleAudio,
AudioVolume,
TwitchChat,
TwitchStream,
TwitchRewards,
Expand Down

0 comments on commit 4662d73

Please sign in to comment.