From f099b277c89d1097eeffead861818e186d32b00b Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Mon, 7 Oct 2024 18:19:13 -0700 Subject: [PATCH] enh: youtube watch param support --- backend/open_webui/main.py | 27 +++++ src/lib/components/chat/Chat.svelte | 102 ++++++++++++++++-- src/lib/components/chat/MessageInput.svelte | 3 + .../chat/MessageInput/Commands.svelte | 75 ++----------- src/lib/components/chat/Placeholder.svelte | 3 + src/routes/watch/+page.svelte | 22 ++++ 6 files changed, 159 insertions(+), 73 deletions(-) create mode 100644 src/routes/watch/+page.svelte diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 7086a3cc9..9fc46fcde 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -824,6 +824,32 @@ class PipelineMiddleware(BaseHTTPMiddleware): app.add_middleware(PipelineMiddleware) +from urllib.parse import urlencode, parse_qs, urlparse + + +class RedirectMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next): + # Check if the request is a GET request + if request.method == "GET": + path = request.url.path + query_params = dict(parse_qs(urlparse(str(request.url)).query)) + + # Check for the specific watch path and the presence of 'v' parameter + if path.endswith("/watch") and "v" in query_params: + video_id = query_params["v"][0] # Extract the first 'v' parameter + encoded_video_id = urlencode({"youtube": video_id}) + redirect_url = f"/?{encoded_video_id}" + return RedirectResponse(url=redirect_url) + + # Proceed with the normal flow of other requests + response = await call_next(request) + return response + + +# Add the middleware to the app +app.add_middleware(RedirectMiddleware) + + app.add_middleware( CORSMiddleware, allow_origins=CORS_ALLOW_ORIGIN, @@ -2416,6 +2442,7 @@ async def healthcheck_with_db(): app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") app.mount("/cache", StaticFiles(directory=CACHE_DIR), name="cache") + if os.path.exists(FRONTEND_BUILD_DIR): mimetypes.add_type("text/javascript", ".js") app.mount( diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 6950910cd..19c71dedb 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -53,7 +53,7 @@ updateChatById } from '$lib/apis/chats'; import { generateOpenAIChatCompletion } from '$lib/apis/openai'; - import { processWebSearch } from '$lib/apis/retrieval'; + import { processWeb, processWebSearch, processYoutubeVideo } from '$lib/apis/retrieval'; import { createOpenAITextStream } from '$lib/apis/streaming'; import { queryMemory } from '$lib/apis/memories'; import { getAndUpdateUserLocation, getUserSettings } from '$lib/apis/users'; @@ -308,6 +308,74 @@ $socket?.off('chat-events'); }); + // File upload functions + + const uploadWeb = async (url) => { + console.log(url); + + const fileItem = { + type: 'doc', + name: url, + collection_name: '', + status: 'uploading', + url: url, + error: '' + }; + + try { + files = [...files, fileItem]; + const res = await processWeb(localStorage.token, '', url); + + if (res) { + fileItem.status = 'uploaded'; + fileItem.collection_name = res.collection_name; + fileItem.file = { + ...res.file, + ...fileItem.file + }; + + files = files; + } + } catch (e) { + // Remove the failed doc from the files array + files = files.filter((f) => f.name !== url); + toast.error(JSON.stringify(e)); + } + }; + + const uploadYoutubeTranscription = async (url) => { + console.log(url); + + const fileItem = { + type: 'doc', + name: url, + collection_name: '', + status: 'uploading', + context: 'full', + url: url, + error: '' + }; + + try { + files = [...files, fileItem]; + const res = await processYoutubeVideo(localStorage.token, url); + + if (res) { + fileItem.status = 'uploaded'; + fileItem.collection_name = res.collection_name; + fileItem.file = { + ...res.file, + ...fileItem.file + }; + files = files; + } + } catch (e) { + // Remove the failed doc from the files array + files = files.filter((f) => f.name !== url); + toast.error(e); + } + }; + ////////////////////////// // Web functions ////////////////////////// @@ -348,6 +416,10 @@ selectedModels = ['']; } + if ($page.url.searchParams.get('youtube')) { + uploadYoutubeTranscription(`https://www.youtube.com/watch?v=NqxUExCZJ5Y`); + } + if ($page.url.searchParams.get('web-search') === 'true') { webSearchEnabled = true; } @@ -366,6 +438,11 @@ .filter((id) => id); } + if ($page.url.searchParams.get('call') === 'true') { + showCallOverlay.set(true); + showControls.set(true); + } + if ($page.url.searchParams.get('q')) { prompt = $page.url.searchParams.get('q') ?? ''; @@ -375,11 +452,6 @@ } } - if ($page.url.searchParams.get('call') === 'true') { - showCallOverlay.set(true); - showControls.set(true); - } - selectedModels = selectedModels.map((modelId) => $models.map((m) => m.id).includes(modelId) ? modelId : '' ); @@ -2060,6 +2132,15 @@ transparentBackground={$settings?.backgroundImageUrl ?? false} {stopResponse} {createMessagePair} + on:upload={async (e) => { + const { type, data } = e.detail; + + if (type === 'web') { + await uploadWeb(data); + } else if (type === 'youtube') { + await uploadYoutubeTranscription(data); + } + }} on:submit={async (e) => { if (e.detail) { prompt = ''; @@ -2095,6 +2176,15 @@ transparentBackground={$settings?.backgroundImageUrl ?? false} {stopResponse} {createMessagePair} + on:upload={async (e) => { + const { type, data } = e.detail; + + if (type === 'web') { + await uploadWeb(data); + } else if (type === 'youtube') { + await uploadYoutubeTranscription(data); + } + }} on:submit={async (e) => { if (e.detail) { prompt = ''; diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index b0991914f..3575fb0af 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -300,6 +300,9 @@ bind:this={commandsElement} bind:prompt bind:files + on:upload={(e) => { + dispatch('upload', e.detail); + }} on:select={(e) => { const data = e.detail; diff --git a/src/lib/components/chat/MessageInput/Commands.svelte b/src/lib/components/chat/MessageInput/Commands.svelte index 9ce943364..6c0892ab7 100644 --- a/src/lib/components/chat/MessageInput/Commands.svelte +++ b/src/lib/components/chat/MessageInput/Commands.svelte @@ -26,71 +26,6 @@ let command = ''; $: command = (prompt?.trim() ?? '').split(' ')?.at(-1) ?? ''; - - const uploadWeb = async (url) => { - console.log(url); - - const fileItem = { - type: 'doc', - name: url, - collection_name: '', - status: 'uploading', - url: url, - error: '' - }; - - try { - files = [...files, fileItem]; - const res = await processWeb(localStorage.token, '', url); - - if (res) { - fileItem.status = 'uploaded'; - fileItem.collection_name = res.collection_name; - fileItem.file = { - ...res.file, - ...fileItem.file - }; - - files = files; - } - } catch (e) { - // Remove the failed doc from the files array - files = files.filter((f) => f.name !== url); - toast.error(JSON.stringify(e)); - } - }; - - const uploadYoutubeTranscription = async (url) => { - console.log(url); - - const fileItem = { - type: 'doc', - name: url, - collection_name: '', - status: 'uploading', - url: url, - error: '' - }; - - try { - files = [...files, fileItem]; - const res = await processYoutubeVideo(localStorage.token, url); - - if (res) { - fileItem.status = 'uploaded'; - fileItem.collection_name = res.collection_name; - fileItem.file = { - ...res.file, - ...fileItem.file - }; - files = files; - } - } catch (e) { - // Remove the failed doc from the files array - files = files.filter((f) => f.name !== url); - toast.error(e); - } - }; {#if ['/', '#', '@'].includes(command?.charAt(0))} @@ -103,11 +38,17 @@ {command} on:youtube={(e) => { console.log(e); - uploadYoutubeTranscription(e.detail); + dispatch('upload', { + type: 'youtube', + data: e.detail + }); }} on:url={(e) => { console.log(e); - uploadWeb(e.detail); + dispatch('upload', { + type: 'web', + data: e.detail + }); }} on:select={(e) => { console.log(e); diff --git a/src/lib/components/chat/Placeholder.svelte b/src/lib/components/chat/Placeholder.svelte index e8e84544c..7b9a0e9ea 100644 --- a/src/lib/components/chat/Placeholder.svelte +++ b/src/lib/components/chat/Placeholder.svelte @@ -204,6 +204,9 @@ {stopResponse} {createMessagePair} placeholder={$i18n.t('How can I help you today?')} + on:upload={(e) => { + dispatch('upload', e.detail); + }} on:submit={(e) => { dispatch('submit', e.detail); }} diff --git a/src/routes/watch/+page.svelte b/src/routes/watch/+page.svelte new file mode 100644 index 000000000..78eab5be3 --- /dev/null +++ b/src/routes/watch/+page.svelte @@ -0,0 +1,22 @@ +