From 1e90b1836352680d4abc4bb2099b3833c7313f58 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Tue, 7 Jan 2025 11:08:28 +0100 Subject: [PATCH 01/15] display highlighter installation status --- app/components-react/pages/Highlighter.tsx | 6 +++- .../pages/PlatformAppStore.tsx | 24 ++++++++++---- app/components-react/shared/MenuItem.tsx | 31 ++++++++++--------- app/components-react/shared/SubMenu.tsx | 21 +++++++++++++ .../windows/settings/InstalledApps.tsx | 18 +++++++++++ 5 files changed, 79 insertions(+), 21 deletions(-) diff --git a/app/components-react/pages/Highlighter.tsx b/app/components-react/pages/Highlighter.tsx index 9a2978eeed60..faeb76258e61 100644 --- a/app/components-react/pages/Highlighter.tsx +++ b/app/components-react/pages/Highlighter.tsx @@ -24,7 +24,11 @@ export default function Highlighter(props: { params?: { view: string } }) { let initialViewState: IViewState; - if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterEnabled) { + if (props.params?.view) { + const view = + props.params?.view === 'settings' ? EHighlighterView.SETTINGS : EHighlighterView.STREAM; + initialViewState = { view }; + } else if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterEnabled) { initialViewState = { view: EHighlighterView.STREAM }; } else if (v.clipsAmount > 0) { initialViewState = { view: EHighlighterView.CLIPS, id: undefined }; diff --git a/app/components-react/pages/PlatformAppStore.tsx b/app/components-react/pages/PlatformAppStore.tsx index 6df6f0699073..39bb28d7b937 100644 --- a/app/components-react/pages/PlatformAppStore.tsx +++ b/app/components-react/pages/PlatformAppStore.tsx @@ -20,6 +20,10 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s getPlatformAppsUrl(); }, [p.params]); + useEffect(() => { + console.log('loooooool', platformAppsUrl); + }, [platformAppsUrl]); + function onBrowserViewReady(view: Electron.BrowserView) { new GuestApiHandler().exposeApi(view.webContents.id, { reloadProductionApps, @@ -64,16 +68,24 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s } async function navigateToApp(appId: string) { + console.log('looooool', appId); + NavigationService.actions.navigate('PlatformAppMainPage', { appId }); } if (!platformAppsUrl) return <>; return ( - + <> + + + ); } diff --git a/app/components-react/shared/MenuItem.tsx b/app/components-react/shared/MenuItem.tsx index 8f6e27c7dc23..2c4e9afd0cbe 100644 --- a/app/components-react/shared/MenuItem.tsx +++ b/app/components-react/shared/MenuItem.tsx @@ -14,19 +14,22 @@ export default function Menutem(p: IMenuProps) { const { title, style, type = 'item' } = p; return ( -
- - {p.children} - -
+ <> + {/* type: {type} */} +
+ + {p.children} + +
+ ); } diff --git a/app/components-react/shared/SubMenu.tsx b/app/components-react/shared/SubMenu.tsx index 762fdcf6a548..e41b9bb46699 100644 --- a/app/components-react/shared/SubMenu.tsx +++ b/app/components-react/shared/SubMenu.tsx @@ -2,6 +2,9 @@ import React, { CSSProperties } from 'react'; import { Menu, MenuItemProps, SubMenuProps } from 'antd'; import styles from './SubMenu.m.less'; import cx from 'classnames'; +import MenuItem from './MenuItem'; +import { Services } from 'components-react/service-provider'; +import { EMenuItemKey } from 'services/side-nav'; interface ISubMenuProps extends SubMenuProps, MenuItemProps { title?: string; @@ -12,11 +15,29 @@ interface ISubMenuProps extends SubMenuProps, MenuItemProps { export default function SubMenu(p: ISubMenuProps) { const { title, style } = p; + const { NavigationService } = Services; + return (
+ {/* submenu: {title} */} {p.children} + + {/* TODO: Below should only be displayed if Ai highlighter is installed. +Needs to be differently handled bcs it is not a web app like the app store apps */} + { + NavigationService.actions.navigate( + 'Highlighter', + { view: 'settings' }, + EMenuItemKey.Highlighter, + ); + }} + > + AI Highlighter +
); } diff --git a/app/components-react/windows/settings/InstalledApps.tsx b/app/components-react/windows/settings/InstalledApps.tsx index fb076dc704dd..60855886cf7b 100644 --- a/app/components-react/windows/settings/InstalledApps.tsx +++ b/app/components-react/windows/settings/InstalledApps.tsx @@ -89,6 +89,24 @@ export default function InstalledApps() { ))} + + + {' '} + Beta + {/* -{' '} */} + + {'AI Highlighter'} + {'0.0.30'} + + + + From 25f8b52a64192c5cb3f0d760a2ce67145e43d69a Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Thu, 9 Jan 2025 16:17:03 +0100 Subject: [PATCH 02/15] installed apps checking for highlighter version --- .../windows/settings/InstalledApps.tsx | 45 ++++++++++--------- app/services/highlighter/index.ts | 7 +++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/app/components-react/windows/settings/InstalledApps.tsx b/app/components-react/windows/settings/InstalledApps.tsx index 60855886cf7b..701ffc4e3653 100644 --- a/app/components-react/windows/settings/InstalledApps.tsx +++ b/app/components-react/windows/settings/InstalledApps.tsx @@ -8,10 +8,11 @@ import styles from './InstalledApps.m.less'; import { useVuex } from 'components-react/hooks'; export default function InstalledApps() { - const { PlatformAppsService } = Services; + const { PlatformAppsService, HighlighterService } = Services; - const { installedApps } = useVuex(() => ({ + const { installedApps, highlighterVersion } = useVuex(() => ({ installedApps: PlatformAppsService.views.productionApps, + highlighterVersion: HighlighterService.views.highlighterVersion, })); const enabledInstalledAppIds = installedApps.filter(app => app.enabled).map(app => app.id); @@ -89,24 +90,28 @@ export default function InstalledApps() { ))} - - - {' '} - Beta - {/* -{' '} */} - - {'AI Highlighter'} - {'0.0.30'} - - - - + {highlighterVersion !== '' && ( + + + -{' '} + + {'AI Highlighter'} + {highlighterVersion} + + + + + )} diff --git a/app/services/highlighter/index.ts b/app/services/highlighter/index.ts index 28a93fd12ee3..bc09c289c158 100644 --- a/app/services/highlighter/index.ts +++ b/app/services/highlighter/index.ts @@ -564,6 +564,7 @@ export class HighlighterService extends PersistentStatefulService Date: Fri, 10 Jan 2025 09:58:08 +0100 Subject: [PATCH 03/15] Better way to display aiHighlighter in sidemenu --- app/components-react/shared/MenuItem.tsx | 1 - app/components-react/shared/SubMenu.tsx | 21 ----------- app/components-react/sidebar/AppsNav.tsx | 48 +++++++++++++++++++++--- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/app/components-react/shared/MenuItem.tsx b/app/components-react/shared/MenuItem.tsx index 2c4e9afd0cbe..a06d7ff21284 100644 --- a/app/components-react/shared/MenuItem.tsx +++ b/app/components-react/shared/MenuItem.tsx @@ -15,7 +15,6 @@ export default function Menutem(p: IMenuProps) { return ( <> - {/* type: {type} */}
- {/* submenu: {title} */} {p.children} - - {/* TODO: Below should only be displayed if Ai highlighter is installed. -Needs to be differently handled bcs it is not a web app like the app store apps */} - { - NavigationService.actions.navigate( - 'Highlighter', - { view: 'settings' }, - EMenuItemKey.Highlighter, - ); - }} - > - AI Highlighter -
); } diff --git a/app/components-react/sidebar/AppsNav.tsx b/app/components-react/sidebar/AppsNav.tsx index 4b18ca42b00c..b6e06edb9a7c 100644 --- a/app/components-react/sidebar/AppsNav.tsx +++ b/app/components-react/sidebar/AppsNav.tsx @@ -1,26 +1,58 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import styles from './SideNav.m.less'; import { useVuex } from 'components-react/hooks'; import { $t } from 'services/i18n'; import { Services } from 'components-react/service-provider'; import MenuItem from 'components-react/shared/MenuItem'; -import { EAppPageSlot } from 'services/platform-apps'; +import { EAppPageSlot, ILoadedApp } from 'services/platform-apps'; import { Menu } from 'util/menus/Menu'; import cx from 'classnames'; +import Highlighter from 'components-react/pages/Highlighter'; +import { EMenuItemKey } from 'services/side-nav'; interface IAppsNav { type?: 'enabled' | 'selected'; } export default function AppsNav(p: IAppsNav) { - const { NavigationService, PlatformAppsService, SideNavService } = Services; + const { NavigationService, PlatformAppsService, SideNavService, HighlighterService } = Services; const { type = 'selected' } = p; + const aiHighlighterApp: ILoadedApp = { + id: 'AiHighlighter', + manifest: { + name: 'AI Highlighter', + version: 'xxx', + buildPath: 'xxx', + permissions: [], + sources: [], + pages: [ + { + slot: EAppPageSlot.TopNav, + file: 'xxx', + }, + ], + authorizationUrls: [], + mediaDomains: [], + icon: 'xxx', + }, + unpacked: false, + beta: true, + appToken: 'xxx', + poppedOutSlots: [], + appPath: 'xxx', + enabled: true, + icon: 'xxx', + }; + const { currentMenuItem, apps, isOpen, navigateApp, enabledApps } = useVuex(() => ({ currentMenuItem: SideNavService.views.currentMenuItem, apps: SideNavService.views.apps, isOpen: SideNavService.views.isOpen, navigateApp: NavigationService.actions.navigateApp, - enabledApps: PlatformAppsService.views.enabledApps + enabledApps: (HighlighterService.views.highlighterVersion !== '' + ? [...PlatformAppsService.views.enabledApps, aiHighlighterApp] + : PlatformAppsService.views.enabledApps + ) .filter(app => { return !!app?.manifest?.pages.find(page => { return page.slot === EAppPageSlot.TopNav; @@ -109,7 +141,13 @@ export default function AppsNav(p: IAppsNav) { currentMenuItem === `sub-${app?.id}` && styles.active, )} title={app.manifest?.name} - onClick={() => app?.id && navigateApp(app?.id, `sub-${app?.id}`)} + onClick={() => { + if (app.id === 'AiHighlighter') { + NavigationService.navigate('Highlighter', {}, EMenuItemKey.Highlighter); + } else { + app?.id && navigateApp(app?.id, `sub-${app?.id}`); + } + }} type="submenu" onContextMenu={e => showContextMenu(e, app?.id)} draggable From 9825bfccf019d155982d8680b7ec6e8e68507d08 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Fri, 10 Jan 2025 12:26:00 +0100 Subject: [PATCH 04/15] Added to plattform apps --- .../pages/PlatformAppStore.tsx | 94 ++++++++++++++++--- app/components-react/shared/BrowserView.tsx | 39 +++++++- .../windows/settings/InstalledApps.tsx | 5 +- 3 files changed, 120 insertions(+), 18 deletions(-) diff --git a/app/components-react/pages/PlatformAppStore.tsx b/app/components-react/pages/PlatformAppStore.tsx index 39bb28d7b937..e14a0189898f 100644 --- a/app/components-react/pages/PlatformAppStore.tsx +++ b/app/components-react/pages/PlatformAppStore.tsx @@ -1,15 +1,25 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import Utils from 'services/utils'; import urlLib from 'url'; import BrowserView from 'components-react/shared/BrowserView'; import { GuestApiHandler } from 'util/guest-api-handler'; import * as remote from '@electron/remote'; import { Services } from 'components-react/service-provider'; +import { Button } from 'antd'; +import { EMenuItemKey } from 'services/side-nav'; +import { $t } from 'services/i18n'; export default function PlatformAppStore(p: { params: { appId?: string; type?: string } }) { - const { UserService, PlatformAppsService, PlatformAppStoreService, NavigationService } = Services; + const { + UserService, + PlatformAppsService, + PlatformAppStoreService, + NavigationService, + HighlighterService, + } = Services; const [platformAppsUrl, setPlatformAppsUrl] = useState(''); - + const [currentUrl, setCurrentUrl] = useState(''); + const browser = useRef(null); useEffect(() => { async function getPlatformAppsUrl() { const url = await UserService.views.appStoreUrl(p.params); @@ -20,10 +30,6 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s getPlatformAppsUrl(); }, [p.params]); - useEffect(() => { - console.log('loooooool', platformAppsUrl); - }, [platformAppsUrl]); - function onBrowserViewReady(view: Electron.BrowserView) { new GuestApiHandler().exposeApi(view.webContents.id, { reloadProductionApps, @@ -68,8 +74,6 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s } async function navigateToApp(appId: string) { - console.log('looooool', appId); - NavigationService.actions.navigate('PlatformAppMainPage', { appId }); } @@ -77,15 +81,77 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s return ( <> { + setCurrentUrl(url); + }} /> - + {currentUrl.includes('installed-apps') && ( +
+
Other installed apps:
+
+
+

AI Highlighter

+

by Streamlabs

+
+
+ + + +
+
+
+ )} ); } diff --git a/app/components-react/shared/BrowserView.tsx b/app/components-react/shared/BrowserView.tsx index fa77501a91d4..fe01a1e9f362 100644 --- a/app/components-react/shared/BrowserView.tsx +++ b/app/components-react/shared/BrowserView.tsx @@ -8,6 +8,7 @@ import Spinner from 'components-react/shared/Spinner'; import { Services } from 'components-react/service-provider'; import electron from 'electron'; import { onUnload } from 'util/unload'; +import { Button } from 'antd'; interface BrowserViewProps { src: string; @@ -18,6 +19,7 @@ interface BrowserViewProps { onReady?: (view: any) => void; className?: string; style?: React.CSSProperties; + emitUrlChange?: (url: string) => void; } export default function BrowserView(p: BrowserViewProps) { @@ -52,17 +54,26 @@ export default function BrowserView(p: BrowserViewProps) { const browserView = useRef(null); + function urlChange(_: any, url: string) { + if (p.emitUrlChange) { + p.emitUrlChange(url); + } + } + useEffect(() => { browserView.current = new remote.BrowserView(options); + const webContents = browserView.current.webContents; if (p.enableGuestApi) { - electron.ipcRenderer.sendSync('webContents-enableRemote', browserView.current.webContents.id); + electron.ipcRenderer.sendSync('webContents-enableRemote', webContents.id); } if (p.onReady) p.onReady(browserView.current); if (p.setLocale) I18nService.setBrowserViewLocale(browserView.current); - browserView.current.webContents.on('did-finish-load', () => setLoading(false)); + webContents.on('did-finish-load', () => setLoading(false)); + webContents.on('did-navigate', urlChange); + webContents.on('did-navigate-in-page', urlChange); remote.getCurrentWindow().addBrowserView(browserView.current); const shutdownSubscription = AppService.shutdownStarted.subscribe(destroyBrowserView); @@ -70,6 +81,8 @@ export default function BrowserView(p: BrowserViewProps) { const cancelUnload = onUnload(() => destroyBrowserView()); return () => { + webContents.removeListener('did-navigate', urlChange); + webContents.removeListener('did-navigate-in-page', urlChange); cancelUnload(); destroyBrowserView(); shutdownSubscription.unsubscribe(); @@ -169,5 +182,25 @@ export default function BrowserView(p: BrowserViewProps) { ); } - return
; + function getCurrentUrl() { + if (!browserView.current) { + console.log('BrowserView: browserView.current is null'); + return ''; + } + return browserView.current!.webContents.getURL(); + } + + return ( + <> + +
+ + ); } diff --git a/app/components-react/windows/settings/InstalledApps.tsx b/app/components-react/windows/settings/InstalledApps.tsx index 701ffc4e3653..43df37a62769 100644 --- a/app/components-react/windows/settings/InstalledApps.tsx +++ b/app/components-react/windows/settings/InstalledApps.tsx @@ -103,11 +103,14 @@ export default function InstalledApps() { {highlighterVersion} From f4c634f1324bb3d2b92a8280a7878c5c7293cae1 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Fri, 10 Jan 2025 12:37:57 +0100 Subject: [PATCH 05/15] renamed aiHighlighter featureflag --- app/components-react/highlighter/ClipsView.tsx | 4 ++-- app/components-react/highlighter/SettingsView.tsx | 11 +++++++---- app/components-react/pages/Highlighter.tsx | 12 ++++++------ app/components-react/pages/RecordingHistory.tsx | 4 ++-- app/components-react/sidebar/FeaturesNav.tsx | 4 ++-- .../windows/go-live/CommonPlatformFields.tsx | 10 ++++++---- .../go-live/platforms/TwitchEditStreamInfo.tsx | 4 ++-- app/services/highlighter/index.ts | 10 +++++----- 8 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/components-react/highlighter/ClipsView.tsx b/app/components-react/highlighter/ClipsView.tsx index 870cd0fd9dfe..b305260b2a8d 100644 --- a/app/components-react/highlighter/ClipsView.tsx +++ b/app/components-react/highlighter/ClipsView.tsx @@ -38,7 +38,7 @@ export default function ClipsView({ emitSetView: (data: IViewState) => void; }) { const { HighlighterService, UsageStatisticsService, IncrementalRolloutService } = Services; - const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const clipsAmount = useVuex(() => HighlighterService.views.clips.length); @@ -215,7 +215,7 @@ export default function ClipsView({ }} /> {streamId && - aiHighlighterEnabled && + aiHighlighterFeatureEnabled && HighlighterService.getClips(HighlighterService.views.clips, props.id) .filter(clip => clip.source === 'AiClip') .every(clip => (clip as IAiClip).aiInfo.metadata?.round) && ( diff --git a/app/components-react/highlighter/SettingsView.tsx b/app/components-react/highlighter/SettingsView.tsx index e5193866be2d..45a01a502f93 100644 --- a/app/components-react/highlighter/SettingsView.tsx +++ b/app/components-react/highlighter/SettingsView.tsx @@ -28,7 +28,7 @@ export default function SettingsView({ HighlighterService, IncrementalRolloutService, } = Services; - const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const [hotkey, setHotkey] = useState(null); @@ -38,6 +38,7 @@ export default function SettingsView({ settingsValues: SettingsService.views.values, isStreaming: StreamingService.isStreaming, useAiHighlighter: HighlighterService.views.useAiHighlighter, + highlighterVersion: HighlighterService.views.highlighterVersion, })); const correctlyConfigured = @@ -124,7 +125,7 @@ export default function SettingsView({

- {aiHighlighterEnabled && ( + {aiHighlighterFeatureEnabled && ( @@ -139,7 +140,7 @@ export default function SettingsView({
- {aiHighlighterEnabled && ( + {aiHighlighterFeatureEnabled && (
@@ -166,7 +167,9 @@ export default function SettingsView({ )}

- {aiHighlighterEnabled ? 'Or use the manual highlighter ' : 'Manual highlighter'} + {aiHighlighterFeatureEnabled + ? 'Or use the manual highlighter ' + : 'Manual highlighter'}

{$t( diff --git a/app/components-react/pages/Highlighter.tsx b/app/components-react/pages/Highlighter.tsx index faeb76258e61..984b906046d2 100644 --- a/app/components-react/pages/Highlighter.tsx +++ b/app/components-react/pages/Highlighter.tsx @@ -10,7 +10,7 @@ import { EAvailableFeatures } from 'services/incremental-rollout'; export default function Highlighter(props: { params?: { view: string } }) { const { HighlighterService, IncrementalRolloutService } = Services; - const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const v = useVuex(() => ({ @@ -28,7 +28,7 @@ export default function Highlighter(props: { params?: { view: string } }) { const view = props.params?.view === 'settings' ? EHighlighterView.SETTINGS : EHighlighterView.STREAM; initialViewState = { view }; - } else if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterEnabled) { + } else if (v.streamAmount > 0 && v.clipsAmount > 0 && aiHighlighterFeatureEnabled) { initialViewState = { view: EHighlighterView.STREAM }; } else if (v.clipsAmount > 0) { initialViewState = { view: EHighlighterView.CLIPS, id: undefined }; @@ -41,7 +41,7 @@ export default function Highlighter(props: { params?: { view: string } }) { async function shouldUpdate() { if (!HighlighterService.aiHighlighterUpdater) return false; const versionAvailable = await HighlighterService.aiHighlighterUpdater.isNewVersionAvailable(); - return versionAvailable && aiHighlighterEnabled && v.useAiHighlighter; + return versionAvailable && aiHighlighterFeatureEnabled && v.useAiHighlighter; } shouldUpdate().then(shouldUpdate => { @@ -62,7 +62,7 @@ export default function Highlighter(props: { params?: { view: string } }) { case EHighlighterView.STREAM: return ( <> - {aiHighlighterEnabled && updaterModal} + {aiHighlighterFeatureEnabled && updaterModal} { setViewFromEmit(data); @@ -73,7 +73,7 @@ export default function Highlighter(props: { params?: { view: string } }) { case EHighlighterView.CLIPS: return ( <> - {aiHighlighterEnabled && updaterModal} + {aiHighlighterFeatureEnabled && updaterModal} { setViewFromEmit(data); @@ -90,7 +90,7 @@ export default function Highlighter(props: { params?: { view: string } }) { default: return ( <> - {aiHighlighterEnabled && updaterModal} + {aiHighlighterFeatureEnabled && updaterModal} { HighlighterService.actions.dismissTutorial(); diff --git a/app/components-react/pages/RecordingHistory.tsx b/app/components-react/pages/RecordingHistory.tsx index ebcde3deb4a8..4cdcb5b95e33 100644 --- a/app/components-react/pages/RecordingHistory.tsx +++ b/app/components-react/pages/RecordingHistory.tsx @@ -200,7 +200,7 @@ export default function RecordingHistoryPage() { export function RecordingHistory() { const controller = useController(RecordingHistoryCtx); const { formattedTimestamp, showFile, handleSelect, postError } = controller; - const aiHighlighterEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const { uploadInfo, uploadOptions, recordings, hasSLID, aiDetectionInProgress } = useVuex(() => ({ @@ -231,7 +231,7 @@ export function RecordingHistory() { {uploadOptions .map(option => { - if (option.value === 'highlighter' && !aiHighlighterEnabled) { + if (option.value === 'highlighter' && !aiHighlighterFeatureEnabled) { return null; } return ( diff --git a/app/components-react/sidebar/FeaturesNav.tsx b/app/components-react/sidebar/FeaturesNav.tsx index 07b3fc5da590..39cad367edbd 100644 --- a/app/components-react/sidebar/FeaturesNav.tsx +++ b/app/components-react/sidebar/FeaturesNav.tsx @@ -267,7 +267,7 @@ function FeaturesNavItem(p: { DualOutputService, IncrementalRolloutService, } = Services; - const aiHighlighterEnabled = IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const { isSubMenuItem, menuItem, handleNavigation, className } = p; @@ -324,7 +324,7 @@ function FeaturesNavItem(p: { >

{title} - {menuItem.key === EMenuItemKey.Highlighter && aiHighlighterEnabled && ( + {menuItem.key === EMenuItemKey.Highlighter && aiHighlighterFeatureEnabled && (

{highlighterEnvironment === 'production' ? 'beta' : highlighterEnvironment} diff --git a/app/components-react/windows/go-live/CommonPlatformFields.tsx b/app/components-react/windows/go-live/CommonPlatformFields.tsx index d76d2d4f014a..c7316f8f144f 100644 --- a/app/components-react/windows/go-live/CommonPlatformFields.tsx +++ b/app/components-react/windows/go-live/CommonPlatformFields.tsx @@ -58,7 +58,7 @@ export const CommonPlatformFields = InputComponent((rawProps: IProps) => { } const view = Services.StreamingService.views; - const aiHighlighterEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); const hasCustomCheckbox = p.layoutMode === 'multiplatformAdvanced'; @@ -132,9 +132,11 @@ export const CommonPlatformFields = InputComponent((rawProps: IProps) => { /> )} - {aiHighlighterEnabled && enabledPlatforms && !enabledPlatforms.includes('twitch') && ( - - )} + {aiHighlighterFeatureEnabled && + enabledPlatforms && + !enabledPlatforms.includes('twitch') && ( + + )}

)} diff --git a/app/components-react/windows/go-live/platforms/TwitchEditStreamInfo.tsx b/app/components-react/windows/go-live/platforms/TwitchEditStreamInfo.tsx index d3c5ddff9d26..8c484cb0bb2a 100644 --- a/app/components-react/windows/go-live/platforms/TwitchEditStreamInfo.tsx +++ b/app/components-react/windows/go-live/platforms/TwitchEditStreamInfo.tsx @@ -18,7 +18,7 @@ import { EAvailableFeatures } from 'services/incremental-rollout'; export function TwitchEditStreamInfo(p: IPlatformComponentParams<'twitch'>) { const twSettings = p.value; - const aiHighlighterEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( + const aiHighlighterFeatureEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); function updateSettings(patch: Partial) { @@ -52,7 +52,7 @@ export function TwitchEditStreamInfo(p: IPlatformComponentParams<'twitch'>) { requiredFields={ - {aiHighlighterEnabled && ( + {aiHighlighterFeatureEnabled && ( )} diff --git a/app/services/highlighter/index.ts b/app/services/highlighter/index.ts index bc09c289c158..c8ba5411ddfa 100644 --- a/app/services/highlighter/index.ts +++ b/app/services/highlighter/index.ts @@ -552,7 +552,7 @@ export class HighlighterService extends PersistentStatefulService { - this.aiHighlighterEnabled = this.incrementalRolloutService.views.featureIsEnabled( + this.aiHighlighterFeatureEnabled = this.incrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); - if (this.aiHighlighterEnabled && !this.aiHighlighterUpdater) { + if (this.aiHighlighterFeatureEnabled && !this.aiHighlighterUpdater) { this.aiHighlighterUpdater = new AiHighlighterUpdater(); } }); @@ -808,7 +808,7 @@ export class HighlighterService extends PersistentStatefulService { - if (this.aiHighlighterEnabled === false) { + if (this.aiHighlighterFeatureEnabled === false) { console.log('HighlighterService: Not enabled'); return; } From 1b3f060d5eac5c3c99fc333232f9225a855ae7c8 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Fri, 10 Jan 2025 17:27:51 +0100 Subject: [PATCH 06/15] Hook up updater to start after install + smaller stuff --- .../highlighter/SettingsView.tsx | 24 ++++++++++---- .../pages/PlatformAppStore.tsx | 15 +++++++-- app/components-react/sidebar/AppsNav.tsx | 6 +++- .../windows/go-live/AiHighlighterToggle.tsx | 33 +++++++++++++++---- .../windows/settings/InstalledApps.tsx | 2 +- app/services/highlighter/index.ts | 16 +++++++++ 6 files changed, 78 insertions(+), 18 deletions(-) diff --git a/app/components-react/highlighter/SettingsView.tsx b/app/components-react/highlighter/SettingsView.tsx index 45a01a502f93..784519451a8e 100644 --- a/app/components-react/highlighter/SettingsView.tsx +++ b/app/components-react/highlighter/SettingsView.tsx @@ -156,12 +156,24 @@ export default function SettingsView({ )}

- + {v.highlighterVersion !== '' ? ( + + ) : ( + + )}
{$t('Recommended')}
)} diff --git a/app/components-react/pages/PlatformAppStore.tsx b/app/components-react/pages/PlatformAppStore.tsx index e14a0189898f..8e438ac5aa69 100644 --- a/app/components-react/pages/PlatformAppStore.tsx +++ b/app/components-react/pages/PlatformAppStore.tsx @@ -17,6 +17,9 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s NavigationService, HighlighterService, } = Services; + const [highlighterInstalled, setHighlighterInstalled] = useState( + HighlighterService.views.highlighterVersion !== '', + ); const [platformAppsUrl, setPlatformAppsUrl] = useState(''); const [currentUrl, setCurrentUrl] = useState(''); const browser = useRef(null); @@ -86,7 +89,12 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s top: 0, right: 0, left: 0, - height: `calc(100% - ${currentUrl.includes('installed-apps') ? '72' : '0'}px)`, + height: `calc(100% - ${ + currentUrl.includes('installed-apps') && + HighlighterService.views.highlighterVersion !== '' + ? '72' + : '0' + }px)`, }} src={platformAppsUrl} onReady={onBrowserViewReady} @@ -95,7 +103,7 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s setCurrentUrl(url); }} /> - {currentUrl.includes('installed-apps') && ( + {currentUrl.includes('installed-apps') && highlighterInstalled && (
{ - HighlighterService.UNINSTALL_HIGHLIGHTER(); + setHighlighterInstalled(false); + HighlighterService.uninstallAiHighlighter(); }} > {$t('Uninstall')} diff --git a/app/components-react/sidebar/AppsNav.tsx b/app/components-react/sidebar/AppsNav.tsx index b6e06edb9a7c..5a1da0fee145 100644 --- a/app/components-react/sidebar/AppsNav.tsx +++ b/app/components-react/sidebar/AppsNav.tsx @@ -143,7 +143,11 @@ export default function AppsNav(p: IAppsNav) { title={app.manifest?.name} onClick={() => { if (app.id === 'AiHighlighter') { - NavigationService.navigate('Highlighter', {}, EMenuItemKey.Highlighter); + NavigationService.navigate( + 'Highlighter', + { view: 'settings' }, + EMenuItemKey.Highlighter, + ); } else { app?.id && navigateApp(app?.id, `sub-${app?.id}`); } diff --git a/app/components-react/windows/go-live/AiHighlighterToggle.tsx b/app/components-react/windows/go-live/AiHighlighterToggle.tsx index b763534cda5a..bb147586d871 100644 --- a/app/components-react/windows/go-live/AiHighlighterToggle.tsx +++ b/app/components-react/windows/go-live/AiHighlighterToggle.tsx @@ -6,6 +6,7 @@ import { Services } from 'components-react/service-provider'; import Highlighter from 'components-react/pages/Highlighter'; import { useVuex } from 'components-react/hooks'; import { DownOutlined, UpOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; export default function AiHighlighterToggle({ game, @@ -16,7 +17,12 @@ export default function AiHighlighterToggle({ }) { //TODO M: Probably good way to integrate the highlighter in to GoLiveSettings const { HighlighterService } = Services; - const useHighlighter = useVuex(() => HighlighterService.views.useAiHighlighter); + const { useHighlighter, highlighterVersion } = useVuex(() => { + return { + useHighlighter: HighlighterService.views.useAiHighlighter, + highlighterVersion: HighlighterService.views.highlighterVersion, + }; + }); function getInitialExpandedState() { if (game === 'Fortnite') { @@ -78,12 +84,25 @@ export default function AiHighlighterToggle({
Beta
- HighlighterService.actions.toggleAiHighlighter()} - /> + {highlighterVersion !== '' ? ( + HighlighterService.actions.toggleAiHighlighter()} + /> + ) : ( + + )}
diff --git a/app/components-react/windows/settings/InstalledApps.tsx b/app/components-react/windows/settings/InstalledApps.tsx index 43df37a62769..54953fb4c6fd 100644 --- a/app/components-react/windows/settings/InstalledApps.tsx +++ b/app/components-react/windows/settings/InstalledApps.tsx @@ -104,7 +104,7 @@ export default function InstalledApps() {
-
!aiDetectionInProgress && setShowModal({ type: 'upload' })} - > - - {$t('Select your Fortnite recording')} - -
+ {v.highlighterVersion !== '' && ( +
!aiDetectionInProgress && setShowModal({ type: 'upload' })} + > + + {$t('Select your Fortnite recording')} + +
+ )} @@ -297,7 +302,9 @@ export default function StreamView({ emitSetView }: { emitSetView: (data: IViewS keyboard={false} > {!!v.error && } - {showModal?.type === 'upload' && } + {showModal?.type === 'upload' && v.highlighterVersion !== '' && ( + + )} {showModal?.type === 'export' && } {showModal?.type === 'preview' && ( From f4565bb8c6dd840a0a7734582c9a6612f6ef6ba3 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Mon, 13 Jan 2025 15:58:48 +0100 Subject: [PATCH 10/15] check if highlighter is installed in recordingView --- .../pages/RecordingHistory.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/components-react/pages/RecordingHistory.tsx b/app/components-react/pages/RecordingHistory.tsx index 4cdcb5b95e33..0c22f2897615 100644 --- a/app/components-react/pages/RecordingHistory.tsx +++ b/app/components-react/pages/RecordingHistory.tsx @@ -64,6 +64,10 @@ class RecordingHistoryController { ); } + get highlighterVersion() { + return this.HighlighterService.views.highlighterVersion; + } + get uploadOptions() { const opts = [ { @@ -203,12 +207,20 @@ export function RecordingHistory() { const aiHighlighterFeatureEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); - const { uploadInfo, uploadOptions, recordings, hasSLID, aiDetectionInProgress } = useVuex(() => ({ + const { + uploadInfo, + uploadOptions, + recordings, + hasSLID, + aiDetectionInProgress, + highlighterVersion, + } = useVuex(() => ({ recordings: controller.recordings, aiDetectionInProgress: controller.aiDetectionInProgress, uploadOptions: controller.uploadOptions, uploadInfo: controller.uploadInfo, hasSLID: controller.hasSLID, + highlighterVersion: controller.highlighterVersion, })); useEffect(() => { @@ -231,7 +243,10 @@ export function RecordingHistory() { {uploadOptions .map(option => { - if (option.value === 'highlighter' && !aiHighlighterFeatureEnabled) { + if ( + option.value === 'highlighter' || + (!aiHighlighterFeatureEnabled && highlighterVersion === '') + ) { return null; } return ( From e5c68f7d9baf19448a34dbcbdfa7d3a57013063c Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Mon, 13 Jan 2025 15:58:48 +0100 Subject: [PATCH 11/15] check if highlighter is installed in recordingView --- .../pages/RecordingHistory.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/components-react/pages/RecordingHistory.tsx b/app/components-react/pages/RecordingHistory.tsx index 4cdcb5b95e33..81e0e43c7f6a 100644 --- a/app/components-react/pages/RecordingHistory.tsx +++ b/app/components-react/pages/RecordingHistory.tsx @@ -64,6 +64,10 @@ class RecordingHistoryController { ); } + get highlighterVersion() { + return this.HighlighterService.views.highlighterVersion; + } + get uploadOptions() { const opts = [ { @@ -203,12 +207,20 @@ export function RecordingHistory() { const aiHighlighterFeatureEnabled = Services.IncrementalRolloutService.views.featureIsEnabled( EAvailableFeatures.aiHighlighter, ); - const { uploadInfo, uploadOptions, recordings, hasSLID, aiDetectionInProgress } = useVuex(() => ({ + const { + uploadInfo, + uploadOptions, + recordings, + hasSLID, + aiDetectionInProgress, + highlighterVersion, + } = useVuex(() => ({ recordings: controller.recordings, aiDetectionInProgress: controller.aiDetectionInProgress, uploadOptions: controller.uploadOptions, uploadInfo: controller.uploadInfo, hasSLID: controller.hasSLID, + highlighterVersion: controller.highlighterVersion, })); useEffect(() => { @@ -231,7 +243,10 @@ export function RecordingHistory() { {uploadOptions .map(option => { - if (option.value === 'highlighter' && !aiHighlighterFeatureEnabled) { + if ( + option.value === 'highlighter' && + (!aiHighlighterFeatureEnabled || highlighterVersion === '') + ) { return null; } return ( From 221bdd6c3b3c42b66a79f021780d1d852a783d68 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Tue, 14 Jan 2025 11:43:28 +0100 Subject: [PATCH 12/15] wording changes --- app/components-react/highlighter/SettingsView.tsx | 13 ++++++++++--- app/i18n/en-US/highlighter.json | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/components-react/highlighter/SettingsView.tsx b/app/components-react/highlighter/SettingsView.tsx index 784519451a8e..f9638c7b7f1f 100644 --- a/app/components-react/highlighter/SettingsView.tsx +++ b/app/components-react/highlighter/SettingsView.tsx @@ -153,6 +153,13 @@ export default function SettingsView({

{$t( 'Automatically capture the best moments from your livestream and turn them into a highlight video.', + )}{' '} + {v.highlighterVersion !== '' && ( + + {$t( + 'The AI Highlighter App can be managed in the Apps Manager tab or in Settings > Installed apps.', + )} + )}

@@ -171,7 +178,7 @@ export default function SettingsView({ HighlighterService.installAiHighlighter(true); }} > - Install AI Highlighter + {$t('Install AI Highlighter App')} )}
{$t('Recommended')}
@@ -180,8 +187,8 @@ export default function SettingsView({

{aiHighlighterFeatureEnabled - ? 'Or use the manual highlighter ' - : 'Manual highlighter'} + ? $t('Or, use the built-in manual highlighter') + : $t('Built-in manual highlighter')}

{$t( diff --git a/app/i18n/en-US/highlighter.json b/app/i18n/en-US/highlighter.json index 87ff899eb2d6..ddeeaa0420fa 100644 --- a/app/i18n/en-US/highlighter.json +++ b/app/i18n/en-US/highlighter.json @@ -104,6 +104,10 @@ "Recordings": "Recordings", "Manual Highlighter": "Manual Highlighter", "Manually capture the best moments from your livestream with a hotkey command, and automatically turn them into a highlight video.": "Manually capture the best moments from your livestream with a hotkey command, and automatically turn them into a highlight video.", + "The AI Highlighter App can be managed in the Apps Manager tab or in Settings > Installed apps.": "The AI Highlighter App can be managed in the Apps Manager tab or in Settings > Installed apps.", + "Install AI Highlighter App": "Install AI Highlighter App", + "Or, use the built-in manual highlighter": "Or, use the built-in manual highlighter", + "Built-in manual highlighter": "Built-in manual highlighter", "End your stream to change the Hotkey or the replay duration.": "End your stream to change the Hotkey or the replay duration.", "No clips found": "No clips found", "All highlight clips": "All highlight clips", From 1486c9343605e5ba0ad273c83bfb0ec51d478286 Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Tue, 14 Jan 2025 12:14:27 +0100 Subject: [PATCH 13/15] PR comments fixed --- .../highlighter/SettingsView.tsx | 2 +- .../pages/PlatformAppStore.m.less | 35 +++++++++++++++++++ .../pages/PlatformAppStore.tsx | 35 ++++--------------- app/components-react/shared/BrowserView.tsx | 3 +- .../windows/settings/InstalledApps.m.less | 9 +++++ .../windows/settings/InstalledApps.tsx | 11 +----- app/services/highlighter/index.ts | 10 ++---- 7 files changed, 55 insertions(+), 50 deletions(-) create mode 100644 app/components-react/pages/PlatformAppStore.m.less diff --git a/app/components-react/highlighter/SettingsView.tsx b/app/components-react/highlighter/SettingsView.tsx index f9638c7b7f1f..22fabb6c7f57 100644 --- a/app/components-react/highlighter/SettingsView.tsx +++ b/app/components-react/highlighter/SettingsView.tsx @@ -175,7 +175,7 @@ export default function SettingsView({ style={{ width: 'fit-content' }} type="primary" onClick={() => { - HighlighterService.installAiHighlighter(true); + HighlighterService.actions.installAiHighlighter(true); }} > {$t('Install AI Highlighter App')} diff --git a/app/components-react/pages/PlatformAppStore.m.less b/app/components-react/pages/PlatformAppStore.m.less new file mode 100644 index 000000000000..3ac00a35aa8c --- /dev/null +++ b/app/components-react/pages/PlatformAppStore.m.less @@ -0,0 +1,35 @@ +@import '../../styles/index.less'; + +.browser-view { + position: absolute; + top: 0; + right: 0; + left: 0; +} + +.other-installed-apps-wrapper { + display: flex; + gap: 16px; + position: absolute; + bottom: 0; + height: 72px; + align-items: center; + padding: 8px; +} + +.other-app-wrapper { + border-radius: 8px; + border: 1px solid #30383d; + display: flex; + gap: 16px; + padding: 8px; + padding-left: 16px; + justify-content: space-between; + align-items: center; +} + +.text-wrapper { + display: flex; + gap: 8px; + align-items: center; +} diff --git a/app/components-react/pages/PlatformAppStore.tsx b/app/components-react/pages/PlatformAppStore.tsx index 1f2aa7d2ef3f..2d6b88e730d6 100644 --- a/app/components-react/pages/PlatformAppStore.tsx +++ b/app/components-react/pages/PlatformAppStore.tsx @@ -8,6 +8,7 @@ import { Services } from 'components-react/service-provider'; import { Button } from 'antd'; import { EMenuItemKey } from 'services/side-nav'; import { $t } from 'services/i18n'; +import styles from './PlatformAppStore.m.less'; export default function PlatformAppStore(p: { params: { appId?: string; type?: string } }) { const { @@ -22,7 +23,7 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s ); const [platformAppsUrl, setPlatformAppsUrl] = useState(''); const [currentUrl, setCurrentUrl] = useState(''); - const browser = useRef(null); + useEffect(() => { async function getPlatformAppsUrl() { const url = await UserService.views.appStoreUrl(p.params); @@ -84,11 +85,8 @@ export default function PlatformAppStore(p: { params: { appId?: string; type?: s return ( <> {currentUrl.includes('installed-apps') && highlighterInstalled && ( -

+
{$t('Other installed apps:')}
-
-
+
+

AI Highlighter

by Streamlabs

diff --git a/app/components-react/shared/BrowserView.tsx b/app/components-react/shared/BrowserView.tsx index fe01a1e9f362..69a7029faf88 100644 --- a/app/components-react/shared/BrowserView.tsx +++ b/app/components-react/shared/BrowserView.tsx @@ -184,10 +184,9 @@ export default function BrowserView(p: BrowserViewProps) { function getCurrentUrl() { if (!browserView.current) { - console.log('BrowserView: browserView.current is null'); return ''; } - return browserView.current!.webContents.getURL(); + return browserView.current.webContents.getURL(); } return ( diff --git a/app/components-react/windows/settings/InstalledApps.m.less b/app/components-react/windows/settings/InstalledApps.m.less index 6d1d4a28e5f3..09034504ae1d 100644 --- a/app/components-react/windows/settings/InstalledApps.m.less +++ b/app/components-react/windows/settings/InstalledApps.m.less @@ -9,3 +9,12 @@ margin-right: 0; } } + +.ai-highlighter-thumbnail { + width: 50px; + height: 32px; + display: grid; + place-content: center; + background: linear-gradient(60deg, #2de8b0 25.56%, #cbe953 60.27%, #ffab48 79.52%, #ff5151 96.69%), + #fff; +} diff --git a/app/components-react/windows/settings/InstalledApps.tsx b/app/components-react/windows/settings/InstalledApps.tsx index 630cbaa85d05..a5afbf4a586f 100644 --- a/app/components-react/windows/settings/InstalledApps.tsx +++ b/app/components-react/windows/settings/InstalledApps.tsx @@ -93,16 +93,7 @@ export default function InstalledApps() { {highlighterVersion !== '' && ( -
+
Date: Wed, 15 Jan 2025 09:38:23 +0100 Subject: [PATCH 14/15] only to kick of tests again --- app/components-react/windows/settings/InstalledApps.m.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components-react/windows/settings/InstalledApps.m.less b/app/components-react/windows/settings/InstalledApps.m.less index 09034504ae1d..8053be279a43 100644 --- a/app/components-react/windows/settings/InstalledApps.m.less +++ b/app/components-react/windows/settings/InstalledApps.m.less @@ -13,8 +13,8 @@ .ai-highlighter-thumbnail { width: 50px; height: 32px; - display: grid; place-content: center; + display: grid; background: linear-gradient(60deg, #2de8b0 25.56%, #cbe953 60.27%, #ffab48 79.52%, #ff5151 96.69%), #fff; } From a99622c36c211d51abb1e22bdda19f5bc9b3d29b Mon Sep 17 00:00:00 2001 From: marvinoffers Date: Wed, 15 Jan 2025 09:47:22 +0100 Subject: [PATCH 15/15] remove helperbutton --- app/components-react/shared/BrowserView.tsx | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/components-react/shared/BrowserView.tsx b/app/components-react/shared/BrowserView.tsx index 69a7029faf88..476869bf439b 100644 --- a/app/components-react/shared/BrowserView.tsx +++ b/app/components-react/shared/BrowserView.tsx @@ -189,17 +189,5 @@ export default function BrowserView(p: BrowserViewProps) { return browserView.current.webContents.getURL(); } - return ( - <> - -
- - ); + return
; }