Skip to content

create-streamer page #789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
4 changes: 2 additions & 2 deletions apps/app/app/PromptPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const allTrendingPrompts = [
},
];

export default function PromptPanel() {
export default function PromptPanel({ isStreamerVersion = false }) {
const { isMobile } = useMobileStore();
const { currentStream } = useMultiplayerStreamStore();
const [promptValue, setPromptValue] = useState("");
Expand Down Expand Up @@ -220,7 +220,7 @@ export default function PromptPanel() {
</div>

{/* Trending prompts section - moved outside main panel */}
{!isMobile && (
{!isMobile && !isStreamerVersion && (
<div className="w-full mt-4 mb-3">
<div
className="w-full bg-white rounded-lg p-3"
Expand Down
78 changes: 78 additions & 0 deletions apps/app/app/create-streamer/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Daydream from "@/components/daydream";
import track from "@/lib/track";
import {
identifyTikTokInAppBrowser,
identifyInstagramInAppBrowser,
} from "@/lib/userAgentIdentify";
import { headers } from "next/headers";
import { cache } from "react";
import { getSharedParams } from "../api/streams/share-params";
import BrowserFallback from "../create/BrowserFallback";

const getCachedSharedParams = cache(async (shareParamsId: string) => {
const { data: sharedParams } = await getSharedParams(shareParamsId);
return sharedParams;
});

export const generateMetadata = async ({
searchParams,
}: {
searchParams: { shared: string };
}) => {
const { shared } = searchParams;
const metaData = {
title: "Daydream",
description: "Transform your video in real-time with AI",
};

if (!shared) {
return metaData;
}

// Update the description with the shared prompt
const sharedParams = await getCachedSharedParams(shared);
const sharedPrompt =
sharedParams?.params?.prompt?.["5"]?.inputs?.text || metaData.description;
metaData.description = sharedPrompt;
return { ...metaData, openGraph: metaData };
};

export default async function StreamerPage({
searchParams,
}: {
searchParams: {
shared: string;
privy_oauth_code: string;
inputPrompt: string;
sourceClipId: string;
};
}) {
const requestHeaders = headers();
const userAgent = requestHeaders.get("user-agent")?.toLowerCase();

const { shared, privy_oauth_code, inputPrompt } = searchParams;
const isGuestAccess = !!inputPrompt; // If there's an inputPrompt, the user is coming from "Try this prompt"

if (userAgent) {
if (identifyTikTokInAppBrowser(userAgent)) {
track("tiktok_browser_fallback_shown");
return <BrowserFallback platform="tiktok" />;
}

if (identifyInstagramInAppBrowser(userAgent)) {
track("instagram_browser_fallback_shown");
return <BrowserFallback platform="instagram" />;
}
}

return (
<Daydream
hasSharedPrompt={!!shared || !!inputPrompt}
isOAuthSuccessRedirect={
privy_oauth_code?.length > 0 && privy_oauth_code !== "error"
}
allowGuestAccess={isGuestAccess}
isStreamerVersion={true}
/>
);
}
46 changes: 42 additions & 4 deletions apps/app/app/hooks/usePromptQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ function cleanupCacheEntry(streamId: string) {
if (!entry) return;

if (entry.subscribers.size === 0) {
// Close WebSocket to free resources
if (entry.ws) {
entry.ws.close();
entry.ws = null;
Expand All @@ -166,7 +167,8 @@ function cleanupCacheEntry(streamId: string) {
clearTimeout(entry.reconnectTimer);
entry.reconnectTimer = null;
}
promptQueueCache.delete(streamId);
// Don't delete the cache entry - preserve prompt data for when overlay reopens
// promptQueueCache.delete(streamId);
}
}

Expand All @@ -179,6 +181,26 @@ export function usePromptQueue(streamId: string | undefined) {
forceUpdate({});
}, []);

// Function to fetch initial data immediately
const fetchInitialData = useCallback(async (streamId: string) => {
try {
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080";
const response = await fetch(
`${apiUrl}/streams/${streamId}/prompts/state`,
);

if (response.ok) {
const data = await response.json();
const entry = getCacheEntry(streamId);
entry.currentPrompt = data.currentPrompt;
entry.recentPrompts = data.recentPrompts || [];
notifySubscribers(streamId);
}
} catch (error) {
console.error("Failed to fetch initial prompt data:", error);
}
}, []);

useEffect(() => {
// Clean up previous subscription if streamKey changed
if (currentStreamIdRef.current && currentStreamIdRef.current !== streamId) {
Expand All @@ -198,6 +220,11 @@ export function usePromptQueue(streamId: string | undefined) {
const entry = getCacheEntry(streamId);
entry.subscribers.add(rerender);

// If we don't have recent prompts data, fetch it immediately
if (entry.recentPrompts.length === 0) {
fetchInitialData(streamId);
}

// Connect WebSocket if not already connected
if (!entry.ws && entry.subscribers.size === 1) {
connectWebSocket(streamId);
Expand All @@ -212,7 +239,7 @@ export function usePromptQueue(streamId: string | undefined) {
}
}
};
}, [streamId, rerender]);
}, [streamId, rerender, fetchInitialData]);

const entry = streamId ? promptQueueCache.get(streamId) : null;
const currentPrompt = entry?.currentPrompt || null;
Expand All @@ -238,6 +265,17 @@ export function usePromptQueue(streamId: string | undefined) {
if (!text.trim() || !streamId || isSubmitting)
return { success: false, promptId: null };

// Auto-append quality parameter if not present
let processedText = text.trim();
if (!processedText.toLowerCase().includes("--quality")) {
processedText += " --quality 3";
}

// Auto-append creativity parameter if not present
if (!processedText.toLowerCase().includes("--creativity")) {
processedText += " --creativity 0.6";
}

const entry = getCacheEntry(streamId);
entry.isSubmitting = true;
notifySubscribers(streamId);
Expand All @@ -251,7 +289,7 @@ export function usePromptQueue(streamId: string | undefined) {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: text.trim(),
text: processedText,
}),
},
);
Expand All @@ -269,7 +307,7 @@ export function usePromptQueue(streamId: string | undefined) {

track("daydream_landing_page_prompt_submitted", {
is_authenticated: authenticated,
prompt: text,
prompt: processedText, // Track the processed text with quality parameter
nsfw: result?.wasCensored || false,
stream_id: streamId,
});
Expand Down
3 changes: 3 additions & 0 deletions apps/app/components/daydream/MainExperience/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { useCapacityCheck } from "@/hooks/useCapacityCheck";
interface MainExperienceProps {
isGuestMode?: boolean;
defaultPrompt?: string | null;
isStreamerVersion?: boolean;
}

export default function MainExperience({
isGuestMode = false,
defaultPrompt = null,
isStreamerVersion = false,
}: MainExperienceProps) {
const { currentStep, cameraPermission, setCameraPermission } = useOnboard();
const { user } = usePrivy();
Expand Down Expand Up @@ -67,6 +69,7 @@ export default function MainExperience({
<Dreamshaper
isGuestMode={isGuestMode}
defaultPrompt={defaultPrompt}
isStreamerVersion={isStreamerVersion}
/>
<ClientSideTracker eventName="home_page_view" />
</div>
Expand Down
16 changes: 14 additions & 2 deletions apps/app/components/daydream/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ interface DaydreamProps {
hasSharedPrompt: boolean;
isOAuthSuccessRedirect: boolean;
allowGuestAccess?: boolean;
isStreamerVersion?: boolean;
}

export default function Daydream({
hasSharedPrompt,
isOAuthSuccessRedirect,
allowGuestAccess = false,
isStreamerVersion = false,
}: DaydreamProps) {
const { user, ready, authenticated } = usePrivy();
const { isGuestUser, setIsGuestUser, setLastPrompt } = useGuestUserStore();
Expand Down Expand Up @@ -118,6 +120,7 @@ export default function Daydream({
<DaydreamRenderer
isGuestMode={true}
defaultPrompt={defaultPrompt || undefined}
isStreamerVersion={isStreamerVersion}
/>
</OnboardProvider>
);
Expand All @@ -137,17 +140,22 @@ export default function Daydream({
// If the user is logged in, show the onboarding screen and main experience
return (
<OnboardProvider hasSharedPrompt={hasSharedPrompt || !!inputPrompt}>
<DaydreamRenderer isGuestMode={false} />
<DaydreamRenderer
isGuestMode={false}
isStreamerVersion={isStreamerVersion}
/>
</OnboardProvider>
);
}

function DaydreamRenderer({
isGuestMode = false,
defaultPrompt,
isStreamerVersion = false,
}: {
isGuestMode?: boolean;
defaultPrompt?: string | null;
isStreamerVersion?: boolean;
}) {
const {
isInitializing,
Expand Down Expand Up @@ -327,7 +335,11 @@ function DaydreamRenderer({
content = <WelcomeScreen />;
} else {
content = (
<MainExperience isGuestMode={isGuestMode} defaultPrompt={defaultPrompt} />
<MainExperience
isGuestMode={isGuestMode}
defaultPrompt={defaultPrompt}
isStreamerVersion={isStreamerVersion}
/>
);
}

Expand Down
Loading
Loading