diff --git a/backend/director/constants.py b/backend/director/constants.py index a398d134..ce32ad14 100644 --- a/backend/director/constants.py +++ b/backend/director/constants.py @@ -31,3 +31,41 @@ class EnvPrefix(str, Enum): GOOGLEAI_ = "GOOGLEAI_" DOWNLOADS_PATH="director/downloads" + +CHAT_NAMING_SYSTEM_PROMPT = """ +You are an assistant that generates short, descriptive titles for chat conversations. +The title should summarize the main intent or topic of the user's first message. + +Guidelines: + +**Output you give should strictly just be the title without any markdown content in plain text** + +Keep the title under 6 words. + +Use concise, professional phrasing. + +Don't include punctuation unless necessary. + +Capitalize like a headline (e.g., “Check SQL Query Logic”). + +Avoid emojis or filler words. + +Example input and outputs: + +“Can you give me download links for these videos?” → Generate Video Download Links + +“Summarize the lecture video for me” → Lecture Video Summary + +“Add a watermark to my demo” → Add Watermark to Video + +“Trim the video from 2:10 to 3:45” → Trim Video Segment + +“Combine these three clips into one” → Merge Video Clips + +“Extract subtitles from this recording” → Extract Video Subtitles + +“Translate this video into Spanish” → Translate Video to Spanish + +“Generate a short trailer from the full video” → Create Video Trailer + +""" \ No newline at end of file diff --git a/backend/director/core/session.py b/backend/director/core/session.py index cbcdaf24..6405caa6 100644 --- a/backend/director/core/session.py +++ b/backend/director/core/session.py @@ -220,28 +220,31 @@ class OutputMessage(BaseMessage): status: MsgStatus = MsgStatus.progress def update_status(self, status: MsgStatus): - """Update the status of the message and publish the message to the socket. for loading state.""" + """Update the status and broadcast to session room.""" self.status = status self._publish() def push_update(self): - """Publish the message to the socket.""" + """Push real-time update to session room.""" try: self._publish() except Exception as e: - print(f"Error in emitting message: {str(e)}") + print(f"Error in emitting update to session {self.session_id}: {str(e)}") def publish(self): - """Store the message in the database. for conversation history and publish the message to the socket.""" + """Store in database and broadcast final result to session room.""" self._publish() def _publish(self): try: - emit("chat", self.model_dump(), namespace="/chat") + emit("chat", self.model_dump(), + room=self.session_id, + namespace="/chat") + print(f"Emitted message to session room: {self.session_id}") except Exception as e: - print(f"Error in emitting message: {str(e)}") - self.db.add_or_update_msg_to_conv(**self.model_dump()) + print(f"Error emitting to session {self.session_id}: {str(e)}") + self.db.add_or_update_msg_to_conv(**self.model_dump()) def format_user_message(message: dict) -> dict: message_content = message.get("content") @@ -393,6 +396,10 @@ def delete(self): """Delete the session from the database.""" return self.db.delete_session(self.session_id) + def update(self, **kwargs) -> bool: + """Update the session in the database.""" + return self.db.update_session(self.session_id, **kwargs) + def emit_event(self, event: BaseEvent, namespace="/chat"): """Emits a structured WebSocket event to notify all clients about updates.""" diff --git a/backend/director/db/base.py b/backend/director/db/base.py index b166bab5..5501bcf8 100644 --- a/backend/director/db/base.py +++ b/backend/director/db/base.py @@ -43,6 +43,28 @@ def add_or_update_context_msg( """Update context messages for a session.""" pass + @abstractmethod + def update_session(self, session_id: str, **kwargs) -> bool: + """Update a session in the database.""" + pass + + @abstractmethod + def delete_session(self, session_id: str) -> tuple[bool, list]: + """Delete a session from the database. + :return: (success, failed_components) + """ + pass + + @abstractmethod + def make_session_public(self, session_id: str, is_public: bool) -> bool: + """Make a session public or private.""" + pass + + @abstractmethod + def get_public_session(self, session_id: str) -> dict: + """Get a public session by session_id.""" + pass + @abstractmethod def health_check(self) -> bool: """Check if the database is healthy.""" diff --git a/backend/director/db/postgres/db.py b/backend/director/db/postgres/db.py index 4a2d5b8e..b8a2043b 100644 --- a/backend/director/db/postgres/db.py +++ b/backend/director/db/postgres/db.py @@ -31,12 +31,13 @@ def __init__(self): port=os.getenv("POSTGRES_PORT", "5432"), ) self.cursor = self.conn.cursor(cursor_factory=RealDictCursor) - + initialize_postgres() def create_session( self, session_id: str, video_id: str, collection_id: str, + name: str = None, created_at: int = None, updated_at: int = None, metadata: dict = {}, @@ -47,14 +48,15 @@ def create_session( self.cursor.execute( """ - INSERT INTO sessions (session_id, video_id, collection_id, created_at, updated_at, metadata) - VALUES (%s, %s, %s, %s, %s, %s) + INSERT INTO sessions (session_id, video_id, collection_id, name, created_at, updated_at, metadata) + VALUES (%s, %s, %s, %s, %s, %s, %s) ON CONFLICT (session_id) DO NOTHING """, ( session_id, video_id, collection_id, + name, created_at, updated_at, json.dumps(metadata), @@ -195,7 +197,7 @@ def delete_context(self, session_id: str) -> bool: self.conn.commit() return self.cursor.rowcount > 0 - def delete_session(self, session_id: str) -> bool: + def delete_session(self, session_id: str) -> tuple[bool, list]: failed_components = [] if not self.delete_conversation(session_id): failed_components.append("conversation") @@ -210,6 +212,89 @@ def delete_session(self, session_id: str) -> bool: success = len(failed_components) < 3 return success, failed_components + def update_session(self, session_id: str, **kwargs) -> bool: + """Update a session in the database.""" + try: + if not kwargs: + return False + + allowed_fields = {"name", "video_id", "collection_id", "metadata"} + update_fields = [] + update_values = [] + + for key, value in kwargs.items(): + if key not in allowed_fields: + continue + if key == "metadata" and not isinstance(value, str): + value = json.dumps(value) + update_fields.append(f"{key} = %s") + update_values.append(value) + + if not update_fields: + return False + + update_fields.append("updated_at = %s") + update_values.append(int(time.time())) + + update_values.extend([session_id]) + + query = f""" + UPDATE sessions + SET {', '.join(update_fields)} + WHERE session_id = %s + """ + + self.cursor.execute(query, update_values) + self.conn.commit() + return self.cursor.rowcount > 0 + + except Exception: + logger.exception(f"Error updating session {session_id}") + return False + + def make_session_public(self, session_id: str, is_public: bool) -> bool: + """Make a session public or private.""" + try: + query = """ + UPDATE sessions + SET is_public = %s, updated_at = %s + WHERE session_id = %s + """ + current_time = int(time.time()) + self.cursor.execute(query, (is_public, current_time, session_id)) + self.conn.commit() + return self.cursor.rowcount > 0 + except Exception as e: + logger.exception(f"Error making session public/private: {e}") + return False + + def get_public_session(self, session_id: str) -> dict: + """Get a public session by session_id.""" + try: + query = """ + SELECT session_id, video_id, collection_id, name, created_at, updated_at, metadata, is_public + FROM sessions + WHERE session_id = %s AND is_public = TRUE + """ + self.cursor.execute(query, (session_id,)) + row = self.cursor.fetchone() + if row: + session = { + "session_id": row["session_id"], + "video_id": row["video_id"], + "collection_id": row["collection_id"], + "name": row["name"], + "created_at": row["created_at"], + "updated_at": row["updated_at"], + "metadata": row["metadata"] if row["metadata"] else {}, + "is_public": row["is_public"] + } + return session + return {} + except Exception as e: + logger.exception(f"Error getting public session: {e}") + return {} + def health_check(self) -> bool: try: query = """ diff --git a/backend/director/db/postgres/initialize.py b/backend/director/db/postgres/initialize.py index 18da07cc..6bce50a7 100644 --- a/backend/director/db/postgres/initialize.py +++ b/backend/director/db/postgres/initialize.py @@ -11,6 +11,8 @@ session_id TEXT PRIMARY KEY, video_id TEXT, collection_id TEXT, + name TEXT, + is_public BOOLEAN DEFAULT FALSE, created_at BIGINT, updated_at BIGINT, metadata JSONB @@ -68,6 +70,8 @@ def initialize_postgres(): cursor.execute(CREATE_SESSIONS_TABLE) cursor.execute(CREATE_CONVERSATIONS_TABLE) cursor.execute(CREATE_CONTEXT_MESSAGES_TABLE) + cursor.execute("ALTER TABLE sessions ADD COLUMN IF NOT EXISTS name TEXT") + cursor.execute("ALTER TABLE sessions ADD COLUMN IF NOT EXISTS is_public BOOLEAN DEFAULT FALSE") conn.commit() logger.info("PostgreSQL tables created successfully") except Exception as e: diff --git a/backend/director/db/sqlite/db.py b/backend/director/db/sqlite/db.py index f47781a9..835e8fd5 100644 --- a/backend/director/db/sqlite/db.py +++ b/backend/director/db/sqlite/db.py @@ -23,6 +23,7 @@ def __init__(self, db_path: str = None): self.db_path = os.getenv("SQLITE_DB_PATH", "director.db") else: self.db_path = db_path + initialize_sqlite(self.db_path) self.conn = sqlite3.connect(self.db_path, check_same_thread=True) self.conn.row_factory = sqlite3.Row self.cursor = self.conn.cursor() @@ -33,6 +34,7 @@ def create_session( session_id: str, video_id: str, collection_id: str, + name: str = None, created_at: int = None, updated_at: int = None, metadata: dict = {}, @@ -52,13 +54,14 @@ def create_session( self.cursor.execute( """ - INSERT OR IGNORE INTO sessions (session_id, video_id, collection_id, created_at, updated_at, metadata) - VALUES (?, ?, ?, ?, ?, ?) + INSERT OR IGNORE INTO sessions (session_id, video_id, collection_id, name, created_at, updated_at, metadata) + VALUES (?, ?, ?, ?, ?, ?, ?) """, ( session_id, video_id, collection_id, + name, created_at, updated_at, json.dumps(metadata), @@ -153,8 +156,7 @@ def add_or_update_msg_to_conv( def get_conversations(self, session_id: str) -> list: self.cursor.execute( - "SELECT * FROM conversations WHERE session_id = ? ORDER BY created_at ASC", - (session_id,), + "SELECT * FROM conversations WHERE session_id = ?", (session_id,) ) rows = self.cursor.fetchall() conversations = [] @@ -241,7 +243,7 @@ def delete_context(self, session_id: str) -> bool: self.conn.commit() return self.cursor.rowcount > 0 - def delete_session(self, session_id: str) -> bool: + def delete_session(self, session_id: str) -> tuple[bool, list]: """Delete a session and all its associated data. :param str session_id: Unique session ID. @@ -259,6 +261,94 @@ def delete_session(self, session_id: str) -> bool: success = len(failed_components) < 3 return success, failed_components + def update_session(self, session_id: str, **kwargs) -> bool: + """Update a session in the database. + + :param session_id: Unique session ID. + :param kwargs: Fields to update. + :return: True if update was successful, False otherwise. + """ + try: + if not kwargs: + return False + + allowed_fields = {"name", "video_id", "collection_id", "metadata"} + update_fields = [] + update_values = [] + + for key, value in kwargs.items(): + if key not in allowed_fields: + continue + if key == "metadata" and not isinstance(value, str): + value = json.dumps(value) + update_fields.append(f"{key} = ?") + update_values.append(value) + + if not update_fields: + return False + + update_fields.append("updated_at = ?") + update_values.append(int(time.time())) + + update_values.extend([session_id]) + + query = f""" + UPDATE sessions + SET {', '.join(update_fields)} + WHERE session_id = ? + """ + + self.cursor.execute(query, update_values) + self.conn.commit() + return self.cursor.rowcount > 0 + + except Exception: + logger.exception(f"Error updating session {session_id}") + return False + + def make_session_public(self, session_id: str, is_public: bool) -> bool: + """Make a session public or private.""" + try: + query = """ + UPDATE sessions + SET is_public = ?, updated_at = ? + WHERE session_id = ? + """ + current_time = int(time.time()) + self.cursor.execute(query, (is_public, current_time, session_id)) + self.conn.commit() + return self.cursor.rowcount > 0 + except Exception: + logger.exception("Error making session public/private") + return False + + def get_public_session(self, session_id: str) -> dict: + """Get a public session by session_id.""" + try: + query = """ + SELECT session_id, video_id, collection_id, name, created_at, updated_at, metadata, is_public + FROM sessions + WHERE session_id = ? AND is_public = TRUE + """ + self.cursor.execute(query, (session_id,)) + row = self.cursor.fetchone() + if row: + session = { + "session_id": row[0], + "video_id": row[1], + "collection_id": row[2], + "name": row[3], + "created_at": row[4], + "updated_at": row[5], + "metadata": json.loads(row[6]) if row[6] else {}, + "is_public": row[7] + } + return session + return {} + except Exception as e: + logger.exception(f"Error getting public session: {e}") + return {} + def health_check(self) -> bool: """Check if the SQLite database is healthy and the necessary tables exist. If not, create them.""" try: diff --git a/backend/director/db/sqlite/initialize.py b/backend/director/db/sqlite/initialize.py index 76a06e16..79f3aaa4 100644 --- a/backend/director/db/sqlite/initialize.py +++ b/backend/director/db/sqlite/initialize.py @@ -7,6 +7,8 @@ session_id TEXT PRIMARY KEY, video_id TEXT, collection_id TEXT, + name TEXT, + is_public BOOLEAN DEFAULT FALSE, created_at INTEGER, updated_at INTEGER, metadata JSON @@ -49,10 +51,20 @@ def initialize_sqlite(db_name="director.db"): conn = sqlite3.connect(db_name) cursor = conn.cursor() + # Create base tables cursor.execute(CREATE_SESSIONS_TABLE) cursor.execute(CREATE_CONVERSATIONS_TABLE) cursor.execute(CREATE_CONTEXT_MESSAGES_TABLE) + cursor.execute("PRAGMA table_info(sessions)") + columns = [col[1] for col in cursor.fetchall()] + + if "name" not in columns: + cursor.execute("ALTER TABLE sessions ADD COLUMN name TEXT") + + if "is_public" not in columns: + cursor.execute("ALTER TABLE sessions ADD COLUMN is_public BOOLEAN DEFAULT FALSE") + conn.commit() conn.close() diff --git a/backend/director/entrypoint/api/routes.py b/backend/director/entrypoint/api/routes.py index f7c2ae48..ab38badc 100644 --- a/backend/director/entrypoint/api/routes.py +++ b/backend/director/entrypoint/api/routes.py @@ -35,7 +35,7 @@ def get_sessions(): return session_handler.get_sessions() -@session_bp.route("/", methods=["GET", "DELETE"]) +@session_bp.route("/", methods=["GET", "DELETE", "POST"]) def get_session(session_id): """ Get or delete the session details @@ -60,6 +60,97 @@ def get_session(session_id): return { "message": f"Failed to delete the entry for following components: {', '.join(failed_components)}" }, 500 + elif request.method == "POST": + data = request.get_json() + + session = session_handler.create_session( + data.get("message") + ) + + return session, 200 + + +@session_bp.route("//rename", methods=["PUT"]) +def rename_session(session_id): + """ + Rename a session by updating its metadata + """ + if not session_id: + return {"message": "Please provide session_id."}, 400 + + data = request.get_json() + if not data or not data.get("name"): + return {"message": "Session name is required."}, 400 + + new_name = data["name"] + if not new_name.strip(): + return {"message": "Session name cannot be empty."}, 400 + + session_handler = SessionHandler( + db=load_db(os.getenv("SERVER_DB_TYPE", app.config["DB_TYPE"])) + ) + + session = session_handler.get_session(session_id) + if not session: + return {"message": "Session not found."}, 404 + + result = session_handler.rename_session(session_id, new_name) + return result + +@session_bp.route("//public", methods=["PUT"]) +def make_session_public(session_id): + """ + Make a session public or private + """ + if not session_id: + return {"message": "Please provide session_id."}, 400 + + data = request.get_json() + if not data or "is_public" not in data: + return {"message": "is_public field is required."}, 400 + + is_public = data["is_public"] + if not isinstance(is_public, bool): + return {"message": "is_public must be a boolean value."}, 400 + + db = load_db(os.getenv("SERVER_DB_TYPE", app.config["DB_TYPE"])) + + # Check if session exists and belongs to user + session_handler = SessionHandler(db=db) + session = session_handler.get_session(session_id) + if not session: + return {"message": "Session not found."}, 404 + + # Update the session's public status + success = db.make_session_public(session_id, is_public) + + if success: + status = "public" if is_public else "private" + return {"message": f"Session successfully made {status}."}, 200 + else: + return {"message": "Failed to update session visibility."}, 500 + + +@session_bp.route("/public/", methods=["GET"]) +def get_public_session(session_id): + """ + Get a public session by session_id (no authentication required) + """ + if not session_id: + return {"message": "Please provide session_id."}, 400 + + db = load_db(os.getenv("SERVER_DB_TYPE", app.config["DB_TYPE"])) + + # Get the public session + session = db.get_public_session(session_id) + if not session: + return {"message": "Public session not found."}, 404 + + # Get the conversation for this session + conversation = db.get_conversations(session_id) + session["conversation"] = conversation + + return session, 200 @videodb_bp.route("/collection", defaults={"collection_id": None}, methods=["GET"]) diff --git a/backend/director/entrypoint/api/socket_io.py b/backend/director/entrypoint/api/socket_io.py index 857e3392..a60ca4aa 100644 --- a/backend/director/entrypoint/api/socket_io.py +++ b/backend/director/entrypoint/api/socket_io.py @@ -1,18 +1,40 @@ import os from flask import current_app as app -from flask_socketio import Namespace - +from flask_socketio import Namespace, join_room, emit from director.db import load_db from director.handler import ChatHandler - class ChatNamespace(Namespace): - """Chat namespace for socket.io""" - + """Chat namespace for socket.io with session-based rooms""" + + def on_connect(self): + """Handle client connection""" + print(f"Client connected to chat namespace") + def on_chat(self, message): - """Handle chat messages""" + """Handle chat messages and auto-join session room""" + session_id = message.get('session_id') + if not session_id: + emit('error', {'message': 'session_id is required'}) + return + + join_room(session_id) + print(f"Client joined session room {session_id}") + chat_handler = ChatHandler( db=load_db(os.getenv("SERVER_DB_TYPE", app.config["DB_TYPE"])) ) chat_handler.chat(message) + + def on_join_session(self, data): + """Explicitly join a session room (for reconnection)""" + session_id = data.get('session_id') + if session_id: + join_room(session_id) + emit('session_joined', {'session_id': session_id}) + print(f"Client explicitly joined session room {session_id}") + + def on_disconnect(self): + """Handle client disconnection""" + print(f"Client disconnected from chat namespace") diff --git a/backend/director/handler.py b/backend/director/handler.py index e42e20da..028d5123 100644 --- a/backend/director/handler.py +++ b/backend/director/handler.py @@ -1,5 +1,11 @@ +import json import os import logging +from typing import Optional + +from director.constants import CHAT_NAMING_SYSTEM_PROMPT +from director.llm import get_default_llm +from director.llm.base import LLMResponseStatus from director.agents.frame import FrameAgent from director.agents.summarize_video import SummarizeVideoAgent @@ -28,7 +34,7 @@ from director.agents.voice_replacement import VoiceReplacementAgent -from director.core.session import Session, InputMessage, MsgStatus +from director.core.session import ContextMessage, RoleTypes, Session, InputMessage, MsgStatus from director.core.reasoning import ReasoningEngine from director.db.base import BaseDB from director.db import load_db @@ -140,6 +146,46 @@ def delete_session(self, session_id): session = Session(db=self.db, session_id=session_id) return session.delete() + def rename_session(self, session_id, new_name): + """Rename a session by updating its name field.""" + session = Session(db=self.db, session_id=session_id) + success = session.update(name=new_name) + if success: + return {"message": "Session renamed successfully", "session_id": session_id, "name": new_name} + else: + return {"message": "Failed to rename session"}, 500 + + def create_session(self, message): + session = Session(db=self.db, **message) + session.create() + + session_dict = session.get() + if not session_dict["name"]: + llm = get_default_llm() + context_messages = [ + ContextMessage( + role=RoleTypes.system, + content=CHAT_NAMING_SYSTEM_PROMPT + ), + ContextMessage( + role=RoleTypes.user, + content=json.dumps(session_dict["conversation"]) if session_dict["conversation"] is not None else message.get("content", json.dumps(message, indent=4)) + ) + ] + + response = llm.chat_completions( + messages=[msg.to_llm_msg() for msg in context_messages] + ) + if response.status == LLMResponseStatus.SUCCESS: + name = response.content + self.rename_session( + session_id=session.session_id, + new_name=name + ) + + return session.get() + + class VideoDBHandler: def __init__(self, collection_id="default"): diff --git a/frontend/src/hooks/shareViewHandler.js b/frontend/src/hooks/shareViewHandler.js new file mode 100644 index 00000000..9115edf0 --- /dev/null +++ b/frontend/src/hooks/shareViewHandler.js @@ -0,0 +1,183 @@ +import { computed, onBeforeMount, reactive, ref, toRefs, watch } from "vue"; + +const fetchData = async (rootUrl, endpoint) => { + const res = {}; + res.status = "success"; + res.data = {}; + return res; +}; + +export function publicUseVideoDBAgent(config, sessionId) { + const { debug = false, socketUrl, httpUrl } = config; + if (debug) console.log("debug :videodb-chat config", config); + + const session = reactive({ + isConnected: true, + sessionId, + videoId: null, + collectionId: "default", + }); + + const configStatus = ref(null); + + const collections = ref([]); + const sessions = ref([]); + const sessionsSorted = computed(() => { + return [...sessions.value].sort((a, b) => b.created_at - a.created_at); + }); + const agents = ref([]); + + const conversations = reactive({}); + const activeCollectionData = ref(null); + + const activeCollectionVideos = ref(null); + const activeVideoData = ref(null); + + const activeCollectionAudios = ref(null); + const activeAudioData = ref(null); + + const activeCollectionImages = ref(null); + const activeImageData = ref(null); + + fetch(`${httpUrl}/session/public/${sessionId}`) + .then((res) => res.json()) + .then((res) => { + console.log("debug :videodb-chat res", res); + if (res) { + session.videoId = res.video_id || null; + session.collectionId = + res.collection_id || session.collectionId || null; + + // Clear existing conversations + Object.keys(conversations).forEach((key) => delete conversations[key]); + + // Populate conversations with fetched data + if (res.conversation) { + res.conversation.forEach((message) => { + const { conv_id, msg_id } = message; + if (!conversations[conv_id]) { + conversations[conv_id] = {}; + } + conversations[String(conv_id)][String(msg_id)] = { + sender: message.msg_type === "input" ? "user" : "assistant", + ...message, + }; + }); + } + } + }) + .catch((error) => { + if (debug) console.error("Error fetching public session:", error); + }); + const fetchConfigStatus = async () => { + try { + const res = await fetchData(httpUrl, "/config/check"); + return res; + } catch (error) { + if (debug) console.error("Error fetching config status:", error); + return { data: { backend: false } }; + } + }; + + const uploadMedia = async (uploadData) => {}; + + const generateAudioUrl = async (collectionId, audioId) => { + const res = {}; + res.status = "success"; + res.url = ""; + return res; + }; + + const generateImageUrl = async (collectionId, imageId) => { + const res = {}; + res.status = "success"; + res.url = ""; + return res; + }; + + const saveMeetingContext = async (msgId, context) => { + const res = {}; + res.status = "success"; + res.data = {}; + return res; + }; + + const makeSessionPublic = async (sessionId, isPublic = true) => { + const res = {}; + res.status = "success"; + res.data = {}; + return res; + }; + + const fetchMeetingContext = async (uiId) => { + const res = {}; + res.status = "success"; + res.data = {}; + return res; + }; + + const refetchCollectionVideos = async () => {}; + + const refetchCollectionAudios = async () => {}; + + const refetchCollectionImages = async () => {}; + + onBeforeMount(() => { + fetchConfigStatus().then((res) => { + if (debug) console.log("debug :videodb-chat config status", res); + configStatus.value = res.data; + }); + }); + + const loadSession = (sessionId) => {}; + + const deleteSession = (sessionId) => {}; + + const updateCollection = async () => {}; + + const createCollection = async (name, description) => {}; + + const deleteCollection = async (collectionId) => {}; + + const deleteVideo = async (collectionId, videoId) => {}; + + const deleteAudio = async (collectionId, audioId) => {}; + + const deleteImage = async (collectionId, imageId) => {}; + + const addMessage = (message) => {}; + + return { + ...toRefs(session), + configStatus, + collections, + sessions: sessionsSorted, + agents, + activeCollectionData, + activeCollectionVideos, + activeVideoData, + refetchCollectionVideos, + activeCollectionAudios, + activeAudioData, + refetchCollectionAudios, + activeCollectionImages, + activeImageData, + refetchCollectionImages, + conversations, + addMessage, + loadSession, + deleteSession, + updateCollection, + createCollection, + deleteCollection, + deleteVideo, + deleteAudio, + deleteImage, + uploadMedia, + generateImageUrl, + generateAudioUrl, + saveMeetingContext, + fetchMeetingContext, + makeSessionPublic, + }; +} diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 78bac065..a220e90e 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from "vue-router"; import DefaultView from "../views/DefaultView.vue"; +import ShareView from "../views/ShareView.vue"; const routes = [ { @@ -7,6 +8,11 @@ const routes = [ name: "Default", component: DefaultView, }, + { + path: "/share/:sessionId", + name: "Share", + component: ShareView, + }, ]; const router = createRouter({ diff --git a/frontend/src/views/ShareView.vue b/frontend/src/views/ShareView.vue new file mode 100644 index 00000000..931c2e78 --- /dev/null +++ b/frontend/src/views/ShareView.vue @@ -0,0 +1,39 @@ + + + + +