Skip to content

Commit

Permalink
Got rid of StatefulService in virtual-webcam
Browse files Browse the repository at this point in the history
  • Loading branch information
avoitenko-logitech committed Jan 10, 2025
1 parent f00f63e commit 0d23234
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 35 deletions.
31 changes: 20 additions & 11 deletions app/components/windows/settings/VirtualWebcamSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { getOS, OS } from 'util/operating-systems';
import { Services } from 'components-react/service-provider';
import { Multiselect } from 'vue-multiselect';
import { VCamOutputType } from 'obs-studio-node';
import { ObjectChangeSet } from 'realm';
import { DefaultObject } from 'realm/dist/public-types/schema';

@Component({
components: {
Expand All @@ -34,11 +36,12 @@ export default class VirtualCamSettings extends Vue {
scenesService = Services.ScenesService;
sourcesService = Services.SourcesService;

isVCamRunning: boolean = false;

created() {
this.checkInstalled();

const outputType: VCamOutputType = this.virtualWebcamService.outputType();
const outputTypeIndex = this.outputTypeOptions.findIndex(val => val.id === outputType);
const outputTypeIndex = this.outputTypeOptions.findIndex(val => val.id === this.virtualWebcamService.outputType);

if (outputTypeIndex !== -1) {
this.outputTypeValue = this.outputTypeOptions[outputTypeIndex];
Expand All @@ -47,6 +50,12 @@ export default class VirtualCamSettings extends Vue {
}

this.onOutputTypeChange(this.outputTypeValue);

// TODO: after migrating this component to React, the listener should be removed and useRealmObject used instead
const listener = (_o: DefaultObject, changes: ObjectChangeSet<DefaultObject>) => {
this.isVCamRunning = this.virtualWebcamService.isRunning;
};
this.virtualWebcamService.ephemeralState.realmModel.addListener(listener);
}

install() {
Expand Down Expand Up @@ -75,7 +84,7 @@ export default class VirtualCamSettings extends Vue {
}

get running() {
return this.virtualWebcamService.state.running;
return this.isVCamRunning;
}

needsInstallSection(isUpdate: boolean) {
Expand Down Expand Up @@ -110,8 +119,8 @@ export default class VirtualCamSettings extends Vue {
}

isInstalledSection() {
const buttonText = this.running ? $t('Stop Virtual Webcam') : $t('Start Virtual Webcam');
const statusText = this.running
const buttonText = this.isVCamRunning ? $t('Stop Virtual Webcam') : $t('Start Virtual Webcam');
const statusText = this.isVCamRunning
? $t('Virtual webcam is <status>Running</status>')
: $t('Virtual webcam is <status>Offline</status>');

Expand All @@ -124,7 +133,7 @@ export default class VirtualCamSettings extends Vue {
scopedSlots={{
status: (text: string) => {
return (
<span class={cx({ [styles.running]: this.running })}>
<span class={cx({ [styles.running]: this.isVCamRunning })}>
<b>{text}</b>
</span>
);
Expand All @@ -133,9 +142,9 @@ export default class VirtualCamSettings extends Vue {
/>
</p>
<button
class={cx('button', { 'button--action': !this.running, 'button--warn': this.running })}
class={cx('button', { 'button--action': !this.isVCamRunning, 'button--warn': this.isVCamRunning })}
style={{ marginBottom: '16px' }}
onClick={this.running ? this.stop : this.start}
onClick={this.isVCamRunning ? this.stop : this.start}
>
{buttonText}
</button>
Expand Down Expand Up @@ -163,7 +172,7 @@ export default class VirtualCamSettings extends Vue {
<button
class="button button--default"
style={{ marginBottom: '16px' }}
disabled={this.running}
disabled={this.isVCamRunning}
onClick={this.uninstall}
>
{$t('Uninstall Virtual Webcam')}
Expand Down Expand Up @@ -195,7 +204,7 @@ export default class VirtualCamSettings extends Vue {
}));

this.outputSelectionOptions = scenes;
const outputSelectionIndex = this.outputSelectionOptions.findIndex(val => val.id === this.virtualWebcamService.outputSelection());
const outputSelectionIndex = this.outputSelectionOptions.findIndex(val => val.id === this.virtualWebcamService.outputSelection);
if (outputSelectionIndex !== -1) {
this.outputSelectionValue = scenes[outputSelectionIndex];
} else {
Expand All @@ -207,7 +216,7 @@ export default class VirtualCamSettings extends Vue {
const sources = this.virtualWebcamService.getVideoSources().map(source => ({name: source.name, id: source.sourceId}));

this.outputSelectionOptions = sources;
const outputSelectionIndex = this.outputSelectionOptions.findIndex(val => val.id === this.virtualWebcamService.outputSelection());
const outputSelectionIndex = this.outputSelectionOptions.findIndex(val => val.id === this.virtualWebcamService.outputSelection);
if (outputSelectionIndex !== -1) {
this.outputSelectionValue = sources[outputSelectionIndex];
} else {
Expand Down
4 changes: 2 additions & 2 deletions app/services/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,13 @@ const GENERAL_ACTIONS: HotkeyGroup = {
name: 'TOGGLE_VIRTUAL_CAMERA_ON',
description: () => $t('Start Virtual Camera'),
down: () => getVirtualCameraService().start(),
isActive: () => getVirtualCameraService().state.running,
isActive: () => getVirtualCameraService().isRunning,
},
TOGGLE_VIRTUAL_CAMERA_OFF: {
name: 'TOGGLE_VIRTUAL_CAMERA_OFF',
description: () => $t('Stop Virtual Camera'),
down: () => getVirtualCameraService().stop(),
isActive: () => !getVirtualCameraService().state.running,
isActive: () => !getVirtualCameraService().isRunning,
},
};

Expand Down
55 changes: 33 additions & 22 deletions app/services/virtual-webcam.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StatefulService, mutation } from 'services/core';
import { Service, mutation } from 'services/core';
import * as obs from '../../obs-api';
import fs from 'fs';
import util from 'util';
Expand All @@ -25,10 +25,19 @@ export enum EVirtualWebcamPluginInstallStatus {
Outdated = 'outdated',
}

interface IVirtualWebcamServiceState {
running: boolean;
class VirtualCamServiceEphemeralState extends RealmObject {
isRunning: boolean;

static schema: ObjectSchema = {
name: 'VirtualCamServiceEphemeralState',
properties: {
isRunning: { type: 'bool', default: false },
},
};
}

VirtualCamServiceEphemeralState.register();

class VirtualCamServicePersistentState extends RealmObject {
// Naming of these fields is taken from OBS for reference
outputType: VCamOutputType;
Expand All @@ -45,12 +54,11 @@ class VirtualCamServicePersistentState extends RealmObject {

VirtualCamServicePersistentState.register({ persist: true });

export class VirtualWebcamService extends StatefulService<IVirtualWebcamServiceState> {
export class VirtualWebcamService extends Service {
@Inject() usageStatisticsService: UsageStatisticsService;
@Inject() sourcesService: SourcesService;
virtualCamSettings = VirtualCamServicePersistentState.inject();

static initialState: IVirtualWebcamServiceState = { running: false };
state = VirtualCamServicePersistentState.inject();
ephemeralState = VirtualCamServiceEphemeralState.inject();

runningChanged = new Subject<boolean>();

Expand Down Expand Up @@ -107,24 +115,22 @@ export class VirtualWebcamService extends StatefulService<IVirtualWebcamServiceS
}

start() {
if (this.state.running) return;
if (this.ephemeralState.isRunning) return;

//obs.NodeObs.OBS_service_createVirtualWebcam('Streamlabs Desktop Virtual Webcam');
obs.NodeObs.OBS_service_startVirtualCam();

this.SET_RUNNING(true);
this.setRunning(true);
this.runningChanged.next(true);

this.usageStatisticsService.recordFeatureUsage('VirtualWebcam');
}

stop() {
if (!this.state.running) return;
if (!this.ephemeralState.isRunning) return;

obs.NodeObs.OBS_service_stopVirtualCam();
//obs.NodeObs.OBS_service_removeVirtualWebcam();

this.SET_RUNNING(false);
this.setRunning(false);
this.runningChanged.next(false);
}

Expand All @@ -134,21 +140,25 @@ export class VirtualWebcamService extends StatefulService<IVirtualWebcamServiceS
}

update(type: VCamOutputType, name: string) {
this.virtualCamSettings.db.write(() => {
this.virtualCamSettings.deepPatch({
this.state.db.write(() => {
this.state.deepPatch({
outputType: type,
outputSelection: name,
});
});
obs.NodeObs.OBS_service_updateVirtualCam(type, name);
}

outputType(): VCamOutputType {
return this.virtualCamSettings.outputType;
get outputType(): VCamOutputType {
return this.state.outputType;
}

outputSelection(): string {
return this.virtualCamSettings.outputSelection;
get outputSelection(): string {
return this.state.outputSelection;
}

get isRunning(): boolean {
return this.ephemeralState.isRunning;
}

getVideoSources() {
Expand All @@ -158,8 +168,9 @@ export class VirtualWebcamService extends StatefulService<IVirtualWebcamServiceS
);
}

@mutation()
private SET_RUNNING(running: boolean) {
this.state.running = running;
private setRunning(running: boolean) {
this.ephemeralState.db.write(() => {
this.ephemeralState.isRunning = running;
});
}
}

0 comments on commit 0d23234

Please sign in to comment.