marcopeocchi / yt-dlp-web-ui

A terrible web ui and RPC server for yt-dlp. Designed to be self-hosted.
GNU General Public License v3.0
818 stars 85 forks source link

support nginx subpath #110

Closed phuslu closed 3 months ago

phuslu commented 10 months ago

I use example nginx.conf but failed, I found the frontend need some fixes, a dirty/PoC fix is like

diff --git a/frontend/src/atoms/downloadTemplate.ts b/frontend/src/atoms/downloadTemplate.ts
index 10c206c..7480b97 100644
--- a/frontend/src/atoms/downloadTemplate.ts
+++ b/frontend/src/atoms/downloadTemplate.ts
@@ -40,7 +40,7 @@ export const downloadTemplateState = selector({
 export const savedTemplatesState = selector<CustomTemplate[]>({
   key: 'savedTemplatesState',
   get: async ({ get }) => {
-    const task = ffetch<CustomTemplate[]>(`${get(serverURL)}/api/v1/template/all`)
+    const task = ffetch<CustomTemplate[]>(`${get(serverURL)}/yt-dlp/api/v1/template/all`)
     const either = await task()

     return pipe(
@@ -48,4 +48,4 @@ export const savedTemplatesState = selector<CustomTemplate[]>({
       getOrElse(() => new Array<CustomTemplate>())
     )
   }
-})
\ No newline at end of file
+})
diff --git a/frontend/src/atoms/settings.ts b/frontend/src/atoms/settings.ts
index 9c713ea..929626b 100644
--- a/frontend/src/atoms/settings.ts
+++ b/frontend/src/atoms/settings.ts
@@ -160,7 +160,7 @@ export const rpcWebSocketEndpoint = selector({
   key: 'rpcWebSocketEndpoint',
   get: ({ get }) => {
     const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
-    return `${proto}//${get(serverAddressAndPortState)}/rpc/ws`
+    return `${proto}//${get(serverAddressAndPortState)}/yt-dlp/rpc/ws`
   }
 })

@@ -168,7 +168,7 @@ export const rpcHTTPEndpoint = selector({
   key: 'rpcHTTPEndpoint',
   get: ({ get }) => {
     const proto = window.location.protocol
-    return `${proto}//${get(serverAddressAndPortState)}/rpc/http`
+    return `${proto}//${get(serverAddressAndPortState)}/yt-dlp/rpc/http`
   }
 })

@@ -208,4 +208,4 @@ export const settingsState = selector<SettingsState>({
     servedFromReverseProxy: get(servedFromReverseProxyState),
     appTitle: get(appTitleState)
   })
-})
\ No newline at end of file
+})
diff --git a/frontend/src/components/CookiesTextField.tsx b/frontend/src/components/CookiesTextField.tsx
index 4d2d148..1b5f7a5 100644
--- a/frontend/src/components/CookiesTextField.tsx
+++ b/frontend/src/components/CookiesTextField.tsx
@@ -79,7 +79,7 @@ const CookiesTextField: React.FC = () => {
   const cookies$ = useMemo(() => new Subject<string>(), [])

   const submitCookies = (cookies: string) =>
-    ffetch(`${serverAddr}/api/v1/cookies`, {
+    ffetch(`${serverAddr}/yt-dlp/api/v1/cookies`, {
       method: 'POST',
       body: JSON.stringify({
         cookies
diff --git a/frontend/src/components/TemplatesEditor.tsx b/frontend/src/components/TemplatesEditor.tsx
index cf2c5b5..750dd84 100644
--- a/frontend/src/components/TemplatesEditor.tsx
+++ b/frontend/src/components/TemplatesEditor.tsx
@@ -59,7 +59,7 @@ const TemplatesEditor: React.FC<Props> = ({ open, onClose }) => {
   }, [open])

   const getTemplates = async () => {
-    const task = ffetch<CustomTemplate[]>(`${serverAddr}/api/v1/template/all`)
+    const task = ffetch<CustomTemplate[]>(`${serverAddr}/yt-dlp/api/v1/template/all`)
     const either = await task()

     pipe(
@@ -72,7 +72,7 @@ const TemplatesEditor: React.FC<Props> = ({ open, onClose }) => {
   }

   const addTemplate = async () => {
-    const task = ffetch<unknown>(`${serverAddr}/api/v1/template`, {
+    const task = ffetch<unknown>(`${serverAddr}/yt-dlp/api/v1/template`, {
       method: 'POST',
       body: JSON.stringify({
         name: templateName,
@@ -97,7 +97,7 @@ const TemplatesEditor: React.FC<Props> = ({ open, onClose }) => {
   }

   const deleteTemplate = async (id: string) => {
-    const task = ffetch<unknown>(`${serverAddr}/api/v1/template/${id}`, {
+    const task = ffetch<unknown>(`${serverAddr}/yt-dlp/api/v1/template/${id}`, {
       method: 'DELETE',
     })

@@ -223,4 +223,4 @@ const TemplatesEditor: React.FC<Props> = ({ open, onClose }) => {
   )
 }

-export default TemplatesEditor
\ No newline at end of file
+export default TemplatesEditor
diff --git a/frontend/src/views/Archive.tsx b/frontend/src/views/Archive.tsx
index e5895e9..384a7e1 100644
--- a/frontend/src/views/Archive.tsx
+++ b/frontend/src/views/Archive.tsx
@@ -56,7 +56,7 @@ export default function Downloaded() {

   const fetcher = () => pipe(
     ffetch<DirectoryEntry[]>(
-      `${serverAddr}/archive/downloaded`,
+      `${serverAddr}/yt-dlp/archive/downloaded`,
       {
         method: 'POST',
         body: JSON.stringify({
@@ -87,7 +87,7 @@ export default function Downloaded() {
       ? ['.', ..._upperLevel].join('/')
       : _upperLevel.join('/')

-    const task = ffetch<DirectoryEntry[]>(`${serverAddr}/archive/downloaded`, {
+    const task = ffetch<DirectoryEntry[]>(`${serverAddr}/yt-dlp/archive/downloaded`, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
@@ -135,7 +135,7 @@ export default function Downloaded() {
   const deleteSelected = () => {
     Promise.all(selectable
       .filter(entry => entry.selected)
-      .map(entry => fetch(`${serverAddr}/archive/delete`, {
+      .map(entry => fetch(`${serverAddr}/yt-dlp/archive/delete`, {
         method: 'POST',
         headers: {
           'Content-Type': 'application/json',
@@ -155,7 +155,7 @@ export default function Downloaded() {
   const onFileClick = (path: string) => startTransition(() => {
     const encoded = base64URLEncode(path)

-    window.open(`${serverAddr}/archive/d/${encoded}`)
+    window.open(`${serverAddr}/yt-dlp/archive/d/${encoded}`)
   })

   const onFolderClick = (path: string) => startTransition(() => {
@@ -269,4 +269,4 @@ export default function Downloaded() {
       </Dialog>
     </Container>
   )
-}
\ No newline at end of file
+}

this fix is hardcoded and ugly, but It figured out the root cause. Could please help fix, thanks again!

marcopeocchi commented 10 months ago

Thanks for investigating, I will look into this asap!

marcopeocchi commented 3 months ago

@hello phuslu,

Now there's a definitive fix.

Using the nginx.conf in the examples folder:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;

    default_type  application/octet-stream;

    sendfile            on;
    keepalive_timeout   65;

    gzip  on;

    server {
        listen       80;
        server_name  localhost;

        location ~/yt-dlp/(.*)$ {
            proxy_pass          http://10.0.0.6:3033/$1;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header    Host $http_host;
            proxy_http_version  1.1;
            proxy_set_header    Upgrade $http_upgrade;
            proxy_set_header    Connection "upgrade";

            client_max_body_size    20000m;
            proxy_connect_timeout   3000;
            proxy_send_timeout      3000;
            proxy_read_timeout      3000;
            send_timeout            3000;
        }
    }
 }

image

It behaves as expected.

To reproduce: docker run --rm -p 80:80 -v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro nginx:stable-alpine