diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 04a96e5467..84eef9d6b2 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -2564,11 +2564,26 @@
},
"supportVideos": {
"supportVideos": "Support Videos",
+ "supportPlaylists": "Video Playlists",
"gettingStarted": "Getting Started",
"controlCanvas": "Control Canvas",
"watch": "Watch",
+ "watchPlaylist": "Watch Playlist",
+ "videoCount": "{{count}} videos",
+ "videoCount_one": "{{count}} video",
+ "videoCount_other": "{{count}} videos",
"studioSessionsDesc1": "Check out the for Invoke deep dives.",
"studioSessionsDesc2": "Join our to participate in the live sessions and ask questions. Sessions are uploaded to the playlist the following week.",
+ "playlists": {
+ "gettingStarted": {
+ "title": "Getting Started with Invoke",
+ "description": "Complete video series covering everything you need to know to get started with Invoke, from creating your first image to advanced techniques."
+ },
+ "studioSessions": {
+ "title": "Studio Sessions",
+ "description": "Deep dive sessions exploring advanced Invoke features, creative workflows, and community discussions."
+ }
+ },
"videos": {
"creatingYourFirstImage": {
"title": "Creating Your First Image",
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
index f124462e8b..c2443dde6b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
@@ -55,9 +55,7 @@ export const ModelInstallQueue = memo(() => {
- {data?.map((model) => (
-
- ))}
+ {data?.map((model) => )}
diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCard.tsx b/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCard.tsx
new file mode 100644
index 0000000000..4c361fb3bd
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCard.tsx
@@ -0,0 +1,33 @@
+import { ExternalLink, Flex, Spacer, Text } from '@invoke-ai/ui-library';
+import { useAppDispatch } from 'app/store/storeHooks';
+import type { PlaylistData } from 'features/system/components/VideosModal/data';
+import { videoModalLinkClicked } from 'features/system/store/actions';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const PlaylistCard = memo(({ playlist }: { playlist: PlaylistData }) => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const { tKey, link, videoCount } = playlist;
+ const handleLinkClick = useCallback(() => {
+ dispatch(videoModalLinkClicked(t(`supportVideos.playlists.${tKey}.title`)));
+ }, [dispatch, t, tKey]);
+
+ return (
+
+
+
+ {t(`supportVideos.playlists.${tKey}.title`)}
+
+
+ {t('supportVideos.videoCount', { count: videoCount })}
+
+
+
+ {t(`supportVideos.playlists.${tKey}.description`)}
+
+
+ );
+});
+
+PlaylistCard.displayName = 'PlaylistCard';
diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCardList.tsx b/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCardList.tsx
new file mode 100644
index 0000000000..d29f4bb206
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/VideosModal/PlaylistCardList.tsx
@@ -0,0 +1,20 @@
+import { Divider } from '@invoke-ai/ui-library';
+import { StickyScrollable } from 'features/system/components/StickyScrollable';
+import type { PlaylistData } from 'features/system/components/VideosModal/data';
+import { PlaylistCard } from 'features/system/components/VideosModal/PlaylistCard';
+import { Fragment, memo } from 'react';
+
+export const PlaylistCardList = memo(({ category, playlists }: { category: string; playlists: PlaylistData[] }) => {
+ return (
+
+ {playlists.map((playlist, i) => (
+
+
+ {i < playlists.length - 1 && }
+
+ ))}
+
+ );
+});
+
+PlaylistCardList.displayName = 'PlaylistCardList';
diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx b/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx
index 4140b05c03..188a2814a3 100644
--- a/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx
+++ b/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx
@@ -1,6 +1,6 @@
import { Divider } from '@invoke-ai/ui-library';
import { StickyScrollable } from 'features/system/components/StickyScrollable';
-import { gettingStartedVideos, type VideoData } from 'features/system/components/VideosModal/data';
+import type { VideoData } from 'features/system/components/VideosModal/data';
import { VideoCard } from 'features/system/components/VideosModal/VideoCard';
import { Fragment, memo } from 'react';
@@ -10,7 +10,7 @@ export const VideoCardList = memo(({ category, videos }: { category: string; vid
{videos.map((video, i) => (
- {i < gettingStartedVideos.length - 1 && }
+ {i < videos.length - 1 && }
))}
diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx b/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx
index 05419be5f1..8ba63e2a33 100644
--- a/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx
+++ b/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx
@@ -15,9 +15,10 @@ import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableCon
import { buildUseDisclosure } from 'common/hooks/useBoolean';
import {
controlCanvasVideos,
- gettingStartedVideos,
studioSessionsPlaylistLink,
+ supportPlaylists,
} from 'features/system/components/VideosModal/data';
+import { PlaylistCardList } from 'features/system/components/VideosModal/PlaylistCardList';
import { VideoCardList } from 'features/system/components/VideosModal/VideoCardList';
import { videoModalLinkClicked } from 'features/system/store/actions';
import { discordLink } from 'features/system/store/constants';
@@ -86,7 +87,7 @@ export const VideosModal = memo(() => {
-
+
diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts b/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts
index 0b194ee5a6..3e7864779e 100644
--- a/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts
+++ b/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts
@@ -1,10 +1,30 @@
/**
- * To add a support video, you'll need to add the video to the list below.
+ * To add a support video playlist, you'll need to add the playlist to the list below.
*
* The `tKey` is a sub-key in the translation file `invokeai/frontend/web/public/locales/en.json`.
- * Add the title and description under `supportVideos.videos`, following the existing format.
+ * Add the title and description under `supportVideos.playlists`, following the existing format.
*/
+export type PlaylistData = {
+ tKey: string;
+ link: string;
+ videoCount: number;
+};
+
+export const supportPlaylists: PlaylistData[] = [
+ {
+ tKey: 'gettingStarted',
+ link: 'https://www.youtube.com/watch?v=jVi2XgSGrfY&list=PLvWK1Kc8iXGrQy8r9TYg6QdUuJ5MMx-ZO&pp=gAQB0gcJCV8EOCosWNin',
+ videoCount: 6,
+ },
+ {
+ tKey: 'studioSessions',
+ link: 'https://www.youtube.com/watch?v=91ZgeeqL7Bo&list=PLvWK1Kc8iXGq_8tWZqnwDVaf9uhlDC09U&pp=gAQB',
+ videoCount: 10,
+ },
+];
+
+// Legacy video data - keeping for backward compatibility if needed
export type VideoData = {
tKey: string;
link: string;