Skip to content

Commit

Permalink
feat(sources): grouping on Capture, Audio/Video and Media
Browse files Browse the repository at this point in the history
  • Loading branch information
blackxored committed Dec 12, 2024
1 parent 11ed258 commit 9987a4d
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 20 deletions.
90 changes: 70 additions & 20 deletions app/components-react/windows/source-showcase/SourceGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Services } from 'components-react/service-provider';
import { useVuex } from 'components-react/hooks';
import { IObsListOption } from 'components/obs/inputs/ObsInput';
import { WidgetDisplayData, WidgetType } from 'services/widgets';
import { TSourceType } from 'services/sources';
import { TSourceType, SourceDisplayData } from 'services/sources';
import { getPlatformService } from 'services/platforms';
import { $i } from 'services/utils';
import { byOS, getOS, OS } from 'util/operating-systems';
Expand All @@ -29,6 +29,9 @@ export default function SourceGrid(p: { activeTab: string }) {
// TODO: persistence
const [expandedSections, setExpandedSections] = useState([
'essentialSources',
'captureSources',
'avSources',
'mediaSources',
'generalSources',
'widgets',
'apps',
Expand Down Expand Up @@ -146,6 +149,10 @@ export default function SourceGrid(p: { activeTab: string }) {

const { Panel } = Collapse;

const toSourceEl = (source: IObsListOption<TSourceType>) => (
<SourceTag key={source.value} type={source.value} essential excludeWrap={excludeWrap} />
);

const essentialSourcesList = useMemo(
() => (
<>
Expand All @@ -171,12 +178,22 @@ export default function SourceGrid(p: { activeTab: string }) {
[essentialSources, isLoggedIn, excludeWrap],
);

const sourceDisplayData = useMemo(() => SourceDisplayData(), []);

const generalSourcesList = useMemo(() => {
return (
<>
{availableSources.filter(filterEssential).map(source => (
<SourceTag key={source.value} type={source.value} excludeWrap={excludeWrap} />
))}
{availableSources
.filter(filterEssential)
.filter(source => {
const displayData = sourceDisplayData[source.value];
// Filter out sources that have their own section group
if (displayData?.group) {
return !['capture', 'av', 'media'].includes(displayData.group);
}
return true;
})
.map(toSourceEl)}
<SourceTag
key="replay"
name={$t('Instant Replay')}
Expand All @@ -195,22 +212,28 @@ export default function SourceGrid(p: { activeTab: string }) {
);
}, [availableSources, p.activeTab, designerMode, excludeWrap]);

const appsList = useMemo(
() => (
<>
{availableAppSources.map(app => (
<SourceTag
key={`${app.appId}${app.source.id}`}
name={app.source.name}
type="app_source"
appId={app.appId}
appSourceId={app.source.id}
excludeWrap={excludeWrap}
/>
))}
</>
),
[availableAppSources, excludeWrap],
const byGroup = (group: 'capture' | 'av' | 'media') => (source: IObsListOption<TSourceType>) => {
const displayData = sourceDisplayData[source.value];
if (!displayData) {
return true;
}

return displayData.group === group;
};

const captureSourcesList = useMemo(
() => availableSources.filter(byGroup('capture')).map(toSourceEl),
[availableSources, excludeWrap],
);

const avSourcesList = useMemo(() => availableSources.filter(byGroup('av')).map(toSourceEl), [
availableSources,
excludeWrap,
]);

const mediaSourcesList = useMemo(
() => availableSources.filter(byGroup('media')).map(toSourceEl),
[availableSources, excludeWrap],
);

const widgetList = useMemo(
Expand Down Expand Up @@ -243,6 +266,24 @@ export default function SourceGrid(p: { activeTab: string }) {
[isLoggedIn, iterableWidgetTypes, p.activeTab, excludeWrap],
);

const appsList = useMemo(
() => (
<>
{availableAppSources.map(app => (
<SourceTag
key={`${app.appId}${app.source.id}`}
name={app.source.name}
type="app_source"
appId={app.appId}
appSourceId={app.source.id}
excludeWrap={excludeWrap}
/>
))}
</>
),
[availableAppSources, excludeWrap],
);

const individualTab = useMemo(() => {
if (showContent('general')) {
return (
Expand Down Expand Up @@ -295,6 +336,15 @@ export default function SourceGrid(p: { activeTab: string }) {
{essentialSourcesList}
</div>
</Panel>
<Panel header={$t('Capture Sources')} key="captureSources">
<div className="collapse-section">{captureSourcesList}</div>
</Panel>
<Panel header={$t('Video and Audio')} key="avSources">
<div className="collapse-section">{avSourcesList}</div>
</Panel>
<Panel header={$t('Media')} key="mediaSources">
<div className="collapse-section">{mediaSourcesList}</div>
</Panel>
<Panel header={$t('General Sources')} key="generalSources">
<div className="collapse-section" data-testid="general-sources">
{generalSourcesList}
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/en-US/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@
"All Sources": "All Sources",
"Media Categories": "Media Categories",
"General Sources": "General Sources",
"Capture Sources": "Capture Sources",
"Media": "Media",
"Add media": "Add media",
"Your webcam device": "Your webcam device",
"Display device video": "Display device video",
Expand Down
2 changes: 2 additions & 0 deletions app/services/sources/sources-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,6 @@ export interface ISourceDisplayData {
shortDesc?: string;
link?: string;
linkText?: string;
// TODO: make required if none are missing
group?: string;
}
25 changes: 25 additions & 0 deletions app/services/sources/sources-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'image.png',
supportList: imageSupport,
icon: 'icon-image',
group: 'media',
},
color_source: {
name: $t('Color Block'),
Expand All @@ -41,6 +42,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'browser-source.png',
supportList: [$t('Websites'), $t('Third party widgets'), 'HTML'],
icon: 'fas fa-globe',
group: 'capture',
},
ffmpeg_source: {
name: $t('Media File'),
Expand All @@ -49,13 +51,15 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
supportList: mediaSupport,
icon: 'far fa-file-video',
shortDesc: $t('Add media'),
group: 'media',
},
slideshow: {
name: $t('Image Slide Show'),
description: $t('Add a slideshow of images to your scene.'),
demoFilename: 'image-slide-show.png',
supportList: imageSupport,
icon: 'icon-image',
group: 'media',
},
text_gdiplus: {
name: $t('Text (GDI+)'),
Expand All @@ -70,6 +74,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'display-capture.png',
supportList: [$t('Primary monitor'), $t('Secondary monitor')],
icon: 'fas fa-desktop',
group: 'capture',
},
window_capture: {
name: $t('Window Capture'),
Expand All @@ -78,6 +83,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
supportList: [$t('Compatible with most modern browsers and programs')],
icon: 'icon-editor-9',
shortDesc: $t('Capture an application window'),
group: 'capture',
},
game_capture: {
name: $t('Game Capture'),
Expand All @@ -86,6 +92,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'game-capture.png',
supportList: [$t('Built in works with most modern computer games')],
icon: 'fas fa-gamepad',
group: 'capture',
},
dshow_input: {
name: $t('Camera / Capture Card'),
Expand All @@ -98,11 +105,13 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
$t('Capture cards (Elgato, Avermedia, BlackMagic)'),
],
icon: 'icon-webcam',
group: 'av',
},
ndi_source: {
name: $t('NDI source'),
description: $t('Allow you to capture NDI output streams.'),
icon: 'fas fa-file',
group: 'capture',
},
'decklink-input': {
name: $t('Blackmagic Device'),
Expand All @@ -117,6 +126,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'vr-capture.png',
supportList: ['OpenVR', 'SteamVR'],
icon: 'fab fa-simplybuilt fa-rotate-180',
group: 'capture',
},
screen_capture: {
name: $t('Screen Capture'),
Expand All @@ -125,6 +135,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
supportList: [$t('Most games, apps, displays')],
icon: 'icon-group',
shortDesc: $t('Capture games and apps'),
group: 'capture',
},
mac_screen_capture: {
name: $t('macOS Screen Capture'),
Expand All @@ -133,6 +144,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
supportList: [$t('Most games, apps, displays')],
icon: 'icon-group',
shortDesc: $t('Capture games and apps'),
group: 'capture',
},
liv_capture: {
name: $t('LIV Client Capture'),
Expand All @@ -142,6 +154,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'vr-capture.png',
supportList: ['LIV'],
icon: 'fab fa-simplybuilt fa-rotate-180',
group: 'capture',
},
wasapi_input_capture: {
name: $t('Audio Input Capture'),
Expand All @@ -151,6 +164,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'audio-input.png',
supportList: [$t('Built in microphones'), $t('USB microphones'), $t('Other USB devices')],
icon: 'icon-mic',
group: 'av',
},
wasapi_output_capture: {
name: $t('Audio Output Capture'),
Expand All @@ -160,6 +174,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'audio-output.png',
supportList: [$t('Desktop audio')],
icon: 'icon-audio',
group: 'av',
},
scene: {
name: $t('Scene'),
Expand All @@ -183,6 +198,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
name: $t('VLC Source'),
description: $t('Add playlists of videos to your scene.'),
icon: 'fas fa-play',
group: 'media',
},
coreaudio_input_capture: {
name: $t('Audio Input Capture'),
Expand All @@ -192,6 +208,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'audio-input.png',
supportList: [$t('Built in microphones'), $t('USB microphones'), $t('Other USB devices')],
icon: 'icon-mic',
group: 'av',
},
coreaudio_output_capture: {
name: $t('Audio Output Capture'),
Expand All @@ -201,6 +218,7 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
demoFilename: 'audio-output.png',
supportList: [$t('Desktop audio')],
icon: 'icon-audio',
group: 'av',
},
av_capture_input: {
name: $t('Camera / Capture Card'),
Expand All @@ -213,25 +231,29 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
$t('Capture cards (Elgato, Avermedia, BlackMagic)'),
],
icon: 'icon-webcam',
group: 'av',
},
display_capture: {
name: $t('Display Capture'),
description: $t('Capture your entire computer monitor.'),
demoFilename: 'display-capture.png',
supportList: [$t('Primary monitor'), $t('Secondary monitor')],
icon: 'fas fa-desktop',
group: 'capture',
},
'syphon-input': {
name: $t('Game Capture'),
description: $t("Capture a game you're playing on your computer."),
demoFilename: 'game-capture.png',
supportList: [$t('Built in works with most modern computer games')],
icon: 'fas fa-gamepad',
group: 'capture',
},
audio_line: {
name: $t('JACK Input Client'),
description: $t(''),
icon: 'fas fa-file',
group: 'av',
},
soundtrack_source: {
name: $t('Twitch Soundtrack'),
Expand Down Expand Up @@ -278,12 +300,14 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
),
demoFilename: 'source-collab-cam.png',
icon: 'icon-team-2',
group: 'av',
},
wasapi_process_output_capture: {
name: $t('Application Audio'),
shortDesc: $t('Beta'),
description: $t('Capture the audio coming from a specific application.'),
icon: 'fas fa-user',
group: 'av',
},
spout_capture: {
name: $t('Spout2 capture'),
Expand All @@ -296,5 +320,6 @@ export const SourceDisplayData = (): { [key: string]: ISourceDisplayData } => ({
icon: 'icon-face-masks-3',
link: 'https://streamlabs.com/content-hub/post/vtuber-support-on-streamlabs-desktop',
linkText: $t('Learn how to set it up'),
group: 'capture',
},
});

0 comments on commit 9987a4d

Please sign in to comment.