Skip to content

Commit

Permalink
Streaming API Integration (#4752)
Browse files Browse the repository at this point in the history
* Add twitter platform service and ui elements

* push WIP

* aesthetic and chat fixes

* back out testing changes

* switch to CDN links for x logo

* fix copy paste error

* remove log

* remove incorrect super call

* a couple small fixes

* fix dual output for X

* remove debug code

---------

Co-authored-by: Sean Beyer <sean.beyer@streamlabs.com>
  • Loading branch information
avacreeth and gettinToasty authored Oct 17, 2023
1 parent cb248b2 commit df060c2
Show file tree
Hide file tree
Showing 28 changed files with 340 additions and 19 deletions.
3 changes: 3 additions & 0 deletions app/app-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export { TiktokService } from 'services/platforms/tiktok';
export { TrovoService } from 'services/platforms/trovo';
export { RestreamService } from 'services/restream';
export { TwitterService } from 'services/integrations/twitter';
export { TwitterPlatformService } from 'services/platforms/twitter';
export { UsageStatisticsService } from './services/usage-statistics';
export { GameOverlayService } from 'services/game-overlay';
export { SharedStorageService } from 'services/integrations/shared-storage';
Expand Down Expand Up @@ -138,6 +139,7 @@ import { TwitterService } from './services/integrations/twitter';
import { SettingsService } from './services/settings';
import { UserService } from './services/user';
import { TwitchService } from './services/platforms/twitch';
import { TwitterPlatformService } from './services/platforms/twitter';
import { TwitchTagsService } from './services/platforms/twitch/index';
import { TrovoService } from './services/platforms/trovo';
import { YoutubeService } from './services/platforms/youtube';
Expand Down Expand Up @@ -217,6 +219,7 @@ export const AppServices = {
FacebookService,
UserService,
TwitchService,
TwitterPlatformService,
TwitchTagsService,
TrovoService,
DismissablesService,
Expand Down
2 changes: 1 addition & 1 deletion app/components-react/pages/PlatformMerge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function PlatformMerge(p: IPlatformMergeProps) {

async function mergePlatform() {
if (!platform) return;
const mode = ['youtube', 'twitch'].includes(platform) ? 'external' : 'internal';
const mode = ['youtube', 'twitch', 'twitter'].includes(platform) ? 'external' : 'internal';
await UserService.actions.return.startAuth(platform, mode, true);

if (p.params.highlighter) {
Expand Down
4 changes: 2 additions & 2 deletions app/components-react/pages/onboarding/Connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function Connect() {

const platforms = RecordingModeService.views.isRecordingModeEnabled
? ['streamlabs', 'youtube']
: ['streamlabs', 'twitch', 'youtube', 'facebook', 'trovo'];
: ['streamlabs', 'twitch', 'youtube', 'facebook', 'trovo', 'twitter'];

return (
<div className={styles.pageContainer}>
Expand Down Expand Up @@ -171,7 +171,7 @@ export class LoginModule {

const result = await this.UserService.startAuth(
platform,
['youtube', 'twitch'].includes(platform) ? 'external' : 'internal',
['youtube', 'twitch', 'twitter'].includes(platform) ? 'external' : 'internal',
merge,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function PrimaryPlatformSelect() {
isLogin: OnboardingService.state.options.isLogin,
}));
const { loading, authInProgress, authPlatform, finishSLAuth } = useModule(LoginModule);
const platforms = ['twitch', 'youtube', 'facebook', 'trovo'];
const platforms = ['twitch', 'youtube', 'facebook', 'trovo', 'twitter'];
const platformOptions = [
{
value: 'twitch',
Expand All @@ -43,6 +43,11 @@ export function PrimaryPlatformSelect() {
label: 'Trovo',
image: <PlatformLogo platform="trovo" size={14} />,
},
{
value: 'twitter',
label: 'X (Twitter)',
image: <PlatformLogo platform="twitter" size={14} />,
},
].filter(opt => {
return linkedPlatforms.includes(opt.value as TPlatform);
});
Expand Down
16 changes: 9 additions & 7 deletions app/components-react/shared/DisplaySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Services } from 'components-react/service-provider';
import { RadioInput } from './inputs';
import { displayLabels } from 'services/dual-output';
import { TDisplayType } from 'services/settings-v2';
import { platformLabels } from 'services/platforms';
import { TPlatform, platformLabels } from 'services/platforms';
import { useGoLiveSettings } from 'components-react/windows/go-live/useGoLiveSettings';
import { ICustomStreamDestination } from 'services/settings/streaming';

interface IDisplaySelectorProps {
title: string;
index: number;
isPlatform: boolean;
platform: TPlatform | null;
nolabel?: boolean;
nomargin?: boolean;
className?: string;
Expand All @@ -29,9 +30,10 @@ export default function DisplaySelector(p: IDisplaySelectorProps) {
isMidstreamMode: StreamingService.views.isMidStreamMode,
}));

const platform = p.title.toLowerCase();
const setting = p.isPlatform ? v.platformSettings[platform] : customDestinations[p.index];
const label = p.isPlatform ? platformLabels(platform) : setting.name;
const setting = p.platform ? v.platformSettings[p.platform] : customDestinations[p.index];
const label = p.platform
? platformLabels(p.platform)
: (setting as ICustomStreamDestination).name;

const displays = [
{
Expand All @@ -55,8 +57,8 @@ export default function DisplaySelector(p: IDisplaySelectorProps) {
defaultValue="horizontal"
options={displays}
onChange={(val: TDisplayType) =>
p.isPlatform
? v.updatePlatformSetting(platform, val)
p.platform
? v.updatePlatformSetting(p.platform, val)
: updateCustomDestinationDisplay(p.index, val)
}
value={setting?.display ?? 'horizontal'}
Expand Down
12 changes: 12 additions & 0 deletions app/components-react/shared/PlatformLogo.m.less
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@
background-image: url('https://slobs-cdn.streamlabs.com/media/trovo-black.png');
}
}
.twitter {
background-image: url(https://slobs-cdn.streamlabs.com/media/twitter-logo-white.png);
display: inline-block;
width: 40px;
height: 40px;
background-size: contain;
background-repeat: no-repeat;

&.twitter--black {
background-image: url(https://slobs-cdn.streamlabs.com/media/twitter-logo-black.png);
}
}
19 changes: 19 additions & 0 deletions app/components-react/shared/PlatformLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { HTMLAttributes } from 'react';
import { TPlatform } from '../../services/platforms';
import cx from 'classnames';
import css from './PlatformLogo.m.less';
import { Services } from 'components-react/service-provider';
import { useVuex } from 'components-react/hooks';

const sizeMap = {
small: 14,
Expand All @@ -18,6 +20,13 @@ interface IProps {
}

export default function PlatformLogo(p: IProps & HTMLAttributes<unknown>) {
const { CustomizationService } = Services;
const { isDark } = useVuex(() => {
return {
isDark: CustomizationService.views.isDarkTheme,
};
});

function iconForPlatform() {
return {
twitch: 'fab fa-twitch',
Expand All @@ -27,6 +36,7 @@ export default function PlatformLogo(p: IProps & HTMLAttributes<unknown>) {
trovo: 'fab fa-trovo',
dlive: 'dlive',
nimotv: 'nimotv',
twitter: 'twitter',
streamlabs: 'icon-streamlabs',
}[p.platform];
}
Expand All @@ -36,6 +46,14 @@ export default function PlatformLogo(p: IProps & HTMLAttributes<unknown>) {
: undefined;
const colorStyle = p.color ? { color: p.color } : undefined;
const style = { ...sizeStyle, ...colorStyle };

let color = p.color;

// This might be a hack - but handle twitter logo for different themes
if (p.platform === 'twitter' && !isDark) {
color = 'black';
}

return (
<>
{p.trovo ? (
Expand All @@ -45,6 +63,7 @@ export default function PlatformLogo(p: IProps & HTMLAttributes<unknown>) {
className={cx(iconForPlatform(), !p.nocolor && css[p.platform], p.className, {
// Trovo doesn't provide an SVG, so just use different colored PNGs
[css['trovo--black']]: p.platform === 'trovo' && p.color === 'black',
[css['twitter--black']]: p.platform === 'twitter' && color === 'black',
})}
style={style}
/>
Expand Down
1 change: 1 addition & 0 deletions app/components-react/sidebar/NavTools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ function LoginMenuItem(p: {
styles.platformLogo,
styles[`platform-logo-${platform?.type ?? 'default'}`],
)}
size={platform.type === 'twitter' ? 20 : undefined}
/>
)}
<span className={styles.username}>{platform?.username || $t('Log Out')}</span>
Expand Down
5 changes: 4 additions & 1 deletion app/components-react/windows/go-live/GoLiveSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function GoLiveSettings() {
canAddDestinations,
shouldShowPrimeLabel,
canUseOptimizedProfile,
showTweet,
} = useGoLiveSettings().extend(module => {
const {
RestreamService,
Expand Down Expand Up @@ -65,6 +66,8 @@ export default function GoLiveSettings() {
canUseOptimizedProfile:
VideoEncodingOptimizationService.state.canSeeOptimizedProfile ||
VideoEncodingOptimizationService.state.useOptimizedProfile,

showTweet: UserService.views.auth?.primaryPlatform !== 'twitter',
};
});

Expand Down Expand Up @@ -104,7 +107,7 @@ export default function GoLiveSettings() {
{isAdvancedMode && <div className={styles.spacer} />}
{/*EXTRAS*/}
<Section isSimpleMode={!isAdvancedMode} title={$t('Extras')}>
<TwitterInput />
{showTweet && <TwitterInput />}
{!!canUseOptimizedProfile && <OptimizedProfileSwitcher />}
</Section>
</Scrollable>
Expand Down
4 changes: 4 additions & 0 deletions app/components-react/windows/go-live/PlatformSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TiktokEditStreamInfo } from './platforms/TiktokEditStreamInfo';
import { IPlatformComponentParams, TLayoutMode } from './platforms/PlatformSettingsLayout';
import { getDefined } from '../../../util/properties-type-guards';
import { TrovoEditStreamInfo } from './platforms/TrovoEditStreamInfo';
import { TwitterEditStreamInfo } from './platforms/TwitterEditStreamInfo';

export default function PlatformSettings() {
const {
Expand Down Expand Up @@ -94,6 +95,9 @@ export default function PlatformSettings() {
<TiktokEditStreamInfo {...createPlatformBinding('tiktok')} />
)}
{platform === 'trovo' && <TrovoEditStreamInfo {...createPlatformBinding('trovo')} />}
{platform === 'twitter' && (
<TwitterEditStreamInfo {...createPlatformBinding('twitter')} />
)}
</Section>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const DestinationSwitcher = React.forwardRef<{ addClass: () => void }, IDestinat
</div>
<div className={styles.platformDisplay}>
<span className={styles.label}>{`${$t('Output')}:`}</span>
<DisplaySelector title={title} isPlatform={!!platform} index={p.index} nolabel nomargin />
<DisplaySelector title={title} platform={platform} index={p.index} nolabel nomargin />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ function DestinationSwitcher(p: IDestinationSwitcherProps) {
</div>
<div className={styles.platformDisplay}>
<span className={styles.label}>{`${$t('Output')}:`}</span>
<DisplaySelector title={title} isPlatform={!!platform} index={p.index} nolabel nomargin />
<DisplaySelector title={title} platform={platform} index={p.index} nolabel nomargin />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import PlatformSettingsLayout, { IPlatformComponentParams } from './PlatformSettingsLayout';
import { ITwitterStartStreamOptions } from '../../../../services/platforms/twitter';
import { createBinding } from '../../../shared/inputs';
import Form from '../../../shared/inputs/Form';
import { CommonPlatformFields } from '../CommonPlatformFields';
import GameSelector from '../GameSelector';

export function TwitterEditStreamInfo(p: IPlatformComponentParams<'twitter'>) {
const twSettings = p.value;

function updateSettings(patch: Partial<ITwitterStartStreamOptions>) {
p.onChange({ ...twSettings, ...patch });
}

const bind = createBinding(twSettings, updatedSettings => updateSettings(updatedSettings));

return (
<Form name="twitter-settings">
<PlatformSettingsLayout
layoutMode={p.layoutMode}
commonFields={
<CommonPlatformFields
key="common"
platform="twitter"
layoutMode={p.layoutMode}
value={twSettings}
onChange={updateSettings}
/>
}
requiredFields={<></>}
/>
</Form>
);
}
4 changes: 2 additions & 2 deletions app/components/LiveDock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@
</a>
</div>
<div class="flex">
<a @click="refreshChat" v-if="isTwitch || isTrovo || (isYoutube && isStreaming) || isFacebook">
<a @click="refreshChat" v-if="isTwitch || isTrovo || (isYoutube && isStreaming) || isFacebook || (isTwitter && isRestreaming)">
{{ $t('Refresh Chat') }}
</a>
</div>
</div>
<div
class="live-dock-chat"
v-if="!hideStyleBlockers && (isTwitch || isTrovo || (isYoutube && isStreaming) || (isFacebook && isStreaming))"
v-if="!hideStyleBlockers && (isTwitch || isTrovo || (isYoutube && isStreaming) || (isFacebook && isStreaming) || (isTwitter && isRestreaming))"
>
<div v-if="hasChatTabs" class="flex">
<tabs :tabs="chatTabs" v-model="selectedChat" :hideContent="true" />
Expand Down
20 changes: 20 additions & 0 deletions app/components/LiveDock.vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export default class LiveDock extends Vue {
underlyingSelectedChat = 'default';

get selectedChat() {
if (this.underlyingSelectedChat === 'default' && this.isTwitter && this.isRestreaming) {
return 'restream';
}

if (this.underlyingSelectedChat === 'default') return 'default';
if (this.underlyingSelectedChat === 'restream') {
if (this.restreamService.shouldGoLiveWithRestream) return 'restream';
Expand Down Expand Up @@ -183,6 +187,10 @@ export default class LiveDock extends Vue {
return this.userService.platform.type === 'trovo';
}

get isTwitter() {
return this.userService.platform.type === 'twitter';
}

get hideViewerCount() {
return this.customizationService.state.hideViewerCount;
}
Expand Down Expand Up @@ -259,9 +267,18 @@ export default class LiveDock extends Vue {
});
}

if (this.userService.state.auth.primaryPlatform === 'twitter') {
// Twitter is the only primary platform without a chat
return tabs.slice(1);
}

return tabs;
}

get isRestreaming() {
return this.restreamService.shouldGoLiveWithRestream;
}

get isPopOutAllowed() {
if (this.showDefaultPlatformChat) return false;
if (this.selectedChat === 'restream') return false;
Expand All @@ -281,6 +298,9 @@ export default class LiveDock extends Vue {
}

get canEditChannelInfo(): boolean {
// Twitter doesn't support editing title after going live
if (this.isTwitter && !this.isRestreaming) return false;

return (
this.streamingService.views.isMidStreamMode ||
this.userService.state.auth?.primaryPlatform === 'twitch'
Expand Down
1 change: 1 addition & 0 deletions app/components/shared/PlatformLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class PlatformLogo extends TsxComponent<LogoProps> {
nimotv: 'nimotv',
streamlabs: 'icon-streamlabs',
trovo: 'trovo',
twitter: 'twitter',
}[this.props.platform];
}

Expand Down
4 changes: 4 additions & 0 deletions app/services/dual-output/dual-output-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export const DualOutputPlatformSettings: TDualOutputPlatformSettings = {
platform: EPlatform.Trovo,
display: EOutputDisplayType.Horizontal,
},
[EPlatform.Twitter]: {
platform: EPlatform.Twitter,
display: EOutputDisplayType.Horizontal,
},
};

export const displayLabels = (display: EOutputDisplayType | string) =>
Expand Down
Loading

0 comments on commit df060c2

Please sign in to comment.