Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 1 addition & 85 deletions app/components-react/editor/elements/SourceSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -662,73 +662,6 @@ class SourceSelectorController {
return this.scene.getSelection(sceneNodeId);
}

toggleDualOutput() {
if (this.userService.isLoggedIn) {
if (Services.StreamingService.views.isMidStreamMode) {
message.error({
content: $t('Cannot toggle Dual Output while live.'),
className: styles.toggleError,
});
} else if (Services.TransitionsService.views.studioMode) {
message.error({
content: $t('Cannot toggle Dual Output while in Studio Mode.'),
className: styles.toggleError,
});
} else {
// only open video settings when toggling on dual output
const skipShowVideoSettings = this.dualOutputService.views.dualOutputMode === true;

this.dualOutputService.actions.setDualOutputModeIfPossible(
!this.dualOutputService.views.dualOutputMode,
skipShowVideoSettings,
);
Services.UsageStatisticsService.recordFeatureUsage('DualOutput');
Services.UsageStatisticsService.recordAnalyticsEvent('DualOutput', {
type: 'ToggleOnDualOutput',
source: 'SourceSelector',
isPrime: this.userService.isPrime,
platforms: this.streamingService.views.linkedPlatforms,
tiktokStatus: this.tiktokService.scope,
});

if (!this.dualOutputService.views.dualOutputMode && this.selectiveRecordingEnabled) {
// show warning message if selective recording is active
remote.dialog
.showMessageBox(Utils.getChildWindow(), {
title: 'Vertical Display Disabled',
message: $t(
'Dual Output can’t be displayed - Selective Recording only works with horizontal sources and disables editing the vertical output scene. Please disable selective recording from Sources to set up Dual Output.',
),
buttons: [$t('OK')],
})
.catch(() => {});
}
}
} else {
this.handleShowModal(true);
}
}

handleShowModal(status: boolean) {
Services.WindowsService.actions.updateStyleBlockers('main', status);
this.store.update('showModal', status);
}

handleAuth() {
this.userService.actions.showLogin();
const onboardingCompleted = Services.OnboardingService.onboardingCompleted.subscribe(() => {
Services.DualOutputService.actions.setDualOutputModeIfPossible();
Services.SettingsService.actions.showSettings('Video');
onboardingCompleted.unsubscribe();
});
}

get dualOutputTitle() {
return !this.isDualOutputActive || !this.userService.isLoggedIn
? $t('Enable Dual Output to stream to horizontal & vertical platforms simultaneously')
: $t('Disable Dual Output');
}

get scene() {
const scene = getDefined(this.scenesService.views.activeScene);
return scene;
Expand Down Expand Up @@ -757,12 +690,6 @@ function SourceSelector() {
</Translate>
</HelpTip>
)}
<AuthModal
prompt={$t('Please log in to enable dual output. Would you like to log in now?')}
showModal={showModal}
handleShowModal={ctrl.handleShowModal}
handleAuth={ctrl.handleAuth}
/>
</>
);
}
Expand Down Expand Up @@ -791,17 +718,6 @@ function StudioControls() {
/>
</Tooltip>

<Tooltip title={ctrl.dualOutputTitle} placement="bottomRight">
<i
data-name="dual-output-toggle"
className={cx('icon-dual-output icon-button icon-button--lg', {
active: ctrl.isDualOutputActive,
})}
onClick={() => ctrl.toggleDualOutput()}
data-testid={ctrl.isDualOutputActive ? 'dual-output-active' : 'dual-output-inactive'}
/>
</Tooltip>

<Tooltip title={$t('Toggle Selective Recording')} placement="bottomRight">
<i
className={cx('icon-smart-record icon-button icon-button--lg', {
Expand Down Expand Up @@ -898,7 +814,7 @@ const TreeNode = React.forwardRef(
removeSource: () => void;
sourceProperties: () => void;
},
ref: React.RefObject<HTMLDivElement>,
ref: React.ForwardedRef<HTMLDivElement>,
) => {
function selectiveRecordingMetadata() {
if (p.isStreamVisible && p.isRecordingVisible) {
Expand Down
6 changes: 3 additions & 3 deletions app/components-react/root/StartStreamingButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ function StartStreamingButton(p: { disabled?: boolean }) {
delaySeconds,
streamShiftStatus,
streamShiftForceGoLive,
isDualOutputMode,
isLoggedIn,
isPrime,
primaryPlatform,
Expand All @@ -43,7 +42,6 @@ function StartStreamingButton(p: { disabled?: boolean }) {
delaySeconds: StreamingService.views.delaySeconds,
streamShiftStatus: RestreamService.state.streamShiftStatus,
streamShiftForceGoLive: RestreamService.state.streamShiftForceGoLive,
isDualOutputMode: StreamingService.views.isDualOutputMode,
isLoggedIn: UserService.isLoggedIn,
isPrime: UserService.state.isPrime,
primaryPlatform: UserService.state.auth?.primaryPlatform,
Expand Down Expand Up @@ -216,7 +214,7 @@ function StartStreamingButton(p: { disabled?: boolean }) {
StreamingService.actions.goLive();
}
}
}, [streamingStatus, streamShiftStatus, isDualOutputMode, isLoggedIn, isPrime]);
}, [streamingStatus, streamShiftStatus, isLoggedIn, isPrime]);

// Wrap the toggleStreaming function in a debounce to prevent multiple rapid clicks
// and also to cancel the action on unmount to prevent memory leaks and state updates on unmounted components
Expand Down Expand Up @@ -254,6 +252,8 @@ function StartStreamingButton(p: { disabled?: boolean }) {

if (!primaryPlatform) return false;

// Show the Go Live window if the user has targets assigned to multiple displays,
// which indicates that they are using dual output mode.
if (StreamingService.views.isDualOutputMode) {
return true;
}
Expand Down
14 changes: 7 additions & 7 deletions app/components-react/root/StudioEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export default function StudioEditor() {
studioMode: TransitionsService.state.studioMode,
showHorizontalDisplay: DualOutputService.views.showHorizontalDisplay,
showVerticalDisplay: DualOutputService.views.showVerticalDisplay,
showBothDisplays: DualOutputService.views.showBothDisplays,
isRecording: StreamingService.views.isRecording,
activeSceneId: ScenesService.views.activeSceneId,
isLoading: DualOutputService.views.isLoading,
dualOutputMode: DualOutputService.views.dualOutputMode,
}));
const displayEnabled = !performanceMode && !v.isLoading;
const displayEnabled = !performanceMode;
const placeholderRef = useRef<HTMLDivElement>(null);
const studioModeRef = useRef<HTMLDivElement>(null);
const [studioModeStacked, setStudioModeStacked] = useState(false);
Expand All @@ -51,9 +51,8 @@ export default function StudioEditor() {
]);

const sourceId = useMemo(() => {
const dualOutputMode = v.showHorizontalDisplay && v.showVerticalDisplay;
return v.studioMode && !dualOutputMode ? studioModeTransitionName : undefined;
}, [v.showHorizontalDisplay, v.showVerticalDisplay, v.studioMode]);
return v.studioMode && !v.showBothDisplays ? studioModeTransitionName : undefined;
}, [v.showBothDisplays, v.studioMode]);

useEffect(() => {
const timeoutHandles: { [key: number]: NodeJS.Timeout | undefined } = {};
Expand Down Expand Up @@ -242,8 +241,9 @@ export default function StudioEditor() {
<div className={styles.mainContainer} ref={studioModeRef}>
{displayEnabled && (
<div className={cx(styles.studioModeContainer, { [styles.stacked]: studioModeStacked })}>
{v.studioMode && <StudioModeControls stacked={studioModeStacked} />}
{v.dualOutputMode && (
{v.studioMode ? (
<StudioModeControls stacked={studioModeStacked} />
) : (
<DualOutputControls stacked={studioModeStacked} isRecording={v.isRecording} />
)}
<div
Expand Down
4 changes: 4 additions & 0 deletions app/components-react/shared/DualOutputToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { CheckboxInput } from 'components-react/shared/inputs';
import { alertAsync } from 'components-react/modals';
import cx from 'classnames';

/**
* @deprecated This component is not currently used. It is left over from when dual output was its own mode.
* Leaving for now for legacy reasons, but it should be removed in the future.
*/
interface IDualOutputToggleProps {
type?: 'dual' | 'single';
value?: boolean;
Expand Down
3 changes: 2 additions & 1 deletion app/components-react/sidebar/NavTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ export default memo(function NavTools() {

const handleAuth = () => {
if (isLoggedIn) {
Services.DualOutputService.actions.setDualOutputModeIfPossible(false, true);
// Subscription in dual output service will handle toggling off the vertical display when logging out,
// because logged out users cannot use dual output.
UserService.actions.logOut();
} else {
WindowsService.actions.closeChildWindow();
Expand Down
6 changes: 3 additions & 3 deletions app/components-react/windows/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ export default function Settings() {
})
.then(({ response }) => {
if (response === 0) {
DualOutputService.actions.return.setDualOutputModeIfPossible(false, true).then(() => {
UserService.actions.logOut();
});
// Subscription in dual output service will handle toggling off the vertical display when logging out,
// because logged out users cannot use dual output.
UserService.actions.logOut();
}
});
} else {
Expand Down
5 changes: 2 additions & 3 deletions app/components-react/windows/go-live/useGoLiveSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,9 @@ export class GoLiveSettingsModule {
}

/**
* If the user is in dual output mode, we need to ensure the stream switcher is disabled
* If the user will stream with dual output, ensure the stream switcher is disabled
*/
const { dualOutputMode } = DualOutputService.state;
if (dualOutputMode && settings.streamShift) {
if (this.state.isDualOutputMode && settings.streamShift) {
settings.streamShift = false;
}

Expand Down
5 changes: 3 additions & 2 deletions app/components-react/windows/settings/Developer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export function DualOutputDeveloperSettings(p: { collection?: string }) {
// convert collection
const filePath = await SceneCollectionsService.actions.return.convertDualOutputCollection(
assignToHorizontal,
p.collection,
);

if (filePath) {
Expand All @@ -181,15 +182,15 @@ export function DualOutputDeveloperSettings(p: { collection?: string }) {
)}
</span>
<div>
<h4>{$t('Convert to Vanilla Scene')}</h4>
<h4>{$t('Repair Scene Collection')}</h4>
<div style={{ display: 'flex', justifyContent: 'space-evenly', paddingBottom: '16px' }}>
<Button
className="button--soft-warning"
style={{ marginRight: '16px' }}
onClick={() => convertDualOutputCollection()}
disabled={busy}
>
{$t('Convert')}
{$t('Repair')}
</Button>
{!p.collection && (
<Button
Expand Down
79 changes: 1 addition & 78 deletions app/components-react/windows/settings/Experimental.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { Button } from 'antd';
import { ObsSettingsSection } from './ObsSettings';
import { Services } from '../../service-provider';
import { alertAsync } from '../../modals';
import { CheckCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { $t } from 'services/i18n/index';

export function ExperimentalSettings() {
const { ScenesService, WindowsService, SceneCollectionsService } = Services;
const { ScenesService, WindowsService } = Services;

function repairSceneCollection() {
ScenesService.repair();
Expand All @@ -22,89 +21,13 @@ export function ExperimentalSettings() {
});
}

/**
* Convert a dual output scene collection to a vanilla scene collection
* @param assignToHorizontal Boolean for if the vertical sources should be assigned to the
* horizontal display or should be deleted
* @param exportOverlay Boolean for is the scene collection should be exported upon completion
*/
async function convertDualOutputCollection(assignToHorizontal: boolean = false) {
// confirm that the active scene collection is a dual output collection
if (
!SceneCollectionsService?.sceneNodeMaps ||
(SceneCollectionsService?.sceneNodeMaps &&
Object.values(SceneCollectionsService?.sceneNodeMaps).length === 0)
) {
alertAsync({
icon: <ExclamationCircleOutlined style={{ color: 'var(--red)' }} />,
getContainer: '#mainWrapper',
className: 'react',
title: $t('Invalid Scene Collection'),
content: $t('The active scene collection is not a dual output scene collection.'),
});
return;
}

await SceneCollectionsService.actions.return
.convertDualOutputCollection(assignToHorizontal)
.then((message: string) => {
const messageData = JSON.parse(message);

const className = messageData.error ? 'react convert-error' : 'react convert-success';

const icon = messageData.error ? (
<ExclamationCircleOutlined style={{ color: 'var(--red)' }} />
) : (
<CheckCircleOutlined style={{ color: 'var(--teal)' }} />
);

const title = $t(messageData?.title) ?? 'Success';

const content = messageData?.content ?? $t('Successfully converted scene collection.');

alertAsync({
icon,
getContainer: '#mainWrapper',
className,
title,
content,
});
});
}

return (
<ObsSettingsSection>
<div className="section">
<h2>{$t('Repair Scene Collection')}</h2>
<Button onClick={repairSceneCollection}>Repair Scene Collection</Button>
</div>
<div className="section">
<h2>{$t('Convert Dual Output Scene Collection')}</h2>

<span>
{$t(
'The below will create a copy of the active scene collection, set the copy as the active collection, and then remove all vertical sources.',
)}
</span>
<div style={{ marginTop: '10px' }}>
<h4>{$t('Convert to Vanilla Scene')}</h4>
<Button
className="convert-collection button button--soft-warning"
onClick={async () => await convertDualOutputCollection()}
>
{$t('Convert')}
</Button>
</div>
{/* <div style={{ marginTop: '10px' }}>
<h4>{$t('Assign Vertical Sources to Horizontal Display')}</h4>
<Button
className="assign-collection button button--soft-warning"
onClick={async () => await convertDualOutputCollection(true)}
>
{$t('Assign')}
</Button>
</div> */}
</div>
<div className="section">
<h2>{$t('Show Components Library')}</h2>
<Button type="primary" onClick={showDemoComponents}>
Expand Down
5 changes: 3 additions & 2 deletions app/components-react/windows/settings/Stream.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,9 @@ export function StreamSettings() {
streamType: 'rtmp_common',
});

if (DualOutputService.views.dualOutputMode) {
DualOutputService.actions.setDualOutputModeIfPossible(false, true);
// Hide the vertical display when protected mode disabled because it is only supported for protected mode
if (DualOutputService.views.showVerticalDisplay) {
DualOutputService.actions.toggleDisplay(false, 'vertical');
}
}

Expand Down
Loading
Loading